diff --git a/DoL Changelog.txt b/DoL Changelog.txt index b057772f2..0989da60c 100644 --- a/DoL Changelog.txt +++ b/DoL Changelog.txt @@ -1,128 +1,385 @@ -0.4.5.0 -Added a few new events to the pirate ship. Thanks to Crimson Tide. -Allowed the Ivory Wraith to possess you during blood moons on the pirate ship. Thanks to Crimson Tide. -Added a new event to the cafe, available after the cafe reopening. Written by Juniaki, and coded by AiHoshino. -Added hair growth formula to the hairdresser's that can be applied in the Medicine Drawer. Thanks to Lollipop Scythe. -Added alternate text for the chastity parasite in Harper's nightmare. Thanks to Lollipop Scythe. -Alex will now sometimes include booze in their shopping list. Written by IndexEnthusiast and coded by Cutiland. -Thanks to P_Ruby, Midnight, and AiHoshino for code improvements. +0.4.6.0 +Added more scenes for "Avery School Pickup" when Robin is present, written by Burnt Toast and coded by P_Ruby. +Added a scene for the weekend after taking on Robin's debt, written by Shysta and Catwillow, coded by P_Ruby. +The PC will no longer cover themselves in embarrassment where being underdressed is contextually appropriate, such as when wearing swimwear at the pool. Thanks to Akoz. +Improved code to ensure all sex encounters with Robin, Alex, Sydney and the Great Hawk have no chance of becoming nonconsensual midway through. Thanks to Crimson Tide. +Animated mascara makeup so that it no longer appears above and separate from the PC's eyelids when blinking. Thanks to OceanSpray. +Added an alternative method of escaping the farm, found by breaking the farm cell gate, or stealing the keys from the morning farmhand. Coded by Tanny78 and written by YetAnotherUser. +The PC can now explore Remy's farm after escaping the cell at night. Coded by Tanny78 and written by YetAnotherUser. +Added two revealing variants of holy outfits to the forest shop, locked behind reaching the rank of nun or monk. Includes the sexy priest's vestments and sexy nun's habit, veil, stockings, and gloves. Sprites by Avur and Beta. Coded by FadedLines. +Added initial lines for generic NPC's and support for Named NPCs in the future. Written by Kinky_one, with edits by Kirsty, and code by Lollipop Scythe. +Added a general indication of body part sensitivity to the Characteristics overlay. +Made a couple of exhibitionism checks inform the PC why the option is unavailable if the PC does not meet the requirements. Thanks to Jimmy. +Added a section for Alex in the cottage to conclude farm work. Thanks to Jimmy. +Added an event for the school antechamber vending machine. Thanks to Jimmy. +Added the Raul and Janet and Pinch books to the scene viewer. Thanks to KnotLikeThis. +Added the Eden, Wraith, Harper and Great Hawk nightmares to the scene viewer. Thanks to KnotLikeThis. +Added the ability to check for the janitor before leaving the school gloryhole, after being caught once. Written by WildUntammedFluffy and coded by mintlflavoured. +Adjusted the chances of being caught by the janitor. Thanks to mintlflavoured. +Kylar can now sit in the library if jealousy is high and/or Sydney's love is high. Written by Void and coded by Jimmy. +Adapted Sydney's Leighton punishment scene to include references to Kylar. Written by Juniaki and coded by Jimmy. +Added a new scene when relaxing with the black wolf with high love/harmony/ferocity. Thanks to BigBadBear. +Added a new scen to sleepin in the wolf cave, at high black wolf love. Thanks to BigBadBear. +Added the black wolf's love to the cheat menu. Thanks to BigBadBear. +Added a giant oyster vore scene to the sea and beach cave diving entrance. Written by Uchiki and coded by Tanny78. +The temple grace bar is now green instead of red, and turns gold with a silver glow when maxed. Thanks to Akoz. +Added cum-dump facial ejaculation paragraphs. Thanks to Jimmy. +Improved pregnancy sperm source tracking. Thanks to Twig. +Added lines acknowledging chastity during NPC orgasm at gloryholes. Thanks to Twig. +Added a rooftop to the school. Whitney hangs out there at lunch, once they've been met elsewhere. +Thanks to majou, Ybyx, Xao, Tanny78, Jimmy and Kirsty for code improvements. Thanks to Kirsty for the following additions and improvements: -Added a new clothing category: Handheld items. -Handheld items that the PC can purchase include umbrellas, a feather duster, and various bags for books. Umbrellas keep the PC dry and shaded, and feather dusters boost housekeeping. Some handheld items have the "bookbag" trait, which is useful for school. -Added a new arm position for the sidebar sprite. -Whitney will now share cigarettes with the PC in the park, if they are a love interest and have high love, and the PC is not pregnant. -Adds a stall that sells balloons and popcorn. It appears for the first time in summer, at the beach. It can help promote, or hurt, Robin's business, depending on the PC's choices. -Popcorn can be eaten in the park or at the beach to reduce trauma, or shared with Robin on a movie date. -Added an orphanage event that lets the PC raise hope by giving an orphan a balloon. -The PC can now buy gingerbread at the cafe during winter. Sprites by Beta. -The sidebar image will now show handheld props in certain scenes, such as sharing a milkshake with love interests or smoking with Whitney. -The pom poms hand item has been replaced with cheerleading gloves. Pom poms can now be found in the handheld category instead. -Added the jingle-bell dress and sleeveless jingle-bell dress to the forest shop, unlocked during Christmas. Thanks to Beta. -Added the jumper and festive/Christmas tree jumper to the clothing shop, as well as skull, heart, and ghost variants. Sprites by Kirsty. -Edited some rainy weather events to account for the player carrying an umbrella or otherwise wearing rainproof clothing. -Added new sections to the journal: Time-Sensitive, Reminders, and Holidays. -Cleaned up cafe code. +Added a locker at school, which can be used to store a bookbag or other handheld item for easy retrieval. +The PC can now choose to go directly to history class, directly to the library, or stay in the canteen after finishing lunch. +The PC can now sleep in classes, if they are tired enough. Scenes written by WildUntamedFluffy, code and additional writing by Kirsty. +Added variations to the lunch scene where the player can intervene when Robin is bullied. Written by Aurora. +During winter, Avery may pick up the PC after school to take them to the pub for mulled wine. Written by Catwillow, Shysta, and Juniaki. +Added the durag and bandanna to the clothing shop. Thanks to Uchiki. +Shrunk the baseball cap so it better fits the player's head. Thanks to MinerDX. Colour adjustments by Kirsty. +Shrunk the cat hat so it better fits the player's head. Added a variant style to the cat hat, thanks to MinerDX. Colour and sprite adjustments by Kirsty. +The love locket now has a new UI at the mirror screen that includes sprites and icons by Kuma, with colour variant sprites by Kirsty. +Added a new alleyway scene for Whitney that may occur at high trauma. Thanks to LenoxVentrue. +The love locket sprite has been adjusted, thanks to Beta, and is now available in gold, silver, and bronze. There is a new UI for the love locket's mirror screen that includes sprites and icons by Kuma, colour variant sprites by Kirsty. +Briar will now remind you to restock your vending machine if you try to collect money when your machine's been empty for more than two weeks +Made the leather jackets, leather dress, and leather gloves recolourable. Leather jackets can now also be zipped. Sprites by Beta and Kirsty. +Added handheld props to scenes where the player drinks, including a beer bottle, wine glass, shot glass, and beer mug. Sprites by Beta. +Added the cropped leather jacket, leather crop top, leather top, leather pants, leather miniskirt, and leather shorts to the shop. Thanks to Beta. +Cleaned up the tights sprite. Thanks to Beta. +Added the messy ponytail sides. Thanks to Beta. +Added the sleek sides and fringe hairstyle and bedhead hairstyle. Thanks to Hyomi. +Revamped the cocoon sprite. Thanks to Mochi. +Overhauled the cleavage sprites. Thanks to Koko. +The player can now continue chatting with Whitney after sharing a smoke. +Added the camisole, newsboy cap, puffy shorts, bloomers, sarong, bunny-tie bikini top, and leather leggings to the clothing shop. Thanks to Beta. +Added the heart sunglasses to the clothing shop, which can be worn on the face or pushed up on the head. Thanks to Beta. +Added the denim panties and denim thong to the clothing and adult shops. Thanks to Mono. +Added an oversized button-down sleep shirt, button-down shirt, and masculine formalwear, including four variants on waistcoats, suit trousers, and a shirt with blazer, to the clothing shop. Sprites by KG. +Improved the dress shirt sprite, including a half-buttoned alternate style. Thanks to KG. +Added the visor, whistle, and life vest to the clothing shop. Thanks to Uchiki. +Added the harem vest and harem pants to the forest shop, unlocked at the same time as the belly dancer's clothes. Thanks to Uchiki. +Added the long-sleeved shirt and cargo pants to the clothing shop. Thanks to Avur. +Made the loose socks recolourable. Thanks to Avur. +Added the latex leotard to the clothing and adult shops. Thanks to Hyomi. +Each trait's description now aims to clearly explain what it affects in-game. +The "athletic" trait will now boost athletics. Its previous function, as a trait for underwear that prevented people from writing on your thighs, bottom, and pubic area, has been folded into the "covered" trait. +Some under upper and under lower items now also have the "covered" trait, so players will no longer act as though they're exposed when they're fully covered by an undershirt. +Removed the rag and event traits. +Added a tooltip to the brothel vending machine if the player has disabled condoms in their game, explaining that their current settings mean they cannot purchase condoms for the machine. +Converted classroom and swimming events to eventpool. +Added a new event that can occur during swim class for players who have a transformation with a visible tail. Written by DeadMan, fox and demon variants by Kirsty. +A swimming event that never triggered due to an incorrect outfit check can now trigger. +Sirris will no longer usurp Doren by talking poetry or leading the class in play reenactments. +Exporting custom settings will now properly save NPC skin colours. +Fixed a bug that caused handheld items to be destroyed. +Added flavour text to Robin's picnic based on the season. Thanks to WildUntammedFluffy. +Added a weekly event to the pub. The player can tip a local musician for trauma and stress reduction and a boost to kindness. fame, or steal from the jar. Thanks to Rain. +Fixed an error being printed to the console with original clothing colours. +Fixed a visual bug with shop traits. +Fixed prettier problems in the clothing files. +Fixed some broken images that weren't correctly displaying. +Added men's garter socks, ruffled socks, mary janes, and a high-waisted skirt to the clothing shop. Thanks to Beta. +Added the jumpsuit to the clothing shop. Thanks to Index. +Revamped the open shoulder lolita dress and added the lolita headband, thanks to Beta. The old open shoulder lolita dress is now known as the classic lolita dress. +Added the gym bag to the clothing shop, an icon for working as a waiter, and a handheld torch prop for when the player's exploring the catacombs. Thanks to Beta. +Icons with skin colours in them, such as the hitchhike, pray, body part surgery, and strip icons, will now reflect the player's skin tone. +Made some minor improvements to the base body sprites. +Added clothes icons to the links when purchasing clothes at the markets. +Added new default wings for the angel and fallen angel transformations, thanks to Mochi. The old wing sprites can still be selected in the mirror as "classic" wings. +Made the cowboy hat recolourable. +Fixed mixels on ruffled shoulder hair. +Added breast sprites for the tuxedo jacket, tank top, and classy vampire jacket. Updated the tank top sprite. Thanks to Beta. +Added the ability to wear eyepatches and monocles on the other eye, as well as the ability to push all glasses up on the head. Art courtesy of Jacko. +Updated winter jacket sprites. Thanks to MinerDX and Beta. +Added new handheld props to certain scenes. Sprites by Beta. +Implemented masking images that allow some clothes to hug the PC's body if they have the feminine or androgynous body shape. Updated many breasts as part of this change. +Under the hood pregnant belly mask changes. +Added breast sprites for the beatnik shirt, short ballgown, and shadbelly coat. Thanks to Beta. +Added breast sprites for the hanfu and updated integrity images. Thanks to Hyomi. + +Thanks to Lollipop Scythe for the following additions and improvements: +Added behind the scenes time tracking for unsafe sex with named npc's. +Added the ear slime hallway masturbation to the school as a later alternative to just skipping class. +Adjusted an older cafe scene to support newer features. +Updated the character view with controls for mouth, tears, blushing, brow, handheld items, and two-tone hair styles. +Moved the hair options in the character viewer into their own section. +Swapped the controls for bodytype to radio controls. +Added an indicator for the minimum value of arousal and pain. +Wearing fetish gear now increases minimal arousal. +Increased the min arousal hard cap from 40% to 50%. +Adjusted the ear slime introduction for when the PC has already had an ear slime. +Added a bed icon alternative should the PC be unable to sleep due to nudity. +The feats My Collection of Feats and My Timeless Collection of Feats now require 50% and 95% of total earnable vrelcoins to unlock. +Added the ability to go shopping with Whitney when either romancing them or being forced to be a crossdressing male. Locations currently including the hairdressers, clothes shopping and putting on cosmetics. Proofreading by Crimson Tide. +The above can lead the player to not being able to wear male clothing at school if being forced to crossdress. +Added stats for when you were last masturbated, molested, raped, penetrated, ejaculated in and passed out. +Added stats to track how much you've paid Bailey or missed in Bailey payments +The Whitney shopping event can now lead to a new Whitney rule of only using the girls changing room and swimwear. Proofreading by Kirsty. +Added alternate chastity comment for non-chastity belts. +Added ear slime forced crossdressing warning. +Added Whitney combat chastity lines, proofread by Kirsty. +Added the ear slime hallway masturbation to the school as a later alternative to just skipping class. +Code clean up. + +Thanks to KnotLikeThis for the following additions and improvements: +Improved save sizes for saves that played the Raul and Janet, Pinch and Schism events. +Improved the protection against unintended changes to player stats in Raul and Janet, Pinch, Schism and Nightmare events. +Cleared old backup variables in old saves that were never unset properly. +Fixed softlocks that could happen when using cheats after Raul and Janet, Pinch and Nightmare events. +Fixed fallen angel progress not being properly restored in Raul and Janet. +Fixed some sex toys not being properly removed in Raul and Janet and Pinch sequences. +Small adjustments for Raul and Janet. +Made Pinch's name spelling consistent. +Fixed some issues with updating timestamps from old saves. Thanks to Xao. + +Thanks to Cutiland for the following additions and improvements: +Alex now has specific ejaculation lines, rather sharing them with generic NPCs. Coded by Cutiland. Written by Cutiland and Index. +Added a different interaction for the party date with Avery, where a random NPC asks Avery what they do to afford such expensive parties. Written by Avur. +When entering the farm around 6 and 7 a.m the PC may find out that Alex is not in the shower shed getting ready for work and decide to look for them in the cottage. Written by Index. +Avery now have a more detailed scene when drinking whine with PC. Written by Avur. +Black Wolf will now bring a baby rabbit for a visible disturbed PC. Written by Charming. +Robin may now sometimes visit PC while they're working in the Cafe. Written by FoxgirlZoe. +Whitney will sometimes try to bully Robin by pulling their lower clothing down in the hallways. Written by Doodle and Juniaki. +While going in labour at the farm, Alex will call an ambulance to the player if the baby isn't or Alex doesn't know is their own. + +Thanks to googlyman for the following improvements to the office: +Added a changing room to the Office Agency Lounge with wardrobe access. +Added functionality for an underdressed PC to sneak into the Agency Lounge from the lobby, resulting in one of two events if the PC is caught. +Added an event where the PC can be caught either by a security guard or the office manager when sneaking into the Agency Lounge underdressed. +Added an additional danger event to the Coffee job. +Added a new temp job where the PC has to file documents with another temp, resulting in a variety of outcomes. Balance Changes -Going commando will now increase exhibitionism once more, but only if the PC willingly chose to do so. Thanks to Lollipop Scythe. -Removed the daily check so it becomes possible to explore the markets more than one time. Thanks to Cutiland. +Some Alex events can now trigger before Alex becomes a love interest, but still after the PC unlocks the cottage as a home location. Thanks to Cutiland. +Keeping up with the vehicle during a particular moor event is not more fatiguing. Thanks to Tanny78. +Moved the school vending machine from the individual shcool toilets to the school antechamber. Thanks to Jimmy. +If the PC is carrying an umbrella while a car splashes them on the road east of town, they can now raise it to shield themselves from the water. Thanks to Jimmy. +Crotchless panties will now be handed to Leighton during inspection. Thanks to Jimmy. +Striped panties are now seductive. +Adjusted the black wolf's relationship caps. +Modified Executive job so that there are instances where the client will not demand that the PC remove an article or clothing. Thanks to googlyman. +Minor rebalance and addition text when entering the girls changing room with high pregnancy fame with a male PC. Thanks to Lollipop Scythe. -Bug Fixes -Fixed a bug that prevented the PC refusing to make breakfast for Eden with low housekeeping skill. Thanks to salagadoola. -Fixed a bug that made Robin appear in the orphanage garden and canteen when they were supposed to be in the mansion. Thanks to salagadoola. -Whitney will no longer smoke in front of PC when they are pregnant. Thanks to Kirsty. -Fixed an event that lowered hope when it should've raised it. Thanks to Kirsty. -Fixed incorrectly coded orphanage events and moved them to the appropriate event pools. Thanks to Kirsty. +Thanks to Lollipop Scythe for the following balance changes: +Minor pregnancy changes so the fetish setting and forced pregnancy still work after hitting random npc pregnancy cap. +The players genital wetness will be increased while having the relevant ear slime focus while in heat/rut at high growth. +Minor strengthening to one of the penis shrinking effects of the ear slime. +The tentacle plains pod can now increase bottom sensitivity. +Enabled the Maximum penis size for female PCs to account for the parasite penis. + +Bug fixes +Fixed a rogue NPC found when passing out on the pirate ship. +Fixed an issue with handheld items. Thanks to P_Ruby. +Fixed issues with the save system. Thanks to majou. +Fixed lace arm warmers not having a femininity value. Thanks to FadedLines. +Fixed a bug that caused history to drop on page refresh. Thanks to majou. +Added missing options to export function. Thanks to majou. +Fixed a number of mismatched fatigue outputs. Thanks to Tanny78. +Fixed a bug that caused school to start at the wrong date. Thanks to creeping G. +Fixed start screen padding. Thanks to xao. +Corrected some issues with links at Mason's pond, the tentacle plains, and the adult shop. Thanks to Electron Emissary. +The bath slime event should now be disabled if swarms are disabled. +Fixed an issue where the player could continuously wait for a new task pass office hours. Security will now escort the PC out into the Lobby if waiting for a new task extends past off hours. Thanks to googlyman. +Fixes error due to missing function. Thanks to Xao. +Fixed small oversight issue that allowed the player to enter the archaeological field office exposed. Thanks to ShinyMilk. +Fixed small oversight issue that allowed the player to go to the strip club exposed if it is their first time. Thanks to ShinyMilk. +Fixed issue that allowed the player to leave the park men and women's bathroom naked without an exhibitionism check. Thanks to ShinyMilk. +Thanks to Kirsty, Isari, Ybyx, P_Ruby, GrandAdmiralM, WHALER'S NIGHTMARE, handle5, GeneralFire, Jimmy and Lollipop Scythe for typo fixes. + +Thanks to Jimmy for the following fixes: +Fixed an issue with text. +Fixed an issue with the brothel vending machine. +Fixed underwear being eaten by the end of the adult shop opening. +Forced divsex to respect boundaries. +Cleaned up duplicated links found during swimming events. +Added missing semen to player vagina when fucking Sydney. +Copied frotting dialogue from global ejaculation to wall ejaculation. +Fixed several images that had the wrong measurements. +Fixed an issue with the black wolf's dominance. +Corrected remaining links that aren't capitalised properly. +Fixed some issues with sex toys. + +Thanks to Ybyx for the following fixes: +Fixed an issue with ironman mode. +Fixed an issue with the clothing system. +Fixed a bug that prevented 2/3rds of Sydney's situational combat dialogue from triggering. +Remove decoration around icon and image widgets for performance reasons. +Fixed a bug that broke the parasite cheats. Thanks to hwp. +Fixed an issue with stress/trauma at the pub. +Fixed an issue with stress during an Avery event. +A bunch of under the hood issues. +Fix Kylar using their own pronouns to talk about Sydney in the library. +Drunk/ Drugged/ Hallucinogens statbars now display their actual values when hovered over. +Extra and non-extra pregnancy stats foldouts are no longer quantumly linked. Thanks to P_Ruby for the following fixes: -Fixed a misdirected link that prevented a certain Whitney passage from displaying after an encounter. -Fixed a bug that prevented the right text displaying when asking Whitney to stop during a particular oral scene. -Fixed a bug that gave Winter a flashback to the last maths competition, if the PC was exposed during it. -Fixed a bug that made a Whitney scene decrease trauma instead of increasing it. -Fixed a bug that allowed the PC to make multiple confessions at the temple in the same day. -Minor fixes. +Fixed a bug involving repeated text in an Avery scene. +Fixed pronouns in an Avery Scene. +Fixed an escaped NPC during the Whitney orphanage loft event. +Fixed a bug that allowed Whitney to be sent to the pillory after being dismissed. +Fixed a bug that allowed Whitney to sunbathe after being dismissed. +Fixed some errors with the PC's speech attitude. +Gave Whitney the appropriate pain tolerance during a scene in the adult shop. +Fixed a bug that prevented screaming during the Whitney milkshake fight working. +Fixed a bug that made love increase instead of decrease. +Fixed a bug with the Ivory Wraith's events. +Fixed a bug with a Whitney street event. +Made a check for long skirts more consistent. +Fixed an issue with Kylar's appearance at the fountain in the park. +Fixed an error with time found when escaping prison. +Formatting fixes. Thanks to Lollipop Scythe for the following fixes: -Minor text adjustments to tentacle combat to clarify it's action when it attempts to damage a chastity device. -Added a missing chastity parasite tentacle check and text. -Added additional checks in masturbation to prevent multiple actions of the same sex toy. -Fix for missing sex toy name when orally using it. -Playtime tooltip adjustment. -Removed a debug check at the lake pond. -Fixed an issue with the feats importer with the local storage slots. +Fixed a minor version update error when new clothing slots are added. +Fixed an issue that allowed the PC to evade the tutorial person if they had an ear slime. +Fixed an incorrect base image for missionary close up images. Art courtesy of sseshess. +Fixed an issue with the fallen angel transformation reducing beast transformation progress. +Fixed an issue with the ear slime focues. +Fixed contraceptives not affecting fetish and silly pregnancy mode as intended. +Fixed a value that could be unintendedly deleted after starting a game. +Fixed a minor feat issue. +Fixed a bug that made the PC be offered a collar while already wearing one. +Fixed an issue allowing sensitivities to continue to rise above the intended cap. +Fixed a text issue when unable to afford hair products in the hair dressers. +Fixed a minor exploit allowing you to dye your hair without having enough money for it. +Fixed an issue with the Schism sequence related to hair color, makeup and fluids. +Minor fix to ear slime checks missing check for dickgirls or cuntboys. +Fixed a bug that allowed the PC to pray about their pregnant belly multiple times per day. Thanks to Lollipop Scythe. +Fixed an issue with Leighton's breast inspection. +Fixed an issue with ear slime growth being lowered beyond what it should. +Corrected a parasite penis ussue in in Extra Stats. +Fixed an issue where the chastity parasite was equipped when it should not have been. +Fixed a minor UI and text issue when using the Try to fondle your chastity actions when the pc has both genitals. +Fix to taken virginity and others being hidden in statistics and cheats after losing the parasite penis or changing sex. +Minor fix to prevent it the ear slime introduction lowering what lewd interactions the ear slime is aware you've done. +Fixed to an ear slime action during masturbation. +Fixed temple virginity being taken/restored incorrectly in the cheat overlay. +Fix to a Remy event check erroring out on starting a new save. +You will no longer be able to walk to school with Robin while naked. +Some Robin code clean up. +Fix to a situation where the ear slime forced commando should apply. +Fix to masturbation using an older genital check. +Fixed an issue with hoods when trying to equip an item that gets prevented. +Fix to prevent random swimmers from producing menacing metal shackles. This excludes those with a diving suit. +Clothing warning text fixes. +Blocked access to hair cheats while at the Hairdressers due to issues with them interacting. +Fix to missing addinlineevent in the passage Vending Machine Buy. +Fix to the ear slime events not return clothes to the wardrobe while at the Temple bunk or Alex's bedroom. +Fix for some exit wardrobe links not running required code. +Fix to a clothing duplication/deletion issue. +Added missing <> widget call. +Fix to the chastity parasite being leaky in the cafe. +Text fixes - -0.4.5.1 Thanks to Kirsty for the following fixes: -The vending machine can now be properly reinstalled and restocked after Briar sells it off. -Science lessons will no longer explode due to errant ifs. -Remy will no longer clamp an iron collar over your iron collar. -Alex's parent will no longer call them their little girl/boy based on what's in their pants. -The streets have been rendered safe from creeps staring at you and slimes commanding you get your hands bound. -Practised practice and travelled travel. -Removed stray pixel on space buns. -Fixed a bug with the random clothing feat booster. -Hid empty underoutfit category in forest shop. -Added missing sleeve sprite for the school vest. -Valentine's Day will no longer display in the journal unless the player has unlocked Eden's Valentine event. -The player can no longer explore the markets while exposed. -Fixed a broken chef inline event. -Corrected some dialogue on Remy's farm to account for the player being gagged. -Fixed or removed many incorrect icons and link indentations. -Fixed jingle-bell dress/skirt separating. -Fixed jingle-bell dress not unlocking properly. Thanks to Lollipop Scythe. -Typo Fixes. +Pinafores and shortalls will now hide pregnant bellies as intended. +Fixed layering issues. +Robin will now undo your bindings before you help them set up their stand. +Eating pancakes will no longer teleport you to Cliff Street. +Fixed a forced crossdressing scene that did not account for the forced crossdressing toggle being disabled. +Fixed a widget that didn't account for NudeGenderDC. +Fixed Z-index for back layer of handheld items. +Adjusted suspenders so they can properly clip onto high-waisted pants. +Changed obsidian disc description so it's not the same as the mine sign description. +Implemented mask for fro so it no longer looks broken in hairstyle preview. +Many hair mixel fixes. +Fixed an event that didn't properly shackle feet. +Fixed some alignment issues on the characteristics page. +Whitney can no longer throw your balloon into the ocean. +Went rogue and shaved a bunch of players heads. (ponytails and pigtails don't work correctly with the bat beanie; lopping off those styles when it's equipped) +Big ol file cleanup, which has resulted in the addition of size 6 breasts being enables for clothes that previously lacked them. +Enabled the straw flower hat for purchase in the clothing shop. +Removed the "exposed" property from the bellydancer bottoms and top, since despite being risque, they don't actually expose the players breasts or genitals. +Underwear will now layer under tights, except for leotards and long johns. +Most pants can now be tucked into boots. +Several shirt sleeves can now be rolled up. +Fixed a bug with selecting a random colour in the shops. +The dress shirt can now be recoloured. +Made all denimwear recolourable. +Sirris will now actually undo your bindings when they say they will, post-adult-shop-passout. +Fixed paper fan layering under face items. +Fixed a layering issue with the angel halo and removed the femininity for the gender-neutral gym shirt. +Fixed a few bugs with hairstyles. +Fixed a bunch of mixels. +Fixed mask issue with breast sprites. +Fixed missing handheld item bug when naked. +Updated size 6 breast sprites for the v-neck sweater, open shoulder sweater, and maid dress, as well as integrity states to the v-neck sweater. Thanks to Beta. +Restored Christmas Wraith rng to its proper place. +Lilies can now be properly picked in the forest. +Fixed colour mismatch in some outfit colours. +Fixed hair traits disappearing. Thanks to Xao. +Fixed incorrect icon dimensions. +Added missing argyle sweater vest acc icon, breast sprites for the peacoat and Christmas dress/shirt, fixed animation issue with the pancake prop sprite, added an icon for the lake dock, and adjusted the hold sprite for the kimonos. Art courtesy of Beta. +Fixed an issue with the leather jacket sprite while pregnant. +Fixed a layering issue with the heart sunglasses in their alt position. +Adjusted some clothing trait descriptions. +Fixed layering bug for glasses. +Fixed a visual bug where sleeves wouldn't cover the player's left arm if they had the feminine or androgynous body type. -Thanks to Lollipop Scythe for the following fixes: -Fixed a bug that allowed you to collect semen during masturbation when dnenies or while wearing the chastity parasite. -Fixed an issue earslime commando demand not expiring. -Fixed bugs that prevented older outfits and wardrobes from including handheld items. -Feats later in a series but earned before prerequisite have been earned can now be displayed. -Fixed an issue with storing clothes outside wardrobes. -Fixed an event that didn't close properly at Alex's farm. +Thanks to P_Ruby for the following fixes: +Fixed a bug that prevented Whitney's school rescue from working properly. +Fixed a bug that prevented rng working correctly in some scenes. +Fixed a bug that caused losing a fight against Whitney to result in the wrong scene. +Fixed an issue with the PC's skirt. +Fixed a bug that prevented bratty PCs speaking appropriately during a scene. + +Thanks to WHALER'S NIGHTMARE for the following fixes: +Fixed an issue with Kylar's sneak event. +Fixed an issue with Robin in the underground brothel. +Kylar will no longer go to bed in the middle of the day +Comforting Kylar's parents increases stress properly +Foxing in Tentacle Plains correctly displays drug gain rather than hallucinogen gain +The Starfish Challenge event now ends properly. +Temple wall victims now use gendered pronouns. +Fixed a bug that interfered with the police side door on Barb Street. + + +0.4.6.1 +Fixed a link that linked to a wrong passage. Thanks to P_Ruby. +Adjusted the "skirt_down" value of some bottoms. Thanks to P_Ruby. +Fixed a couple of widget typos. Thanks to Ybyx. +Thanks to Ybyx for code improvements. +Added a tooltip to clarify what the sensitivity indicators in Characteristics are. Thanks to Lollipop Scythe. +Minus sydney's corruption is now teal like before. Thanks to xao. +School stars are not longer displayed when you have gold star already. Thanks to xao. +Thanks to majou for code improvements. + +Thanks to KnotLikeThis for the following additions and improvements: +Fixed an error that occured when ponytails got messy. +Fixed new forest shop clothes not being properly locked in old saves. +Fixed some incorrect links in a rooftop event. +Restored some widgets that were deleted by mistake. +Fixed grace loss being displayed as gain. +Fixed Robin's Confidence being displayed as Dominance. Thanks to Xao. +Fixed an error in a swimming lesson event. +Fixed some outfit issues in the Whitney shopping event. +Thanks to Lollipop Scythe for the following fixes: +Fix to Whitney shopping check. +Added the stairs icon to the school roof link. +Icon image fixes by Mochi. +Added missed Whitney shopping cross-dress makeup tracking. +Fixes to paying the canal thug the requested amount of money. +Fixed a JavaScript issue. +Fixed an issue with tracking Bailey's pay. +Fixed a broken scarlet book exit link. -0.4.5.2 Thanks to Kirsty for the following fixes: -Added toggle to facewear category to layer facewear behind hair. -Added failsafe to prevent props from overwriting equipped handheld items. -Added current number of stolen underwear to the journal, under the "Inventory" category. Adjusted some colours for easier skimming. -Brothel vending machine fixes and code cleanup. Players whose vending machines were mistakenly marked as sold should see them back in the dressing room. -Told Whitney to stop talking about PC's penis and visibly aroused and bushy pussy. (Simplified a "Nice <>" remark so it reads more naturally) -Players will finally stop groping their naked breasts underneath their shortalls. (Corrected hand z-index to layer on top of the shortalls/pinafores and disabled the sprites for covering your breasts when wearing these clothes without a shirt underneath) -Lowered the default halo sprite to rest beneath the umbrella instead of clipping through it. -Fixed an incorrect link when ducking on the pirate ship. -Sleeve sprite fixes. -Fixed the player being able to ask Whitney for a smoke prior to meeting the requisite conditions. -Fixed an incorrect wearProp call -Continued removal/replacement of incorrectly used icons. -Adjusted fatigue values for relaxing at the wolf cave. -Corrected audience members of some Whitney events. -Typo fixes. +Fixed a bug preventing players from lockpicking the school lockers. +Fixed a bug where players would cover themselves with their arm despite not being considered exposed. +Fixed a layering issue with handheld props and updated sprites accordingly. +The new angel/fallen angel wing sprites will now correctly layer above the body when they are positioned in front of the body, and hair is positioned behind the body. +Fixed incorrect size on beatnik shirt sprite and adjusted draggletail sprites. Thanks to Beta. +Fixed acc image layering underneath breasts for the serafuku and sailor shirt. +Added missing size 6 sprite for cream. -Thanks to Lollipop Scythe for the following fixes: -Fixed another issue with handheld items in the wardrobe editor. -Fixed the Medicine Drawer not being displayed when buying hair growth formula. -Fixed incorrect pronouns in a Bailey scene. -Prevented the player from flashing their butt when already exposed. -Fixed issues where Remy's Encroachmen showed up before ever visiting Alex's farm. -Fixed a minor pubic hair layer issue in the close images. -Fixed an issue with Eden sex after masturbating not properly ending the masturbation. -Fixed exposure and towel issue with Alex's farm. -Fixed incorrect passage of time at the docks. -Moved the message from hiding transformation parts to a more appropriate place. -Fixed an oversight that allowed access to the pillory when there was no one in it. -Fixed issue with the prison expecting you to work more hours than available after the intro scene. -Minor ear slime scene adjustment to clarify what dog it wants you to have sex with. -Formatting fixes. 0.4.5.3 Added alternate npc comments when the PC's orgasm is ruined. Thanks to Lollipop Scythe. Adjusted the plant npc orgasm comment checks to support female climax or ruined orgasms. Thanks to Lollipop Scythe. Added alternate orgasm text when forced to by the Wraith with a slime or urchin penis parasite. Thanks to Lollipop Scythe. -Given the Wraith compound event priority over placing camera's in the compound. Thanks to Lollipop Scythe. +Given the Wraith compound event priority over placing cameras in the compound. Thanks to Lollipop Scythe. Thanks to Lollipop Scythe for the following fixes: Fixed the greenthumb trail still being included in the background randomizer/importer. @@ -163,6 +420,126 @@ Fixed back image sprites for thick ponytail. Fixed missionary sprites for the star hairpin. +0.4.5.2 +Thanks to Kirsty for the following fixes: +Added toggle to facewear category to layer facewear behind hair. +Added failsafe to prevent props from overwriting equipped handheld items. +Added current number of stolen underwear to the journal, under the "Inventory" category. Adjusted some colours for easier skimming. +Brothel vending machine fixes and code cleanup. Players whose vending machines were mistakenly marked as sold should see them back in the dressing room. +Told Whitney to stop talking about PC's penis and visibly aroused and bushy pussy. (Simplified a "Nice <>" remark so it reads more naturally) +Players will finally stop groping their naked breasts underneath their shortalls. (Corrected hand z-index to layer on top of the shortalls/pinafores and disabled the sprites for covering your breasts when wearing these clothes without a shirt underneath) +Lowered the default halo sprite to rest beneath the umbrella instead of clipping through it. +Fixed an incorrect link when ducking on the pirate ship. +Sleeve sprite fixes. +Fixed the player being able to ask Whitney for a smoke prior to meeting the requisite conditions. +Fixed an incorrect wearProp call +Continued removal/replacement of incorrectly used icons. +Adjusted fatigue values for relaxing at the wolf cave. +Corrected audience members of some Whitney events. +Typo fixes. + +Thanks to Lollipop Scythe for the following fixes: +Fixed another issue with handheld items in the wardrobe editor. +Fixed the Medicine Drawer not being displayed when buying hair growth formula. +Fixed incorrect pronouns in a Bailey scene. +Prevented the player from flashing their butt when already exposed. +Fixed issues where Remy's Encroachmen showed up before ever visiting Alex's farm. +Fixed a minor pubic hair layer issue in the close images. +Fixed an issue with Eden sex after masturbating not properly ending the masturbation. +Fixed exposure and towel issue with Alex's farm. +Fixed incorrect passage of time at the docks. +Moved the message from hiding transformation parts to a more appropriate place. +Fixed an oversight that allowed access to the pillory when there was no one in it. +Fixed issue with the prison expecting you to work more hours than available after the intro scene. +Minor ear slime scene adjustment to clarify what dog it wants you to have sex with. +Formatting fixes. + + +0.4.5.1 +Thanks to Kirsty for the following fixes: +The vending machine can now be properly reinstalled and restocked after Briar sells it off. +Science lessons will no longer explode due to errant ifs. +Remy will no longer clamp an iron collar over your iron collar. +Alex's parent will no longer call them their little girl/boy based on what's in their pants. +The streets have been rendered safe from creeps staring at you and slimes commanding you get your hands bound. +Practised practice and travelled travel. +Removed stray pixel on space buns. +Fixed a bug with the random clothing feat booster. +Hid empty underoutfit category in forest shop. +Added missing sleeve sprite for the school vest. +Valentine's Day will no longer display in the journal unless the player has unlocked Eden's Valentine event. +The player can no longer explore the markets while exposed. +Fixed a broken chef inline event. +Corrected some dialogue on Remy's farm to account for the player being gagged. +Fixed or removed many incorrect icons and link indentations. +Fixed jingle-bell dress/skirt separating. +Fixed jingle-bell dress not unlocking properly. Thanks to Lollipop Scythe. +Typo Fixes. + +Thanks to Lollipop Scythe for the following fixes: +Fixed a bug that allowed you to collect semen during masturbation when dnenies or while wearing the chastity parasite. +Fixed an issue earslime commando demand not expiring. +Fixed bugs that prevented older outfits and wardrobes from including handheld items. +Feats later in a series but earned before prerequisite have been earned can now be displayed. +Fixed an issue with storing clothes outside wardrobes. +Fixed an event that didn't close properly at Alex's farm. + + +0.4.5.0 +Added a few new events to the pirate ship. Thanks to Crimson Tide. +Allowed the Ivory Wraith to possess you during blood moons on the pirate ship. Thanks to Crimson Tide. +Added a new event to the cafe, available after the cafe reopening. Written by Juniaki, and coded by AiHoshino. +Added hair growth formula to the hairdresser's that can be applied in the Medicine Drawer. Thanks to Lollipop Scythe. +Added alternate text for the chastity parasite in Harper's nightmare. Thanks to Lollipop Scythe. +Alex will now sometimes include booze in their shopping list. Written by Index and coded by Cutiland. +Thanks to P_Ruby, Midnight, and AiHoshino for code improvements. + +Thanks to Kirsty for the following additions and improvements: +Added a new clothing category: Handheld items. +Handheld items that the PC can purchase include umbrellas, a feather duster, and various bags for books. Umbrellas keep the PC dry and shaded, and feather dusters boost housekeeping. Some handheld items have the "bookbag" trait, which is useful for school. +Added a new arm position for the sidebar sprite. +Whitney will now share cigarettes with the PC in the park, if they are a love interest and have high love, and the PC is not pregnant. +Adds a stall that sells balloons and popcorn. It appears for the first time in summer, at the beach. It can help promote, or hurt, Robin's business, depending on the PC's choices. +Popcorn can be eaten in the park or at the beach to reduce trauma, or shared with Robin on a movie date. +Added an orphanage event that lets the PC raise hope by giving an orphan a balloon. +The PC can now buy gingerbread at the cafe during winter. Sprites by Beta. +The sidebar image will now show handheld props in certain scenes, such as sharing a milkshake with love interests or smoking with Whitney. +The pom poms hand item has been replaced with cheerleading gloves. Pom poms can now be found in the handheld category instead. +Added the jingle-bell dress and sleeveless jingle-bell dress to the forest shop, unlocked during Christmas. Thanks to Beta. +Added the jumper and festive/Christmas tree jumper to the clothing shop, as well as skull, heart, and ghost variants. Sprites by Kirsty. +Edited some rainy weather events to account for the player carrying an umbrella or otherwise wearing rainproof clothing. +Added new sections to the journal: Time-Sensitive, Reminders, and Holidays. +Cleaned up cafe code. + +Balance Changes +Going commando will now increase exhibitionism once more, but only if the PC willingly chose to do so. Thanks to Lollipop Scythe. +Removed the daily check so it becomes possible to explore the markets more than one time. Thanks to Cutiland. + +Bug Fixes +Fixed a bug that prevented the PC refusing to make breakfast for Eden with low housekeeping skill. Thanks to salagadoola. +Fixed a bug that made Robin appear in the orphanage garden and canteen when they were supposed to be in the mansion. Thanks to salagadoola. +Whitney will no longer smoke in front of PC when they are pregnant. Thanks to Kirsty. +Fixed an event that lowered hope when it should've raised it. Thanks to Kirsty. +Fixed incorrectly coded orphanage events and moved them to the appropriate event pools. Thanks to Kirsty. + +Thanks to P_Ruby for the following fixes: +Fixed a misdirected link that prevented a certain Whitney passage from displaying after an encounter. +Fixed a bug that prevented the right text displaying when asking Whitney to stop during a particular oral scene. +Fixed a bug that gave Winter a flashback to the last maths competition, if the PC was exposed during it. +Fixed a bug that made a Whitney scene decrease trauma instead of increasing it. +Fixed a bug that allowed the PC to make multiple confessions at the temple in the same day. +Minor fixes. + +Thanks to Lollipop Scythe for the following fixes: +Minor text adjustments to tentacle combat to clarify it's action when it attempts to damage a chastity device. +Added a missing chastity parasite tentacle check and text. +Added additional checks in masturbation to prevent multiple actions of the same sex toy. +Fix for missing sex toy name when orally using it. +Playtime tooltip adjustment. +Removed a debug check at the lake pond. +Fixed an issue with the feats importer with the local storage slots. + + 0.4.4.5 The Whitney analingus even should no longer have such a disproportionate influence on Whitney's dominance. @@ -591,7 +968,7 @@ Clothing is now colour-coded according to integrity in the sidebar description. Added a bunch of baby interactions for Alex. Thanks to Cutiland and Mona, with typo corrections by Kirsty. Added a docks ID variation scene that allows the PC to work there at any day of the week after getting the ID from Brar. Written by Meat Glacier and coded by Cutiland. Added the "whip" and "baton" weapon types. -The PC can now wake up to find Alex checking on their children at night. Scene written by IndexEnthusiast and coded by Cutiland. +The PC can now wake up to find Alex checking on their children at night. Scene written by Index and coded by Cutiland. Added a series of small events around the orphanage that involve posters or paper in some way. Written by Soul and coded by Cutiland. Added a collection of events to the orphanage that involve music or musical instruments as a theme. Written by Soul and coded by Cutiland. Added the "Defy the Night" feat. diff --git a/README.md b/README.md index 0fb5c9b02..7cc21f000 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,3 @@ -# Degrees of Lewdity ѱ ǿ Ͽ - - Vrelnir Degrees of Lewdity (https://vrelnir.blogspot.com/) CC-BY-NC-SA ̼ ǰ ī̺ ؽƮ ä (https://arca.live/b/textgame) Դϴ. - - ϴ 2۹ CC-BY-NC-SA ̼ ̻ Ӱ ( ) Ͻ ֽϴ. - -1. BY - (ó) Ǿ մϴ. -2. NC - 񿵸 θ ̿ մϴ. -3. SA - CC-BY-NC-SA ̼ Ͽ մϴ. - -## -- CC-BY-NC-SA ̼ (): ԵǾ ִ LICENSE -- CC-BY-NC-SA ̼ ѱ۹: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ko -- CC-BY-NC-SA ̼ Ǹ ѱ۹: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ko - - Ʒʹ README.md ̾ϴ. ---------------------------------------- # Degrees of Lewdity ## Lexicon of Lewdity @@ -43,7 +26,7 @@ _Failure to do so can lead to your work being denied._ ### Build Android version (.apk) -See [mobile-build.md](docs\mobile-build.md) +See [README in devTools/apkbuilder](devTools/apkbuilder/README-windows.txt) ## Development diff --git a/deprecated.twee-config.yml b/deprecated.twee-config.yml new file mode 100644 index 000000000..f9b153b2c --- /dev/null +++ b/deprecated.twee-config.yml @@ -0,0 +1,522 @@ +aliases: + decoration: + img: &img + border: 1px + borderStyle: dashed dashed dashed + unused: &unused + border: 0px +sugarcube-2: + macros: + abominationold: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + abominationold2: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + actionsanuspenisfuck: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + actionsanustopenis: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + actionsvaginapenisfuck: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + actionsvaginatopenis: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + actionsvaginatovagina: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + actionsvaginatovaginafuck: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + adultshopicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + animals: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - '?animals' + bathicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + bathrobeicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + beericon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + binicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + bloodmirroricon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + bodyparts: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - '?bodypart' + breastenlargeicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + breastreduceicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + cellicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + checkTimeSystem: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - Time cannot desync anymore + chickenicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + clearyardicon: + deprecated: true + deprecatedSuggestions: + - <> + closedstoreicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + clothingShop: # Deprecated 0.4.5+ + deprecated: true + clothingShopColorSet: # Deprecated 0.4.5+ + deprecated: true + clothingshopicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + coffeeicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + computericon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + condommachineicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + contactsicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + cottageicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + cowicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + danubehouseicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + dilapidatedshopicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + dogicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + domushouseicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + dyeicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + eaticon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + feetgrab: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + flatdooricon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + flatdooropenicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + flowericon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + forestroaddeepicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + forestroadtownicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + forestshopicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + garden: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - '?garden' + generatec1: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + generatec2: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + generatec3: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + generatec4: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + generatec5: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + generatec6: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + hairchairicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + hairgelicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + headdeskicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + hitchhikeicon: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + homemirroricon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + horseicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + hotchocicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + hotchocstandicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + humiliation10: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <><> + infirmaryicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + lemonadeicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + lemonadestandicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + libraryicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + lowerbodyparts: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - '?lowerbodypart' + masturbationactionsOld: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + masturbationeffectsOld: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + meadowicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + milkshakeicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + newversionnotification: # Deprecated 0.4.5+ + deprecated: true + description: Has been non-functioning for a good while now + olivenewicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + oralswallow: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + orphanagedoorsicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + parasitecreamicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + partyicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - - <> + paternity_test: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + penisenlargeicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + penisreduceicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + pigicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + plaqueicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + poollockericon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + printAnd: # Deprecrated 0.4.5.3+ + deprecated: true + deprecatedSuggestions: + - and + description: |- + Prints `and` for use where `and` cannot be used without escaping + printTo: # Deprecrated 0.4.5.3+ + deprecated: true + deprecatedSuggestions: + - to + description: |- + Prints `to` for use where `to` cannot be used without escaping + prisoncanteenicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + prisondooricon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + prisonlaundryicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + prisonlifticon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + prisonmedicalicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + prisonrunicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + putinlockericon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + registericon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + scarletnewicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + schoolbuildingicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + schoolcourtyardicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + searchicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + secretpathicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + shaveicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + shopbuy: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + shopClothingFilterLoadTraits: # Deprecated 0.4.5+ + deprecated: true + shopClothingFilterSettings: # Deprecated 0.4.5+ + deprecated: true + shopClothingFilterToggle: # Deprecated 0.4.5+ + deprecated: true + shopCustomColors: # Deprecated 0.4.5+ + deprecated: true + shopCustomColorsBars: # Deprecated 0.4.5+ + deprecated: true + shopCustomColorsDisplay: # Deprecated 0.4.5+ + deprecated: true + shopCustomColorsPresets: # Deprecated 0.4.5+ + deprecated: true + shopCustomColorsPresetsLoad: # Deprecated 0.4.5+ + deprecated: true + shopCustomColorsSet: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - '<>' + shopCustomColorsToggle: # Deprecated 0.4.5+ + deprecated: true + shopDefaultOptions: # Deprecated 0.4.3+ + deprecated: true + shopDefaultOptionsToggle: # Deprecated 0.4.3+ + deprecated: true + shoppingcentreicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + shopTraitDescription: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - 'setup.shopDetails[_trait].desc' + sizeLimitsSettingsOld: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + stairschristmasicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + stairsdownicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + stairshalloweenicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + stairsupicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + statbaricons: # Deprecated Old + deprecated: true + description: "![img](%workspaceDir%/img/ui/point.png)" + stripicon: # Deprecated 0.4.5+ + deprecated: true + deprecatedSuggestions: + - <> + stumpicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + sweetsicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + - <> + swimbackicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + tailoricon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + tattooicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + toileticon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + toyshopicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> + updateClothes-obsolete: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + updateClothingColours-obsolete: # Deprecated Old + deprecated: true + deprecatedSuggestions: + - <> + venteyesicon: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + volleyballicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - - <> + wolfpup: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + wolfsleep: # Deprecated 0.4.2+ + deprecated: true + deprecatedSuggestions: + - <> + wrenchicon: # Deprecated 0.4.3+ + deprecated: true + deprecatedSuggestions: + - <> \ No newline at end of file diff --git a/devTools/apkbuilder/README-linux b/devTools/apkbuilder/README-linux new file mode 100644 index 000000000..f8f948856 --- /dev/null +++ b/devTools/apkbuilder/README-linux @@ -0,0 +1,15 @@ +cordova apk builder for primarily DoL + +dependencies: (in parentheses are versions at the moment of writing) +- nodejs stable (20.10.0), with npm and npx installed +- gradle latest (8.5) +- openjdk 17 (Temurin-17.0.8.1+1) +- commandlinetools latest (10406996_latest) https://developer.android.com/studio#cmdline-tools scroll to the bottom of the page + +to create a building environment, install nodejs, gradle and openjdk with your favorite package manager, then unzip and place `bin/`, `lib/`, and `source.properties` from commandlinetools archive into `androidsdk/cmdline-tools/latest`, then run ./setup_deps.sh. this only needs to be done once per repository. + +debug builds are set up so they can be installed in parallel with release builds. to run debug build, run `./build_app_debug.sh`. compiled apk will be sent to the `../../dist/` directory. + +release builds require official dol.keystore to sign the resulting apk. using any other keystore will make the app incompatible with official release. +in vast majority of cases, you'll be better off with a debug build, but if you're a mod dev and you really want your apk signed, then you can use the release build. first, you need to change `android-packageName` in `config.xml`, so it won't conflict with official app. second, generate a new keystore by running `keytool -genkey -v -keystore dol.keystore -alias dol -keyalg RSA -keysize 2048 -deststoretype pkcs12 -validity 9999` command, included with jdk. once done, place generated `dol.keystore` into `../../keys`. +after that, you can run `./build_app_release.sh`. it will ask for password to keystore. once the build is successful, you can uncomment the line in `./build_app_release.sh` to stop it from further asking for passwords. like with debug builds, compiled apks are sent to `../../dist`. diff --git a/devTools/apkbuilder/README-windows.txt b/devTools/apkbuilder/README-windows.txt new file mode 100644 index 000000000..9fac9a0b5 --- /dev/null +++ b/devTools/apkbuilder/README-windows.txt @@ -0,0 +1,32 @@ +cordova apk builder for primarily DOL + +# Installing dependencies +## nodejs latest +- download from https://nodejs.org/, ~25.4mb +- doesn't matter if it's LTS or latest +- options "npm package manager" and "Add to PATH" must be selected +- "Automatically install the necessary tools" is optional + +## openjdk / temurin 17 +- download the latest version of .msi installer for windows from https://adoptium.net/temurin/releases/?version=17&package=jdk&arch=x64, ~171mb +- when installing, choose to install "Set JAVA_HOME variable" option + +## android commandline-tools latest +- download from https://developer.android.com/studio#cmdline-tools, links for tools-only are at the bottom of the page, ~146mb +- in the folder with this README, open androidsdk\cmdline-tools\latest, and extract "bin", "lib", and "source.properties" from the downloaded archive from the previous step into it. there should be no other sub-folders + +## gradle 7.4.2 +- download from https://gradle.org/next-steps/?version=7.4.2&format=bin, ~110mb +- extract the archive into `androidsdk/gradle` + +## the rest +- once everything above is done, run `setup_deps.bat` + +# Building the apk +## debug build +debug builds require no additional steps and can be installed in parallel with official app. +just run `build_app_debug.bat` batch file. compiled apk will appear in the dist folder. + +## release build +if you have the official dol.keystore, run `build_app_debug.bat` batch file. +if you don't have one, stick to debug builds, they're not any less good. diff --git a/devTools/apkbuilder/build_app_debug.bat b/devTools/apkbuilder/build_app_debug.bat new file mode 100644 index 000000000..e238c6afc --- /dev/null +++ b/devTools/apkbuilder/build_app_debug.bat @@ -0,0 +1,19 @@ +@echo off +SET "PATH=%PATH%;%CD%\androidsdk\commandline-tools\latest\bin;%CD%\androidsdk\gradle\bin" +SET "ANDROID_HOME=%CD%\androidsdk" +SET "ANDROID_SDK_ROOT=%ANDROID_HOME%" + +IF NOT EXIST "..\..\Degrees of Lewdity.html" ( + IF EXIST "..\..\Degrees of Lewdity VERSION.html" ( + copy "..\..\Degrees of Lewdity VERSION.html" "..\..\Degrees of Lewdity.html" + ) ELSE ( + echo game is not compiled. make sure to run compile.bat first + pause + exit + ) +) + +@echo on +call node scripts/build_app.js + +pause diff --git a/devTools/apkbuilder/build_app_debug.sh b/devTools/apkbuilder/build_app_debug.sh new file mode 100644 index 000000000..4a30c9a6c --- /dev/null +++ b/devTools/apkbuilder/build_app_debug.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# set up the environment +ANDROID_HOME="$PWD/androidsdk/" \ +ANDROID_SDK_ROOT="$ANDROID_HOME" \ +ANDROID_PATH="$ANDROID_HOME/cmdline-tools/latest/bin" \ +PATH="./androidsdk/cmdline-tools/latest/bin:$PATH" \ +node scripts/build_app.js diff --git a/devTools/apkbuilder/build_app_release.bat b/devTools/apkbuilder/build_app_release.bat new file mode 100644 index 000000000..4c5ce6a51 --- /dev/null +++ b/devTools/apkbuilder/build_app_release.bat @@ -0,0 +1,24 @@ +@echo off +SET "PATH=%PATH%;%CD%\androidsdk\commandline-tools\latest\bin;%CD%\androidsdk\gradle\bin" +SET "ANDROID_HOME=%CD%\androidsdk" +SET "ANDROID_SDK_ROOT=%ANDROID_HOME%" + +IF NOT EXIST "..\..\Degrees of Lewdity.html" ( +REM windows' compile.bat is dumb about versions + IF EXIST "..\..\Degrees of Lewdity VERSION.html" ( + copy "..\..\Degrees of Lewdity VERSION.html" "..\..\Degrees of Lewdity.html" + ) ELSE ( + echo game is not compiled. make sure to run compile.bat first + pause + exit + ) +) + +REM uncomment the line below (remove "REM") to skip prompt for password when it's set correctly +REM SET SKIP_PASSWORD=true + +SET BUILD_RELEASE=true +@echo on +call node scripts/build_app.js + +pause diff --git a/devTools/apkbuilder/build_app_release.sh b/devTools/apkbuilder/build_app_release.sh new file mode 100644 index 000000000..d999f294b --- /dev/null +++ b/devTools/apkbuilder/build_app_release.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# uncomment this flag to skip asking for password if the correct one is already stored in build.json +#export SKIP_PASSWORD=true + +ANDROID_HOME="$PWD/androidsdk/" \ +ANDROID_SDK_ROOT="$ANDROID_HOME" \ +ANDROID_PATH="$ANDROID_HOME/cmdline-tools/latest/bin" \ +PATH="./androidsdk/cmdline-tools/latest/bin:$PATH" \ +BUILD_RELEASE=true \ +node scripts/build_app.js diff --git a/devTools/apkbuilder/config.xml b/devTools/apkbuilder/config.xml new file mode 100644 index 000000000..f38657c14 --- /dev/null +++ b/devTools/apkbuilder/config.xml @@ -0,0 +1,33 @@ + + + Degrees of Lewdity + Degrees of Lewdity + + Vrelnir + + + + + + + + + + + + + + + + + + + + + diff --git a/devTools/apkbuilder/package.json b/devTools/apkbuilder/package.json new file mode 100644 index 000000000..95caf2c0b --- /dev/null +++ b/devTools/apkbuilder/package.json @@ -0,0 +1,32 @@ +{ + "name": "apk-builder", + "displayName": "built app", + "version": "1.0.0", + "description": "pack html games into a nice cordova app", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "ecosystem:cordova" + ], + "author": "majou", + "license": "you owe author a beer now.", + "dependencies": { + "cordova": "^12.0.0" + }, + "cordova": { + "platforms": [ + "android" + ], + "plugins": { + "cordova-plugin-save-dialog": {}, + "cordova-plugin-rnk-toast": {} + } + }, + "devDependencies": { + "cordova-android": "11.0.x", + "cordova-plugin-rnk-toast": "^0.0.3", + "cordova-plugin-save-dialog": "^2.0.1" + } +} \ No newline at end of file diff --git a/devTools/apkbuilder/res/GooglePlayStore.png b/devTools/apkbuilder/res/GooglePlayStore.png new file mode 100644 index 000000000..aae6172b4 Binary files /dev/null and b/devTools/apkbuilder/res/GooglePlayStore.png differ diff --git a/devTools/apkbuilder/res/icon-hdpi.png b/devTools/apkbuilder/res/icon-hdpi.png new file mode 100644 index 000000000..ec0bb8e06 Binary files /dev/null and b/devTools/apkbuilder/res/icon-hdpi.png differ diff --git a/devTools/apkbuilder/res/icon-ldpi.png b/devTools/apkbuilder/res/icon-ldpi.png new file mode 100644 index 000000000..4f06cd039 Binary files /dev/null and b/devTools/apkbuilder/res/icon-ldpi.png differ diff --git a/devTools/apkbuilder/res/icon-mdpi.png b/devTools/apkbuilder/res/icon-mdpi.png new file mode 100644 index 000000000..1745723e1 Binary files /dev/null and b/devTools/apkbuilder/res/icon-mdpi.png differ diff --git a/devTools/apkbuilder/res/icon-xhdpi.png b/devTools/apkbuilder/res/icon-xhdpi.png new file mode 100644 index 000000000..f6ff75de5 Binary files /dev/null and b/devTools/apkbuilder/res/icon-xhdpi.png differ diff --git a/devTools/apkbuilder/res/icon-xxhdpi.png b/devTools/apkbuilder/res/icon-xxhdpi.png new file mode 100644 index 000000000..c810cc99f Binary files /dev/null and b/devTools/apkbuilder/res/icon-xxhdpi.png differ diff --git a/devTools/apkbuilder/res/icon-xxxhdpi.png b/devTools/apkbuilder/res/icon-xxxhdpi.png new file mode 100644 index 000000000..435eecf5c Binary files /dev/null and b/devTools/apkbuilder/res/icon-xxxhdpi.png differ diff --git a/devTools/apkbuilder/res/magick_icons.sh b/devTools/apkbuilder/res/magick_icons.sh new file mode 100644 index 000000000..f3529a737 --- /dev/null +++ b/devTools/apkbuilder/res/magick_icons.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# convert source icon to appropriate sizes +if magick -version; then continue; else echo "this script requires image magick installed and in PATH"; fi; + +magick icon.png -resize 192x192 icon-xxxhdpi.png +magick icon.png -resize 144x144 icon-xxhdpi.png +magick icon.png -resize 96x96 icon-xhdpi.png +magick icon.png -resize 72x72 icon-hdpi.png +magick icon.png -resize 48x48 icon-mdpi.png +magick icon.png -resize 36x36 icon-ldpi.png diff --git a/devTools/apkbuilder/res/splashscreen.png b/devTools/apkbuilder/res/splashscreen.png new file mode 100644 index 000000000..5d9674eb1 Binary files /dev/null and b/devTools/apkbuilder/res/splashscreen.png differ diff --git a/devTools/apkbuilder/scripts/build_app.js b/devTools/apkbuilder/scripts/build_app.js new file mode 100644 index 000000000..17aa000c3 --- /dev/null +++ b/devTools/apkbuilder/scripts/build_app.js @@ -0,0 +1,93 @@ +/** +* build wrapper +*/ +(async () => { + const cordova = require('cordova'); + const read = require('read'); + const fs = require('fs-extra'); + const path = require('path'); + const xml = require('cordova-common').xmlHelpers; + + // assume we're in gameRoot/devTools/apkbuilder + const gameRoot = path.resolve("../.."); + + const release = process.env.BUILD_RELEASE; + // read version info from gameRoot/game/01-config/sugarcubeConfig.js + const versionData = fs.readFileSync(path.resolve(gameRoot, "game/01-config/sugarcubeConfig.js")).toString(); + // extract 0.X.Y.Z from 'version: "0.X.Y.Z",' + const version = versionData.match(/(?<=version: ")\d.\d*.\d*.\d*/)[0]; + // adjust config.xml + const configFile = path.resolve("config.xml"); + const configData = xml.parseElementtreeSync(configFile); + // update version + configData.getroot().attrib.version = version; + // update package name for debug and release builds + let targetName = configData.getroot().attrib['android-packageName']; + if (release && targetName.endsWith("_debug")) targetName = targetName.slice(0, -6); + else if (!release && !targetName.endsWith("_debug")) targetName += "_debug"; + configData.getroot().attrib['android-packageName'] = targetName; + fs.writeFileSync(configFile, configData.write({indent: 4}), "utf-8"); + // update android manifest + const manifestFile = path.resolve('platforms/android/app/src/main/AndroidManifest.xml'); + if (fs.existsSync(manifestFile)) { + const manifestData = xml.parseElementtreeSync(manifestFile); + if (manifestData.getroot().attrib.package !== targetName) { + const prevName = manifestData.getroot().attrib.package; + // update targetName + manifestData.getroot().attrib.package = targetName; + fs.writeFileSync(manifestFile, manifestData.write({ indent: 4 }), 'utf-8'); + // update project paths + const javaDir = "platforms/android/app/src/main/java/"; + const prevDir = path.resolve(javaDir, prevName.split(".").join("/")); + const targetDir = path.resolve(javaDir, targetName.split(".").join("/")) + fs.moveSync(prevDir, targetDir); + const mainActivity = path.resolve(targetDir, "MainActivity.java"); + fs.writeFileSync(mainActivity, fs.readFileSync(mainActivity).toString().replace(prevName, targetName)); + } + } + + + if (release) { + const keystore = path.resolve(gameRoot, 'keys/dol.keystore'); + if (release && !fs.existsSync(keystore)) { + throw new Error( + `Release build requires a valid keystore @${keystore}. + If you don't have one, it is recommended to use debug build instead.`) + } + + const buildFile = path.resolve('build.json'); + const buildFileExists = fs.existsSync(buildFile); + let password = "" + try { + if (buildFileExists && process.env.SKIP_PASSWORD); // skip the prompt + else password = await read({ + prompt: 'Please enter the password' + (buildFileExists ? ' (or press enter to re-use last provided): ' : ': '), + silent: true, + replace: '*', + timeout: 15000, + }) + } catch (ex) { + if (ex.message !== "timed out" || !buildFileExists) return console.log("\n " + ex.message); + } + + if (password) { + const build_opts = { + android: { + release: { + keystore: keystore, + storePassword: password, + password: password, + alias: "dol", + keystoreType: "pkcs12", + packageType: "apk", + } + } + } + fs.writeFileSync(buildFile, JSON.stringify(build_opts, null, "\t")); + } + else if(buildFileExists) console.log(" re-using old password "); + else return console.log(" no password provided, exiting ") + } + + cordova.build({options: {release}}); +})() diff --git a/devTools/apkbuilder/scripts/move_built_apks.js b/devTools/apkbuilder/scripts/move_built_apks.js new file mode 100644 index 000000000..eaab57c06 --- /dev/null +++ b/devTools/apkbuilder/scripts/move_built_apks.js @@ -0,0 +1,32 @@ +/* + * move built apks to an easier to access dist/ folder + */ +const fs = require("fs-extra"); +const path = require("path"); +const xml = require("cordova-common").xmlHelpers; + +const gameRoot = path.resolve("../.."); +const src = path.resolve("platforms/android/app/build/outputs/apk"); +const dest = path.resolve(gameRoot, "dist"); + +// extract app name from config.xml +// const appName = fs.readFileSync("config.xml").toString().match(/(?<=>).*?(?=<\/name>)/)[0].replace(/ /g,"-"); +const configData = xml.parseElementtreeSync("config.xml"); +const appName = configData.getroot().getchildren().find(c => c.tag === "name").text.replace(/ /g, "-"); +// check apk build dirs and move built apks to the dist dir for easier access +if (fs.existsSync(src)) fs.readdirSync(src).forEach(dir => { + const targetDir = path.resolve(src, dir); + if (!fs.statSync(targetDir).isDirectory()) return; // in case of some hidden system files + const metadata = fs.readJSONSync(path.resolve(targetDir, "output-metadata.json")); + const srcFile = metadata.elements[0].outputFile; + const version = metadata.elements[0].versionName; + const variant = metadata.variantName; + const destFile = appName + "-" + version + "-" + variant + ".apk"; + const srcPath = path.resolve(targetDir, srcFile); + const destPath = path.resolve(dest, destFile); + if (fs.existsSync(srcPath)) { + fs.moveSync(srcPath, destPath, {overwrite: true}); + console.log("moved " + srcFile + " to " + destPath); + } +}) + diff --git a/devTools/apkbuilder/scripts/prepare_files.js b/devTools/apkbuilder/scripts/prepare_files.js new file mode 100644 index 000000000..5b9a6c20b --- /dev/null +++ b/devTools/apkbuilder/scripts/prepare_files.js @@ -0,0 +1,59 @@ +/* + * update and patch files that will go into the built apk + */ +const fs = require('fs-extra'); +const path = require('path'); + +const sourcePath = path.resolve('../..'); // assume we're in gameDir/devTools/apkbuilder +const sourceDirs = ["img"]; // it's almost universally just img +const sourceName = "Degrees of Lewdity.html"; // game's compiled html +const targetPath = path.resolve('./platforms/android/app/src/main/assets/www'); +const targetName = "index.html"; + +// before anything else, move saved sourceDirs to www +const tmp = path.resolve("tmp"); +sourceDirs.forEach(dir => { + const savedDir = path.resolve(tmp, dir); + const targetDir = path.resolve(targetPath, dir); + if (fs.existsSync(savedDir)) fs.moveSync(savedDir, targetDir); +}) + +let counter = 0; // files copied/updated +let skipCounter = 0; // files skipped +let delCounter = 0; // files removed +// recursively copy files from src to dest, preserving timestamps and skipping copy if file already exists and timestamps match +function syncCopy(src, dest, options) { + //console.log("entry", src, dest); + if (!fs.existsSync(src)) return console.log("syncCopy fail: source doesn't exist: ", src); + if (fs.statSync(src).isDirectory()) { + fs.ensureDirSync(dest); // make sure dest exists, create it if it doesn't + const srcFiles = fs.readdirSync(src); // files in source dir + const destFiles = fs.readdirSync(dest); // files in dest dir + const absentFiles = destFiles.filter(f => !srcFiles.includes(f)); // files to remove from dest dir because they were removed from src dir + // console.log("wawawa", absentFiles); + for (const file of absentFiles) { + fs.rmSync(path.resolve(dest, file), { recursive: true }); + delCounter++; + } + for (const file of srcFiles) syncCopy(path.resolve(src, file), path.resolve(dest, file), options); + } + // note: copied files' .mtimeMs are preserved with ms precision, but .mtimeMs can have ns precision, requiring rounding + else if (!fs.existsSync(dest) || Math.round(fs.statSync(src).mtimeMs) !== Math.round(fs.statSync(dest).mtimeMs)) { + counter++ + fs.cpSync(src, dest, {dereference: true, preserveTimestamps: true}); + } + else skipCounter++; +} +sourceDirs.forEach(dir => syncCopy(path.resolve(sourcePath, dir), path.resolve(targetPath, dir))) +console.log(`copied ${counter} files, skipped ${skipCounter} files that don't need updating, and removed ${delCounter} files not present in sourceDirs`); + +// copy html and update it to include and make use of selected scripts +fs.readFile(path.resolve(sourcePath, sourceName), (err, data) => { + if (err) return console.log("Error reading source html", err); + let updatedData = data.toString() + .replace('\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t
\n\t\t
\n\t\t

Browser lacks capabilities required to play.

Upgrade or switch to another browser.

\n\t\t
Loading…
\n\t
\n\t{{STORY_DATA}}\n\t\n\n\n"}); \ No newline at end of file +window.storyFormat({"name":"SugarCube","version":"2.36.1","description":"A full featured, highly customizable story format. See its documentation.","author":"Thomas Michael Edwards","image":"icon.svg","url":"http://www.motoslave.net/sugarcube/","license":"BSD-2-Clause","proofing":false,"source":"\n\n\n\n{{STORY_NAME}}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t
\n\t\t
\n\t\t

Browser lacks capabilities required to play.

Upgrade or switch to another browser.

\n\t\t
Loading…
\n\t
\n\t{{STORY_DATA}}\n\t\n\n\n"}); \ No newline at end of file diff --git a/devTools/tweego/storyFormats/sugarcube-2/format.js b/devTools/tweego/storyFormats/sugarcube-2/format.js index 2f659eb39..5b48d55ed 100644 --- a/devTools/tweego/storyFormats/sugarcube-2/format.js +++ b/devTools/tweego/storyFormats/sugarcube-2/format.js @@ -1 +1 @@ -window.storyFormat({"name":"SugarCube","version":"2.36.1","description":"A full featured, highly customizable story format. See its documentation.","author":"Thomas Michael Edwards","image":"icon.svg","url":"http://www.motoslave.net/sugarcube/","license":"BSD-2-Clause","proofing":false,"source":"\n\n\n\n{{STORY_NAME}}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t
\n\t\t
\n\t\t

Browser lacks capabilities required to play.

Upgrade or switch to another browser.

\n\t\t
Loading…
\n\t
\n\t{{STORY_DATA}}\n\t\n\n\n"}); \ No newline at end of file +window.storyFormat({"name":"SugarCube","version":"2.36.1","description":"A full featured, highly customizable story format. See its documentation.","author":"Thomas Michael Edwards","image":"icon.svg","url":"http://www.motoslave.net/sugarcube/","license":"BSD-2-Clause","proofing":false,"source":"\n\n\n\n{{STORY_NAME}}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t
\n\t\t
\n\t\t

Browser lacks capabilities required to play.

Upgrade or switch to another browser.

\n\t\t
Loading…
\n\t
\n\t{{STORY_DATA}}\n\t\n\n\n"}); \ No newline at end of file diff --git a/devTools/typings.d.ts b/devTools/typings.d.ts index 0995f5cfb..a1cc0bc71 100644 --- a/devTools/typings.d.ts +++ b/devTools/typings.d.ts @@ -55,7 +55,7 @@ declare interface ClothesItem { accessory_colour_options: string[]; accessory_colour_sidebar: number; /** - * if 1, then accessory files are integrity-dependent "acc_(torn|tattered|frayed|full).png" + * if 1, then accessory files are integrity-dependent "acc_(tattered|torn|frayed|full).png" */ accessory_integrity_img?: 0|1; high_img: 0|1; diff --git a/docs/mobile-build.md b/docs/mobile-build-deprecated.md similarity index 100% rename from docs/mobile-build.md rename to docs/mobile-build-deprecated.md diff --git a/events.twee-config.yml b/events.twee-config.yml new file mode 100644 index 000000000..f964352e9 --- /dev/null +++ b/events.twee-config.yml @@ -0,0 +1,221 @@ +sugarcube-2: + macros: + alexBoozeShopping: + name: alexBoozeShopping + balloonRobinIntro: + name: balloonRobinIntro + beach_strip_events: + name: beach_strip_events + tags: ["multiEvents"] + blackWolfPresentsPregnancyToPlayer: + name: blackWolfPresentsPregnancyToPlayer + CaveHumanPregnancyDiscovered: + name: CaveHumanPregnancyDiscovered + eventParkStreak1: + name: eventParkStreak1 + eventParkStreak2: + name: eventParkStreak2 + eventParkStreak3: + name: eventParkStreak3 + eventParkStreak4: + name: eventParkStreak4 + eventParkStreak5: + name: eventParkStreak5 + events_catacombs: + description: |- + Prints an inline event passage from the `events_catacomb` pool + + `<>` + - **subset**: `string` - subset of catacombs events to pull from + - `"normal"` | `"compound"` | `"barrow"` + parameters: + - '"normal"|"compound"|"barrow"' + tags: ["multiEvents"] + events_pirate_bilge: + name: events_pirate_bilge + tags: ["multiEvents"] + events_pirate_cabin: + name: events_pirate_cabin + tags: ["multiEvents"] + events_pirate_deck: + name: events_pirate_deck + tags: ["multiEvents"] + eventsenglishsleep: + name: eventsenglishsleep + tags: ["multiEvents"] + eventshistorysleep: + name: eventshistorysleep + tags: ["multiEvents"] + eventsmathssleep: + name: eventsmathssleep + tags: ["multiEvents"] + eventssciencesleep: + name: eventssciencesleep + tags: ["multiEvents"] + forest_nest: + name: forest_nest + forestarrow: + name: forestarrow + forestbear: + name: forestbear + forestboar: + name: forestboar + ForestBullies: + name: ForestBullies + forestcutter: + name: forestcutter + forestdagger: + name: forestdagger + forestgather: + name: forestgather + forestgem: + name: forestgem + forestplant_kiss: + name: forestplant_kiss + forestplant_snare: + name: forestplant_snare + forestriver: + name: forestriver + forestrock: + name: forestrock + forestsnake: + name: forestsnake + forestwasps: + name: forestwasps + forestwolfspy: + name: forestwolfspy + forestwolfspy2: + name: forestwolfspy2 + fox_grudge: + name: fox_grudge + halloween_lake_invite: + name: halloween_lake_invite + hitchike_1: + name: hitchike_1 + hitchike_2: + name: hitchike_2 + hitchike_3: + name: hitchike_3 + hitchike_4: + name: hitchike_4 + hitchike_5: + name: hitchike_5 + hitchike_6: + name: hitchike_6 + hitchike_7: + name: hitchike_7 + hitchike_8: + name: hitchike_8 + hitchike_9: + name: hitchike_9 + island_events_arrow: + name: island_events_arrow + island_events_battle: + name: island_events_battle + island_events_battleground: + name: island_events_battleground + island_events_bay: + name: island_events_bay + island_events_birds: + name: island_events_birds + island_events_broken_mask: + name: island_events_broken_mask + island_events_castle: + name: island_events_castle + island_events_cat: + name: island_events_cat + island_events_cat_2: + name: island_events_cat_2 + island_events_duel: + name: island_events_duel + island_events_flower: + name: island_events_flower + island_events_gulch: + name: island_events_gulch + island_events_hunted: + name: island_events_hunted + island_events_mosquitoes: + name: island_events_mosquitoes + island_events_overhear: + name: island_events_overhear + island_events_plain: + name: island_events_plain + island_events_pleasant_flower: + name: island_events_pleasant_flower + island_events_plumeria: + name: island_events_plumeria + island_events_rocky: + name: island_events_rocky + island_events_rope: + name: island_events_rope + island_events_runes: + name: island_events_runes + tags: ["unused"] + island_events_shore: + name: island_events_shore + island_events_sing: + name: island_events_sing + island_events_slugs: + name: island_events_slugs + island_events_snake: + name: island_events_snake + island_events_squirrel: + name: island_events_squirrel + island_events_tear: + name: island_events_tear + island_events_trap: + name: island_events_trap + island_events_trilobite: + name: island_events_trilobite + island_events_walnut: + name: island_events_walnut + island_events_walnut_2: + name: island_events_walnut_2 + island_events_walnut_3: + name: island_events_walnut_3 + island_events_waterfall: + name: island_events_waterfall + island_events_wood: + name: island_events_wood + island_events_wood_1: + name: island_events_wood_1 + island_events_wood_2: + name: island_events_wood_2 + island_events_wood_3: + name: island_events_wood_3 + island_events_wood_4: + name: island_events_wood_4 + kylarFlirtEventClothesPull: + name: kylarFlirtEventClothesPull + kylarLibraryStalkTease1: + description: |- + Prints paragraph teasing Kylar in the library + tags: ["text"] + mushroompick: + name: mushroompick + scienceCondomIntro: + name: scienceCondomIntro + scienceProject: + name: scienceProject + stallShop-main: + name: stallShop-main + sydneyEvent1AnalBeads: + name: sydneyEvent1AnalBeads + sydneyEvent2CuffedSydney: + name: sydneyEvent2CuffedSydney + sydneyEvent3HumblePeddlerofHonestWares: + name: sydneyEvent3HumblePeddlerofHonestWares + sydneyEvent4Backrooms: + name: sydneyEvent4Backrooms + whitney_roof_events: + description: |- + Draws and prints the start of a Whitney bully event + tags: ["multiEvents", "text", "links"] + whitneyShoppingCentre: + description: |- + Prints interrupt passage of Whitney taking pc to the shopping centre + tags: ["text", "links"] + wolfwetnurse: + name: wolfwetnurse + wraithIntro: + name: wraithIntro diff --git a/game/00-framework-tools/03-Patcher/DoLError.js b/game/00-framework-tools/03-Patcher/dol-error.js similarity index 100% rename from game/00-framework-tools/03-Patcher/DoLError.js rename to game/00-framework-tools/03-Patcher/dol-error.js diff --git a/game/00-framework-tools/03-Patcher/DoLWidget.js b/game/00-framework-tools/03-Patcher/dol-widget.js similarity index 100% rename from game/00-framework-tools/03-Patcher/DoLWidget.js rename to game/00-framework-tools/03-Patcher/dol-widget.js diff --git a/game/00-framework-tools/03-Patcher/stringFromPatcher.js b/game/00-framework-tools/03-Patcher/string-from-patcher.js similarity index 100% rename from game/00-framework-tools/03-Patcher/stringFromPatcher.js rename to game/00-framework-tools/03-Patcher/string-from-patcher.js diff --git a/game/00-framework-tools/10-time/00-time-constants.js b/game/00-framework-tools/10-time/00-time-constants.js new file mode 100644 index 000000000..ddd2b1629 --- /dev/null +++ b/game/00-framework-tools/10-time/00-time-constants.js @@ -0,0 +1,27 @@ +/** + * Stores constants for time. + * This was contained in time.js, but that causes issues with placement of that file. + * As datetime.js had to reference Time to get these. Even though datetime.js should be + * an isolated type for the most part. + * + * We could move these again at some point, like into datetime.js, as static fields of DateTime. + * This cannot be done at the moment due to ECMA2022 being too new. + */ +const TimeConstants = (() => { + const secondsPerDay = 86400; + const secondsPerHour = 3600; + const secondsPerMinute = 60; + const standardYearMonths = Object.freeze([31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]); + const leapYearMonths = Object.freeze([31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]); + const synodicMonth = 29.53058867; + + return Object.freeze({ + secondsPerDay, + secondsPerHour, + secondsPerMinute, + standardYearMonths, + leapYearMonths, + synodicMonth, + }); +})(); +window.TimeConstants = TimeConstants; diff --git a/game/00-framework-tools/10-time/datetime.js b/game/00-framework-tools/10-time/datetime.js new file mode 100644 index 000000000..6d0ac458f --- /dev/null +++ b/game/00-framework-tools/10-time/datetime.js @@ -0,0 +1,489 @@ +class DateTime { + constructor(year = 2020, month = 1, day = 1, hour = 0, minute = 0, second = 1) { + if (arguments.length === 1) { + // If the argument is a DateTime object, copy its properties + if (year instanceof DateTime) { + this.year = year.year; + this.month = year.month; + this.day = year.day; + this.hour = year.hour; + this.minute = year.minute; + this.second = year.second; + this.timeStamp = year.timeStamp; + return; + } + // If the argument is a timestamp, validate it and set the DateTime object accordingly + if (arguments[0] < 0 || arguments[0] > 315569437199) + throw new Error("Invalid timestamp: Timestamp cannot be lower than 0 or higher than 315569437199."); + this.fromTimestamp(arguments[0]); + return; + } + this.toTimestamp(year, month, day, hour, minute, second); + } + + /** + * Total days since start of timeStamp calculation (year 1) + * + * @param {number} year The year to calculate total days from. + * @returns {number} Total number of days since year 1. + */ + static getTotalDaysSinceStart(year) { + return (year - 1) * 365 + Math.floor((year - 1) / 4) - Math.floor((year - 1) / 100) + Math.floor((year - 1) / 400); + } + + /** + * Check if a given year is a leap year. + * + * @param {number} year The year to check. + * @returns {boolean} True if the year is a leap year, false otherwise + */ + static isLeapYear(year) { + return year !== 0 && year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + } + + /** + * Get the number of days in each month for a given year, considering leap years. + * + * @param {number} year Year to check + * @returns {Array} Array with number of days in each month for the given year + */ + static getDaysOfMonthFromYear(year) { + return DateTime.isLeapYear(year) ? TimeConstants.leapYearMonths : TimeConstants.standardYearMonths; + } + + /** + * Get number of days in a given year, considering leap years. + * + * @param {number} year The year + * @returns {number} Total number of days + */ + static getDaysOfYear(year) { + return DateTime.isLeapYear(year) ? 366 : 365; + } + + /** + * Converts the given date components into a timestamp. + * + * @param {number} year + * @param {number} month + * @param {number} day + * @param {number} hour + * @param {number} minute + * @param {number} second + */ + toTimestamp(year, month, day, hour, minute, second) { + if (year < 1 || year > 9999) throw new Error("Invalid year: Year must be between 1-9999."); + if (month < 1 || month > 12) throw new Error("Invalid month: Month must be between 1-12."); + const daysInMonth = DateTime.getDaysOfMonthFromYear(year); + if (day < 1 || day > daysInMonth[month - 1]) throw new Error("Invalid date: Day must be between 1-" + daysInMonth[month - 1] + "."); + + const totalDays = DateTime.getTotalDaysSinceStart(year) + daysInMonth.slice(0, month - 1).reduce((a, b) => a + b, 0) + day - 1; + const totalSeconds = totalDays * TimeConstants.secondsPerDay + hour * TimeConstants.secondsPerHour + minute * TimeConstants.secondsPerMinute + second; + + this.timeStamp = totalSeconds; + this.year = year; + this.month = month; + this.day = day; + this.hour = hour; + this.minute = minute; + this.second = second; + } + + /** + * Converts a timestamp into year, month, day, hour, minute, and second + * + * @param {number} timestamp The timestamp to convert. + */ + fromTimestamp(timestamp) { + // Initialize the year to 1 + let year = 1; + let month = 0; + let day = (timestamp / TimeConstants.secondsPerDay) | 0; + const hour = (timestamp / TimeConstants.secondsPerHour) | 0; + const minute = (timestamp / TimeConstants.secondsPerMinute) | 0; + const second = timestamp; + + // Maps the total number of days to the corresponding year and day. + while (day > DateTime.getDaysOfYear(year)) { + day -= DateTime.getDaysOfYear(year++); + } + + const daysPerMonth = DateTime.getDaysOfMonthFromYear(year); + + // Determines the month and day by subtracting the number of days in each month and incrementing the month value. + while (day >= daysPerMonth[month]) { + day -= daysPerMonth[month++]; + if (month > 11) { + month = 0; + year++; + } + } + + this.timeStamp = timestamp; + this.year = year; + this.month = month + 1; + this.day = day + 1; + this.hour = hour % 24; + this.minute = minute % 60; + this.second = second % 60; + } + + /** + * Compares this DateTime object with another DateTime object and returns the difference. + * Gives an approximate comparison only when working with higher numbers (several years), since it doesn't take leap years into account for simplicity + * + * @param {DateTime} otherDateTime The DateTime object to compare with. + * @param {boolean} getSeconds If true, returns the difference in seconds, rather than an object + * @returns {object} An object with the difference + */ + compareWith(otherDateTime, getSeconds = false) { + let diffSeconds = otherDateTime.timeStamp - this.timeStamp; + if (getSeconds) return diffSeconds; + + const sign = Math.sign(diffSeconds); + diffSeconds = Math.abs(diffSeconds); + + const years = Math.floor(diffSeconds / (TimeConstants.secondsPerDay * 365.25)); + diffSeconds -= years * TimeConstants.secondsPerDay * 365; + + const months = Math.floor(diffSeconds / (TimeConstants.secondsPerDay * 30)); + diffSeconds -= months * TimeConstants.secondsPerDay * 30; + + const days = Math.floor(diffSeconds / TimeConstants.secondsPerDay); + diffSeconds -= days * TimeConstants.secondsPerDay; + + const hours = Math.floor(diffSeconds / TimeConstants.secondsPerHour); + diffSeconds -= hours * TimeConstants.secondsPerHour; + + const minutes = Math.floor(diffSeconds / TimeConstants.secondsPerMinute); + diffSeconds -= minutes * TimeConstants.secondsPerMinute; + + const seconds = diffSeconds; + + return { + years: years * sign, + months: months * sign, + days: days * sign, + hours: hours * sign, + minutes: minutes * sign, + seconds: seconds * sign, + }; + } + + /** + * Returns the first occurrence of a specified weekday in the current month + * + * @param {number} weekDay The day of the week (1-7 for Sun-Sat) + * @returns {DateTime} The DateTime for the first occurrence of the specified weekday + */ + getFirstWeekdayOfMonth(weekDay) { + if (weekDay < 1 || weekDay > 7) throw new Error("Invalid weekDay: Must be between 1-7"); + + const date = new DateTime(this.year, this.month, 1); + return date.addDays((weekDay - date.weekDay + 7) % 7); + } + + /** + * Returns the next occurrence of a specified weekday after the current date. + * + * @param {number} weekDay The day of the week (1-7 for Sun-Sat). + * @returns {DateTime} The DateTime for the next occurrence of the specified weekday + */ + getNextWeekdayDate(weekDay) { + if (weekDay < 1 || weekDay > 7) throw new Error("Invalid weekDay: Must be between 1-7"); + + const days = ((7 + weekDay - this.weekDay - 1) % 7) + 1; + const date = new DateTime(this); + return date.addDays(days); + } + + /** + * Returns the previous occurrence of a specified weekday before the current date. + * + * @param {number} weekDay The day of the week (1-7 for Sun-Sat). + * @returns {DateTime} The DateTime for the previous occurrence of the specified weekday + */ + getPreviousWeekdayDate(weekDay) { + if (weekDay < 1 || weekDay > 7) throw new Error("Invalid weekDay: Must be between 1-7"); + const days = ((7 + weekDay - this.weekDay) % 7) - 7; + const date = new DateTime(this); + return date.addDays(days); + } + + /** + * Adds a specified number of years to the current date + * Adding a negative value (e.g. -1) subtracts the years instead + * + * @param {number} years The number of years to add. + * @returns {DateTime} + */ + addYears(years) { + if (!years) return this; + const newYear = this.year + years; + const daysInMonth = DateTime.getDaysOfMonthFromYear(newYear); + const newDay = Math.min(this.day, daysInMonth[this.month - 1]); + + this.toTimestamp(newYear, this.month, newDay, this.hour, this.minute, this.second); + return this; + } + + /** + * Adds a specified number of months to the current date + * Adding a negative value (e.g. -1) subtracts the months instead + * + * @param {number} months The number of months to add. + * @returns {DateTime} + */ + addMonths(months) { + if (!months) return this; + const addedMonths = this.month + months; + const newYear = this.year + Math.floor((addedMonths - 1) / 12); + const newMonth = ((addedMonths - 1) % 12) + 1; + const newDay = Math.min(this.day, DateTime.getDaysOfMonthFromYear(newYear)[newMonth - 1]); + + this.toTimestamp(newYear, newMonth, newDay, this.hour, this.minute, this.second); + return this; + } + + /** + * Adds a specified number of days to the current date + * Adding a negative value (e.g. -1) subtracts the days instead + * + * @param {number} days The number of days to add. + * @returns {DateTime} + */ + addDays(days) { + if (!days) return this; + this.fromTimestamp(this.timeStamp + days * TimeConstants.secondsPerDay); + return this; + } + + /** + * Adds a specified number of hours to the current date + * Adding a negative value (e.g. -1) subtracts the hours instead + * + * @param {number} hours The number of hours to add. + * @returns {DateTime} + */ + addHours(hours) { + if (!hours) return this; + this.timeStamp += hours * TimeConstants.secondsPerHour; + this.fromTimestamp(this.timeStamp); + return this; + } + + /** + * Adds a specified number of minutes to the current date + * Adding a negative value (e.g. -1) subtracts the minutes instead + * + * @param {number} minutes The number of minutes to add. + * @returns {DateTime} + */ + addMinutes(minutes) { + if (!minutes) return this; + this.timeStamp += minutes * TimeConstants.secondsPerMinute; + this.fromTimestamp(this.timeStamp); + return this; + } + + /** + * Adds a specified number of seconds to the current date + * Adding a negative value (e.g. -1) subtracts the seconds instead + * + * @param {number} seconds The number of seconds to add. + * @returns {DateTime} + */ + addSeconds(seconds) { + if (!seconds) return this; + this.timeStamp += seconds; + this.fromTimestamp(this.timeStamp); + return this; + } + + /** + * Checks if the current date is the last day of the month + * + * @returns {boolean} True if it's the last day of the month, false otherwise + */ + isLastDayOfMonth() { + const daysInMonth = DateTime.getDaysOfMonthFromYear(this.year)[this.month - 1]; + return this.day === daysInMonth; + } + + /** + * Checks if the current date is the first day of the month + * + * @returns {boolean} True if it's the first day of the month, false otherwise + */ + isFirstDayOfMonth() { + return this.day === 1; + } + + /** + * Checks if the current date is between two specified dates + * + * @param {DateTime} startDate The start date + * @param {DateTime} endDate The end date + * @returns {boolean} True if the current date is between the specified dates, false otherwise + */ + between(startDate, endDate) { + if (!(startDate instanceof DateTime && endDate instanceof DateTime)) { + throw new Error("Parameters must be instances of DateTime."); + } + + return this.timeStamp >= startDate.timeStamp && this.timeStamp <= endDate.timeStamp; + } + + /** + * Returns the weekday (1-7 for Sun-Sat) of the current object's date + * + * @returns {number} The weekday number of the current date + */ + get weekDay() { + const daysSinceStart = DateTime.getTotalDaysSinceStart(this.year + 1); + const daysInMonth = TimeConstants.standardYearMonths.slice(0, this.month - 1).reduce((a, b) => a + b, 0); + const isLeapYear = DateTime.isLeapYear(this.year) && this.month < 3; + const weekDayOffset = V.weekDayOffset !== undefined ? V.weekDayOffset : 6; + + const totalDays = daysSinceStart + daysInMonth + this.day - Number(isLeapYear) + weekDayOffset; + const weekDay = (totalDays % 7) + 1; + + return weekDay; + } + + /** + * Returns the name of the weekday of the current object's date + * + * @returns {string} The name of the weekday + */ + get weekDayName() { + return Time.daysOfWeek[this.weekDay - 1]; + } + + /** + * Returns the name of the month (e.g. "January") of the current object's date + * + * @returns {string} Name of month + */ + get monthName() { + return Time.monthNames[this.month - 1]; + } + + /** + * Returns whether the current object's date falls on a weekend (Saturday or Sunday) + * + * @returns {boolean} True if the current date is a weekend, false otherwise + */ + get weekEnd() { + return this.weekDay === 7 || this.weekDay === 1; + } + + /** + * Returns the last date of the current month + * + * @returns {number} 1-31 + */ + get lastDayOfMonth() { + const daysPerMonth = DateTime.getDaysOfMonthFromYear(this.year); + return daysPerMonth[this.month - 1]; + } + + /** + * Returns the day of the year for the current object's date + * + * @returns {number} The day of the year + */ + get yearDay() { + const daysPerMonth = DateTime.getDaysOfMonthFromYear(this.year); + return daysPerMonth.slice(0, this.month - 1).reduce((a, b) => a + b, 0) + this.day; + } + + /** + * Returns the current moon phase as a fraction, where 0 and 1 are new moon and 0.5 is full moon + * + * @returns {number} The current moon phase as a fraction + */ + get moonPhaseFraction() { + // Real new moon (in london) as a reference point + const referenceNewMoon = new DateTime(2022, 1, 2, 18, 33); + let phaseFraction = ((this.timeStamp - referenceNewMoon.timeStamp) / (TimeConstants.synodicMonth * TimeConstants.secondsPerDay)) % 1; + + // Adjust in case of negative date (date before the reference date) + phaseFraction = (phaseFraction + 1) % 1; + + // Special rounding cases - to round to a complete new-moon or full-moon more often + return phaseFraction >= 0.48 && phaseFraction <= 0.52 ? 0.5 : phaseFraction < 0.02 || phaseFraction > 0.98 ? 0 : round(phaseFraction, 2); + } + + /** + * Returns a fraction of a day (0 at 0:00 and 1 at 24:00) for the current date and time + * + * @returns {number} Fraction between 0 and 1 + */ + get fractionOfDay() { + return (this.hour * 60 + this.minute) / (24 * 60); + } + + /** + * Returns a fraction of a day starting at noon (0 at 12:00 and 0.99 at 11:59) + * + * @returns {number} Fraction between 0 and 1 + */ + get fractionOfDayFromNoon() { + return (((this.hour + 12) % 24) * 60 + this.minute) / (24 * 60); + } + + /** + * Returns 1 during noon, and 0 during midnight, interpolating between. The difference between a fraction is that + * at the dawn and dusk (6 and 18 - the factor is 0.5) + * + * @returns {number} Factor between 0 and 1 + */ + get simplifiedDayFactor() { + const timeInHours = this.hour + this.minute / 60; + return (Math.sin((Math.PI / 12) * timeInHours - Math.PI / 2) + 1) / 2; + } + + /** + * Returns a fraction of the year (0 at the start of the year and 1 at the end) for the current date + * + * @returns {number} The fraction of the year. + */ + get fractionOfYear() { + return this.yearDay / DateTime.getDaysOfYear(this.year); + } + + /** + * Calculates the seasonal factor for the current date + * Returns 1 in winter (December 21) and 0 in summer (June 21), interpolating between them. + * + * @returns {number} Factor between 0 and 1 + */ + get seasonFactor() { + const summerSolstice = new DateTime(this.year, 6, 21); + const winterSolstice = new DateTime(this.year, 12, 21); + + const previousSolstice = + this.timeStamp < summerSolstice.timeStamp + ? new DateTime(this.year - 1, 12, 21) + : this.timeStamp < winterSolstice.timeStamp + ? summerSolstice + : winterSolstice; + + const nextSolstice = + this.timeStamp < summerSolstice.timeStamp + ? summerSolstice + : this.timeStamp < winterSolstice.timeStamp + ? winterSolstice + : new DateTime(this.year + 1, 6, 21); + + const nextSolsticeFactor = nextSolstice === winterSolstice ? 1 : 0; + + const totalSecondsBetweenSolstices = nextSolstice.timeStamp - previousSolstice.timeStamp; + const secondsSinceLastSolstice = this.timeStamp - previousSolstice.timeStamp; + const factor = secondsSinceLastSolstice / totalSecondsBetweenSolstices; + + return nextSolsticeFactor === 1 ? factor : 1 - factor; + } +} +window.DateTime = DateTime; diff --git a/game/00-framework-tools/perflog.js b/game/00-framework-tools/perflog.js index 465223a7d..770ce7513 100644 --- a/game/00-framework-tools/perflog.js +++ b/game/00-framework-tools/perflog.js @@ -117,9 +117,9 @@ function niceround(x) { if (Math.floor(x) === x) return x; // Integers if (x > -1 && x < 1) { // Small number, round to 0.001 instead - return Math.round(x * 1000) / 1000; + return round(x, 3); } - return Math.round(x * 10) / 10; + return round(x, 1); } /** * Return performance report. Can be viewed by DevTools table() function. diff --git a/game/01-config/start.twee b/game/01-config/start.twee index 337f5e688..f709db46e 100644 --- a/game/01-config/start.twee +++ b/game/01-config/start.twee @@ -18,7 +18,7 @@ Degrees of Lewdity <> -본 작품에는 성적인 내용이 존재하며 미성년자에게는 부적절합니다. 작품에서 사된 모든 등장인물은 만 18세 이상입니다. 모든 행위는 합의된 역극이고, 모든 짐승은 사실 코스튬을 차려입은 사람들입니다. +This work of fiction contains content of a sexual nature and is inappropriate for minors. All characters depicted are at least 18 years of age. Everything is consensual role play, and any animals are actually people in costumes.

@@ -79,23 +79,23 @@ Degrees of Lewdity의 첫 페이지에 오신 것을 환영합니다!
<><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>
- <><><><><><><><><> + <><><><><><><><><>

<><><><><><><><> @@ -106,7 +106,7 @@ Degrees of Lewdity의 첫 페이지에 오신 것을 환영합니다! <><><><> (테스트를 위해 겨울에 시작합니다.)
- <><><><><><><><><> (크리스마스 이틀 전에 시작합니다. 테스트용.) + <><><><><><><><><> (Begins the game two days before Christmas. For testing.)
<><><><> (발렌타인 데이 이틀 전에 시작합니다. 테스트용.) diff --git a/game/01-config/sugarcubeConfig.js b/game/01-config/sugarcubeConfig.js index 8cdc5a247..93fa929d8 100644 --- a/game/01-config/sugarcubeConfig.js +++ b/game/01-config/sugarcubeConfig.js @@ -1,7 +1,7 @@ /* eslint-disable prefer-const */ Config.history.controls = false; Config.saves.slots = 9; -Config.history.maxStates = 1; +Config.history.maxStates = 5; /* LinkNumberify and images will enable or disable the feature completely */ /* debug will enable or disable the feature only for new games */ @@ -11,9 +11,9 @@ window.StartConfig = { debug: false, enableImages: true, enableLinkNumberify: true, - version: "0.4.5.3", + version: "0.4.6.1", versionName: "", - sneaky: false, + sneaky: true, }; State.prng.init(); @@ -26,7 +26,7 @@ let pageLoading = false; Config.saves.isAllowed = () => { if (tags().includes("nosave") || V.replayScene) return false; return true; -} +}; idb.footerHTML = `
@@ -42,7 +42,7 @@ idb.footerHTML = `
-
` +
`; function onLoad(save) { // some flags for version update. ideally, all updating should be done here in onLoad, but we don't live in an ideal world @@ -56,6 +56,9 @@ function onLoad(save) { // decompression should be the FIRST save modification DoLSave.decompressIfNeeded(save); + // ironman is not currently supported with idb + if (save.state.history[save.state.index].variables.ironmanmode) idb.active = false; + // cache current date before assigning it to every frame in history const date = new Date(); save.state.history.forEach(h => { @@ -483,17 +486,119 @@ Config.navigation.override = function (dest) { return "Chalets Work One Sex"; case "Chalets Work One Rape Finish": return "Chalets Work One Sex Finish"; - + case "Whitney Bully Parasite Event Submit": case "Whitney Bully Parasite Event Escape Attempt": return "Bully Parasite"; - + case "Whitney Bully Parasite Event Combat": return "Bully Parasite Fight"; case "Whitney Bully Parasite Event Combat Loss": case "Whitney Bully Parasite Event Combat Victory": return "Bully Parasite Fight Finish"; + + case "Robin Kiyoura Start": + return "Canteen Robin Whitney"; + + case "Robin Kiyoura canteen oral": + return "Canteen Robin Whitney Oral"; + + case "Robin Kiyoura canteen oral Finish": + return "Canteen Robin Whitney Oral Finish"; + + case "Robin Kiyoura Back to Robin": + return "Canteen Robin Whitney Talk"; + + case "Robin Kiyoura canteen fight": + return "Canteen Robin Whitney Fight"; + + case "Robin Kiyoura after fight": + return "Canteen Robin Whitney Fight 2"; + + case "Robin Kiyoura weak": + return "Canteen Robin Whitney Trauma"; + + case "Robin Kiyoura cuddle": + case "Robin Kiyoura Secret": + case "Robin Kiyoura Didn't want to": + case "Robin Kiyoura Enjoyed it": + return "Canteen Robin Whitney End"; + + case "Livestock Field Centaur Friendly": + case "Livestock Field Centaur Firm": + case "Livestock Field Friendly": + case "Livestock Field Firm": + return "Livestock Field Horse Intro"; + + case "Livestock Field Centaur Brush": + return "Livestock Field Horse Brush"; + + case "Livestock Field Centaur Apples": + return "Livestock Field Horse Apples"; + + case "Livestock Field Horse Help 2": + case "Livestock Field Centaur Help": + case "Livestock Field Centaur Help 2": + return "Livestock Field Horse Help"; + + case "Livestock Field Centaur Female Vagina": + case "Livestock Field Centaur Female": + case "Livestock Field Centaur Male Vagina": + case "Livestock Field Centaur Male": + case "Livestock Field Centaur Female Vagina 2": + case "Livestock Field Centaur Female 2": + case "Livestock Field Centaur Male Vagina 2": + case "Livestock Field Centaur Male 2": + case "Livestock Field Horse Lewd": + case "Livestock Field Horse Lewd Female": + case "Livestock Field Horse Lewd 2": + case "Livestock Field Horse Lewd Female 2": + case "Livestock Field Horse Continue": + return "Livestock Field Horse Deviancy"; + + case "Livestock Field Centaur Female Vagina 3": + case "Livestock Field Centaur Female 3": + case "Livestock Field Centaur Male Vagina 3": + case "Livestock Field Centaur Male 3": + case "Livestock Field Centaur Male Hand": + case "Livestock Field Centaur Male Hand Vagina": + case "Livestock Field Centaur Female Hand": + case "Livestock Field Centaur Female Hand Vagina": + case "Livestock Field Horse Lewd 3": + case "Livestock Field Horse Lewd Female 3": + case "Livestock Field Horse Lewd Hand": + case "Livestock Field Horse Lewd Female Hand": + return "Livestock Field Horse Deviancy Hand"; + + case "Livestock Field Centaur Male Mouth": + case "Livestock Field Centaur Male Mouth Vagina": + case "Livestock Field Centaur Female Mouth": + case "Livestock Field Centaur Female Mouth Vagina": + case "Livestock Field Horse Lewd Mouth": + case "Livestock Field Horse Lewd Female Mouth": + return "Livestock Field Horse Deviancy Mouth"; + + case "Livestock Field Centaur Male Seduce": + case "Livestock Field Centaur Male Sex": + case "Livestock Field Centaur Male Sex Finish": + case "Livestock Field Centaur Male Vagina Seduce": + case "Livestock Field Centaur Male Vagina Sex": + case "Livestock Field Centaur Male Vagina Sex Finish": + case "Livestock Field Centaur Female Vagina Seduce": + case "Livestock Field Centaur Female Vagina Sex": + case "Livestock Field Centaur Female Vagina Sex Finish": + case "Livestock Field Centaur Female Seduce ": + case "Livestock Field Centaur Female Sex": + case "Livestock Field Centaur Female Sex Finish": + case "Livestock Field Horse Lewd Seduce": + case "Livestock Field Horse Lewd Female Seduce": + case "Livestock Field Horse Lewd Sex": + case "Livestock Field Horse Lewd Female Sex": + case "Livestock Field Horse Lewd Sex Finish": + case "Livestock Field Horse Lewd Female Sex Finish": + return "Livestock Field Horse Deviancy Sex"; + default: return false; } diff --git a/game/03-JavaScript/02-Helpers/colour-utils.js b/game/03-JavaScript/02-Helpers/colour-utils.js index 04a86c93f..5cbc9d6c4 100644 --- a/game/03-JavaScript/02-Helpers/colour-utils.js +++ b/game/03-JavaScript/02-Helpers/colour-utils.js @@ -141,6 +141,66 @@ const ColourUtils = (() => { return hslToFilter(hsl); } + /** + * Interpolates between 2 colors, based on a factor + * + * @param {string} color1 + * @param {string} color2 + * @param {string} factor + * @returns {string} + */ + function interpolateColor(color1, color2, factor) { + factor = Math.max(0, Math.min(factor, 1)); + + // Convert hex colors to RGBA + const parseColor = color => { + const rgb = parseInt(color.slice(1, 7), 16); + const r = (rgb >> 16) & 0xff; + const g = (rgb >> 8) & 0xff; + const b = rgb & 0xff; + const a = color.length > 7 ? parseInt(color.slice(7), 16) / 255 : 1; // Normalize alpha to [0, 1] + return [r, g, b, a]; + }; + + const [r1, g1, b1, a1] = parseColor(color1); + const [r2, g2, b2, a2] = parseColor(color2); + + // Interpolate RGBA + const r = Math.round(r1 + (r2 - r1) * factor); + const g = Math.round(g1 + (g2 - g1) * factor); + const b = Math.round(b1 + (b2 - b1) * factor); + const a = Math.round((a1 + (a2 - a1) * factor) * 255); // Denormalize alpha to [0, 255] + + // Convert each component to a two-digit hexadecimal string and concatenate + let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; + + // Append alpha to hex string only if it's less than full opacity + if (a < 255) { + hex += a.toString(16).padStart(2, "0"); + } + + return hex; + } + + /** + * Interpolates between 3 colors, based on a factor + * - If factor is negative, interpolates between color1 and color2. + * - If factor is positive, interpolates between color2 and color3. + * + * @param {string} color1 + * @param {string} color2 + * @param {string} color3 + * @param {string} factor + * @returns {string} + */ + function interpolateTripleColor(color1, color2, color3, factor) { + if (factor < 0) { + return interpolateColor(color2, color1, Math.abs(factor)); + } else { + return interpolateColor(color2, color3, factor); + } + } + /* Export module */ return Object.seal({ partToFilter, @@ -152,6 +212,8 @@ const ColourUtils = (() => { rgbToHex, intToHex, invertHex, + interpolateColor, + interpolateTripleColor, }); })(); diff --git a/game/03-JavaScript/02-Helpers/macros.js b/game/03-JavaScript/02-Helpers/macros.js index 55edafbbf..7c4c441d8 100644 --- a/game/03-JavaScript/02-Helpers/macros.js +++ b/game/03-JavaScript/02-Helpers/macros.js @@ -59,26 +59,6 @@ const ErrorSystem = ((Scripting, Errors) => { }, }); - /** - * DEPRECATED: Time should no longer be able to desynchronise, so this check is unnecessary. - * - * Jimmy: checkTimeSystem macro to print a message if time desynchronises. - * Potential to place time correction code here instead of in backComp. - */ - Macro.add("checkTimeSystem", { - handler() { - if (V.time !== undefined && V.hour !== undefined && V.minute !== undefined) { - if (V.time !== V.hour * 60 + V.minute) { - const message = `$time: ${V.time} desynchronised from $hour: ${V.hour} and $minute: ${V.minute}. Total: ${V.hour * 60 + V.minute}.`; - const source = `Caught in Passage ${this.args[0]}. ${V.passage}, <>.`; - throwError(this.output, message, source); - } - } else { - console.debug(`One of the time variables is not accessible yet: ${V.passage}: ${DOL.Stack}.`); - } - }, - }); - /** * Jimmy: defer Macro, to be used to defer execution of the provided contents until after the passage has been processed. * For example, let's say you create
in a widget. And you want to use $('#myDiv') in that diff --git a/game/03-JavaScript/03-Templates/t-misc.js b/game/03-JavaScript/03-Templates/t-misc.js index c7a088040..4bc41c67d 100644 --- a/game/03-JavaScript/03-Templates/t-misc.js +++ b/game/03-JavaScript/03-Templates/t-misc.js @@ -51,7 +51,7 @@ Template.add('animal', () => "연어", "참치", "사슴", - "울새" + "oyster" ); getPostNum(T.trResult); return T.trResult;} ); @@ -108,7 +108,7 @@ Template.add("animals", () => "연어들", "참치들", "사슴들", - "울새들" + "oysters" ); getPostNum(T.trResult); return T.trResult;} ); @@ -152,3 +152,57 @@ Template.add("gwylanItem", () => /* ?sin */ Template.add("sin", () => {T.trResult = either("오만", "분노", "질투", "음욕", "탐욕", "인색", "나태"); getPostNum(T.trResult); return T.trResult;}); + +/* ?chatter1 */ +Template.add("chatter1",() => + {T.trResult = either( + "If you think about it, neither of us are really here.", + + "Hey, listen. You hear that? It's nothing.", + + "Don't knock it 'till you try it. Jazz metal just works.", + + "Winter's actually kinda hot, in a fossil kinda way.", + + "I'd let a wolf knot me, to be honest.", + + "If quizzes are quizzical, then what are tests?", + + "You. Me. That bookshelf. Library dodgeball. You in?", + + "I haven't seen a single recycling bin in this school. Unbelievable.", + + "The canteen food is actually pretty good. It's a guilty pleasure of mine.", + + "Oh, to be a caterpillar, unaware of the woes of the world.", + + "Someone said they saw a whale that had, like, tentacles, like a squid. Think squids and whales finally put their differences aside and got busy?" + ); getPostNum(T.trResult); return T.trResult;} +); + +/* ?chatter2 */ +Template.add("chatter2",() => + {T.trResult = either( + "Why am I friends with you?", + + "I told you to stop hanging out with me.", + + "What the fuck?", + + "I'm out of here.", + + "Can we please have a normal conversation for once?", + + "...What?", + + "Pretty sure you're not supposed to come to school drunk.", + + "I'm calling the police.", + + "I gotta get that restraining order. For real, this time.", + + "Can I peg you?", + + "Can you make like a tree and die for the winter?" + ); getPostNum(T.trResult); return T.trResult;} +); diff --git a/game/03-JavaScript/03-Templates/t-pronouns.js b/game/03-JavaScript/03-Templates/t-pronouns.js index 37c501d4e..6f148241b 100644 --- a/game/03-JavaScript/03-Templates/t-pronouns.js +++ b/game/03-JavaScript/03-Templates/t-pronouns.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ function getHe() { switch (V.pronoun) { case "m": diff --git a/game/03-JavaScript/04-Pregnancy/childrenStoryFunctions.js b/game/03-JavaScript/04-Pregnancy/children-story-functions.js similarity index 99% rename from game/03-JavaScript/04-Pregnancy/childrenStoryFunctions.js rename to game/03-JavaScript/04-Pregnancy/children-story-functions.js index fb5aa1582..ea00f32ba 100644 --- a/game/03-JavaScript/04-Pregnancy/childrenStoryFunctions.js +++ b/game/03-JavaScript/04-Pregnancy/children-story-functions.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ const setChildFirstWord = (childId, word, playerAbsent = false) => { if (!childId && V.childSelected) childId = V.childSelected.childId; if (!childId && !V.childSelected) return false; diff --git a/game/03-JavaScript/04-Pregnancy/parasite.js b/game/03-JavaScript/04-Pregnancy/parasite.js index 43bd2ef58..ff5c3d278 100644 --- a/game/03-JavaScript/04-Pregnancy/parasite.js +++ b/game/03-JavaScript/04-Pregnancy/parasite.js @@ -1,5 +1,3 @@ -/* eslint-disable no-undef */ - function fertiliseParasites(genital = "anus") { // Runs whenever someone ejaculates in your `genital` const pregnancy = V.sexStats[genital].pregnancy; diff --git a/game/03-JavaScript/04-Pregnancy/parentFunctions.js b/game/03-JavaScript/04-Pregnancy/parent-functions.js similarity index 99% rename from game/03-JavaScript/04-Pregnancy/parentFunctions.js rename to game/03-JavaScript/04-Pregnancy/parent-functions.js index b43f9657b..0650d3ac1 100644 --- a/game/03-JavaScript/04-Pregnancy/parentFunctions.js +++ b/game/03-JavaScript/04-Pregnancy/parent-functions.js @@ -1,5 +1,4 @@ /* eslint-disable jsdoc/require-returns-type */ -/* eslint-disable no-undef */ // Format for storing the parents of a child. const parentList = { mothers: [{ name: "pc", compressed: "none", births: 0, kids: 0, id: 0 }], diff --git a/game/03-JavaScript/04-Pregnancy/pregnancyTypes.js b/game/03-JavaScript/04-Pregnancy/pregnancy-types.js similarity index 98% rename from game/03-JavaScript/04-Pregnancy/pregnancyTypes.js rename to game/03-JavaScript/04-Pregnancy/pregnancy-types.js index 83de1ec3b..b39900c64 100644 --- a/game/03-JavaScript/04-Pregnancy/pregnancyTypes.js +++ b/game/03-JavaScript/04-Pregnancy/pregnancy-types.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ function maxParasites(genital = "anus") { switch (V.sexStats[genital].pregnancy.motherStatus) { case 1: @@ -119,6 +118,7 @@ function pregPrep({ motherObject, fatherObject, parasiteType = null, genital = n let pregnancy; let fertility = 0; let magicTattoo = 0; + let contraceptive = 0; if (!motherObject) { return [`mother object not provided`]; } else if (!["anus", "vagina"].includes(genital)) { @@ -147,6 +147,7 @@ function pregPrep({ motherObject, fatherObject, parasiteType = null, genital = n if (parasiteType && pregnancy.fetus.length >= maxParasites(genital)) return ["Player does not have room for more parasites"]; fertility += Math.clamp(V.sexStats.pills.pills["fertility booster"].doseTaken || 0, 0, Infinity); + contraceptive += Math.clamp(V.sexStats.pills.pills.contraceptive.doseTaken || 0, 0, 2); } else if (!parasiteType) { if (V.npcPregnancyDisable === "t") return ["NPC pregnancy disabled"]; @@ -165,9 +166,8 @@ function pregPrep({ motherObject, fatherObject, parasiteType = null, genital = n if (pregnancy.fetus.length && pregnancy.fetus[0] && pregnancy.fetus[0].eggTimer) return ["Hawk eggs already fertilised"]; } else if (pregnancy.type !== "parasite" && pregnancy.fetus.length) return ["NPC currently pregnant and cannot support other types"]; - if (pregnancy.pills === "fertility") { - fertility += 1; - } + if (pregnancy.pills === "fertility") fertility += 1; + if (pregnancy.pills === "contraceptive") contraceptive += 1; } else { // ToDo: Random npc pregnancy pregnancy = { @@ -189,7 +189,7 @@ function pregPrep({ motherObject, fatherObject, parasiteType = null, genital = n } } - return [pregnancy, fertility, magicTattoo]; + return [pregnancy, fertility, magicTattoo, contraceptive]; } function bodySizeCalc(bodysize) { diff --git a/game/03-JavaScript/04-Pregnancy/pregnancy.js b/game/03-JavaScript/04-Pregnancy/pregnancy.js index 79ddaf68c..734e91c2e 100644 --- a/game/03-JavaScript/04-Pregnancy/pregnancy.js +++ b/game/03-JavaScript/04-Pregnancy/pregnancy.js @@ -1,5 +1,4 @@ /* eslint-disable no-undef */ - // Should a name type for species be setup, say, human/wolf specific names function generateBabyName(name, gender, childId) { let result = ""; @@ -81,7 +80,7 @@ function fetishPregnancy({ genital = "vagina", target = null, spermOwner = null, if (["hand", "kiss"].includes(genital)) genital = target === "pc" && !V.player.vaginaExist ? "anus" : "vagina"; const motherObject = npcPregObject(target, true); - const [pregnancy, fertility, magicTattoo] = pregPrep({ motherObject, genital }); + const [pregnancy, fertility, magicTattoo, contraceptive] = pregPrep({ motherObject, genital }); // Check the cycle settings let multi = 1; @@ -108,6 +107,9 @@ function fetishPregnancy({ genital = "vagina", target = null, spermOwner = null, multi = 1 / Math.pow(4, C.npc[target].pregnancy.nonCycleRng[0]); } } + + if (!forcePregnancy && contraceptive && (random(0, 100) >= 10 || contraceptive > 1)) return "contraceptive fail"; + if (["hawk", "harpy"].includes(spermType) || target === "Great Hawk") { if (!["pc", "Great Hawk"].includes(target)) return false; if ((target === "pc" && !V.harpyEggs) || (target === "Great Hawk" && multi === 1)) return false; @@ -138,7 +140,6 @@ function fetishPregnancy({ genital = "vagina", target = null, spermOwner = null, } return false; } -window.fetishPregnancy = fetishPregnancy; /* Player pregnancy starts here */ /* V.pregnancytype === "realistic" uses this function */ @@ -163,8 +164,8 @@ function playerPregnancyAttempt(baseMulti = 1, genital = "vagina") { /* The lower basePenalty is, the easier it is for the player to get pregnant */ let basePenalty = Math.floor((100 - V.basePlayerPregnancyChance) * Math.clamp(fertilityBoost, 0.1, 1) * baseMulti); - if (V.earSlime.growth >= 100 && V.earSlime.focus === "pregnancy") basePenalty = Math.floor(basePenalty * 2); - if (V.earSlime.growth >= 100 && V.earSlime.focus === "impregnation") basePenalty = Math.floor(basePenalty / 2); + if (V.earSlime.growth >= 100 && V.earSlime.focus === "pregnancy") basePenalty = Math.floor(basePenalty / 2); + if (V.earSlime.growth >= 100 && V.earSlime.focus === "impregnation") basePenalty = Math.floor(basePenalty * 2); /* When spermArray.length - 1 is lower than basePenalty, it uses basePenalty to determin if the pregnancy should occur or not @@ -337,7 +338,7 @@ function playerEndWaterProgress() { const pregnancy = getPregnancyObject(); if (!pregnancy || !pregnancy.type || pregnancy.type === "parasite" || pregnancy.timer < pregnancy.timerEnd || V.statFreeze) { // Fixes an issue when the above "parasite" was "parasites" - if (pregnancy && (!pregnancy.type || pregnancy.type === "parasite") && pregnancy.waterBreaking) waterBreaking = false; + if (pregnancy && (!pregnancy.type || pregnancy.type === "parasite") && pregnancy.waterBreaking) pregnancy.waterBreaking = false; return null; } @@ -881,7 +882,7 @@ function recordSperm({ if (V.disableImpregnation) return false; // To be set at the start of sex scenes, unset with <> if (V.playerPregnancyHumanDisable === "t" && spermType === "human" && target === "pc") return false; // Human player pregnancy disabled if (V.playerPregnancyBeastDisable === "t" && spermType !== "human" && target === "pc") return false; // Beast player pregnancy disabled - if (V.playerPregnancyEggLayingDisable === "t" && ["hawk", "harpy"].includes(npcType)) return false; // Egg laying player pregnancy disabled + if (V.playerPregnancyEggLayingDisable === "t" && ["hawk", "harpy"].includes(spermOwner?.type)) return false; // Egg laying player pregnancy disabled if (V.npcPregnancyDisable === "t" && target !== "pc") return false; // Npc pregnancy disabled if (["realistic", "fetish"].includes(V.pregnancytype) && !["anus", "vagina"].includes(genital)) return null; @@ -908,6 +909,11 @@ function recordSperm({ } if (setup.pregnancy.infertile.includes(spermOwnerName) || setup.pregnancy.infertile.includes(target)) return null; + // Tracking for unsafe sex + if (target === "pc" || spermOwner === "pc") { + unsafeSexTracking(genital, target, spermOwnerName); + } + const forcePregnancy = (target === "pc" && ((V.vaginaaction === "forceImpregnation" && genital === "vagina") || (V.anusaction === "forceImpregnation" && genital === "anus"))) || @@ -1046,9 +1052,20 @@ function washRecordedSperm(genital, target) { } DefineMacro("washRecordedSperm", washRecordedSperm); +function unsafeSexTracking(genital, mother, father) { + console.log("unsafeSexTracking", genital, mother, father); + if (mother === "pc" && !playerIsPregnant() && (V.player.vaginaExist || canBeMPregnant() || true)) { + if (V.NPCNameList.includes(father) || father === "pc") { + V.unsafeSexTimeStat[father] = Time.date.timeStamp; + } + } else if (genital === "vagina" && V.NPCNameList.includes(mother) && !npcIsPregnant(mother)) { + V.unsafeSexTimeStat[mother] = Time.date.timeStamp; + } +} + function playerCanBreedWith(npc) { /* This function can accept either a named NPC's name, or an NPC object from either NPCList or NPCName. - * Examples: playerCanBreedWith("Kylar"), or playerCanBreedWith($NPCList[0]) or playerCanBreedWith($NPCName[$NPCNameList.indexOf("Kylar")]) + * Examples: playerCanBreedWith("Kylar"), or playerCanBreedWith($NPCList[0]) or playerCanBreedWith(C.npc.Kylar) * Returns true or false. If you give it garbage, like a totally wrong name, it'll return false, so be careful about silent failures like that. * Should be used for NPC breeding lines ONLY. */ diff --git a/game/03-JavaScript/04-Pregnancy/storyFunctions.js b/game/03-JavaScript/04-Pregnancy/story-functions.js similarity index 99% rename from game/03-JavaScript/04-Pregnancy/storyFunctions.js rename to game/03-JavaScript/04-Pregnancy/story-functions.js index 170ceda26..b903fda0c 100644 --- a/game/03-JavaScript/04-Pregnancy/storyFunctions.js +++ b/game/03-JavaScript/04-Pregnancy/story-functions.js @@ -1,5 +1,3 @@ -/* eslint-disable no-undef */ - function getPregnancyObject(mother = "pc", returnGenital = false) { let pregnancy = {}; let genital = "vagina"; @@ -78,7 +76,8 @@ function playerBellyVisible(pregnancyOnly = false) { const size = playerBellySize(pregnancyOnly); if (size <= 7) return false; if (size <= 12 && ((V.worn.upper.name !== "naked" && !V.worn.upper.type.includes("bellyShow")) || !V.worn.over_upper.type.includes("naked"))) return false; - if (size <= 17 && (V.worn.upper.type.includes("bellyHide") || !V.worn.over_upper.type.includes("naked"))) return false; + if (size <= 17 && (V.worn.upper.type.includes("bellyHide") || V.worn.lower.type.includes("bellyHide") || !V.worn.over_upper.type.includes("naked"))) + return false; return true; } @@ -650,7 +649,7 @@ function setKnowsAboutPregnancyCurrentLoaded() { } DefineMacro("setKnowsAboutPregnancyCurrentLoaded", setKnowsAboutPregnancyCurrentLoaded); -/* +/* <> - When Bailey is now aware of all the pc's current children at the orphanage, this excludes those they already know of This will set T.nowAwareOfChildren[whoNowKnows] to an array with all with birth id's so that u can use the filter below to get an array of all the children they are just now aware of Object.values(V.children).filter(child => V.babyIntros[whoNowKnows].includes(child.birthId)) diff --git a/game/03-JavaScript/IronMan.js b/game/03-JavaScript/IronMan.js index ea001d3fd..6460cae3f 100644 --- a/game/03-JavaScript/IronMan.js +++ b/game/03-JavaScript/IronMan.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ /* eslint-disable no-var */ var IronMan = (Save => { "use strict"; diff --git a/game/03-JavaScript/UI.js b/game/03-JavaScript/UI.js index 52917bc50..95183304e 100644 --- a/game/03-JavaScript/UI.js +++ b/game/03-JavaScript/UI.js @@ -33,75 +33,9 @@ function overlayMenu(elementId, type) { } window.overlayMenu = overlayMenu; -/* Sidebar swipe */ -document.addEventListener("touchstart", handleTouchStart, false); -document.addEventListener("touchmove", handleTouchMove, false); - -let xDown = null; -let yDown = null; - -function getTouches(evt) { - return ( - evt.touches || // browser API - evt.originalEvent.touches // jQuery - ); -} - -function handleTouchStart(evt) { - const firstTouch = getTouches(evt)[0]; - xDown = firstTouch.clientX; - yDown = firstTouch.clientY; -} - -function handleTouchMove(evt) { - if (!xDown || !yDown) { - return; - } - - /** - * Activate the swipe only when finger near the UI Bar. - * 50px - +/- width of unstowed UI Bar - * 280px - +/- width of unstowed UI bar - */ - if (UIBar.isStowed()) { - if (xDown > 50) { - return; - } - } else { - if (xDown > 280) { - return; - } - } - - const xUp = evt.touches[0].clientX; - const yUp = evt.touches[0].clientY; - - const xDiff = xDown - xUp; - const yDiff = yDown - yUp; - - if (Math.abs(xDiff) > Math.abs(yDiff)) { - // most significant - if (xDiff > 0) { - UIBar.stow(); // left swipe - } else { - UIBar.unstow(); // right swipe - } - } else { - if (yDiff > 0) { - // up swipe - } else { - // down swipe - } - } - // reset values - xDown = null; - yDown = null; -} - -//Links.disableNumberifyInVisibleElements.push("#passage-testing-room"); +// Links.disableNumberifyInVisibleElements.push("#passage-testing-room"); $(document).on(":passagerender", function (ev) { - if (passage() === "GiveBirth") { $(ev.content) .find("[type=checkbox]") @@ -432,9 +366,17 @@ function settingsGenericGenders(id) { if (id === "mlm" || id === "wlw" || id === "blw" || id === "blm") { switch (val) { - case 100: text = `0%${women}100%${men}이 ${attraction} 것입니다.`; break; - case 0: text = `100%${women}0%${men}이 ${attraction} 것입니다.`; break; - default: text = `${(100 - val)}%${women}${val}%${men}이 ${attraction} 것입니다.`; break; + case 100: + text = `No ${women} and all ${men} will be ${attraction}.`; + break; + case 0: + text = `All ${women} and no ${men} will be ${attraction}.`; + break; + default: + text = `${ + 100 - val + }% of ${women} and ${val}% of ${men} will be ${attraction}.`; + break; } } else { if (val === 100) { @@ -442,18 +384,24 @@ function settingsGenericGenders(id) { } else if (val === 0) { text = `모든 ${trid[id]}은 ${id === "beasts"? "암컷":"여성"}일 것입니다.`; } else if (val === 50) { - text = `${trid[id]}은 ${id === "beasts"? "수컷":"남성"}${id === "beasts"? "암컷":"여성"}으로 동등하게 나누어질 것입니다.`; + text = + id.charAt(0).toUpperCase() + + id.slice(1) + + " will be evenly split between male and female genders."; } else if (val > 50) { text = `${val}%의 ${trid[id]}은 ${id === "beasts"? "수컷":"남성"}일 것입니다.`; } else { - text = `${(100 - val)}%의 ${trid[id]}은 ${id === "beasts"? "암컷":"여성"}일 것입니다.`; + text = `${100 - val}% of ${id} will be female.`; } } - jQuery("#numberslider-value-" + slider).text("").append(text).addClass("small-description"); - }; + jQuery("#numberslider-value-" + slider) + .text("") + .append(text) + .addClass("small-description"); + }; - $(() => { + $(() => { updateText(); $("#numberslider-input-" + slider).on("input change", function (e) { updateText(); @@ -469,16 +417,24 @@ function settingsMonsterChance() { let text = null; switch (val) { - case 100: text = "짐승들은 항상 몬스터 소년과 소녀로 나올 것입니다."; break; - case 0: text = "환각 중에 허용되지 않는다면, 짐승들은 절대 몬스터 소년과 소녀로 나오지 않을 것입니다."; break; - case 50: text = "모든 짐승들 중 절반은 몬스터 소년과 소녀로 대체될 것입니다."; break; - default: text = `모든 짐승들 중 ${val}%는 몬스터 소년과 소녀로 대체될 것입니다.`; break; + case 100: + text = "Beasts will always be monster girls and boys."; + break; + case 0: + text = "Beasts will never appear as monster girls and boys, unless allowed while hallucinating."; + break; + case 50: + text = "Half of all beasts will be replaced by monster girls and boys."; + break; + default: + text = `${val}% of beasts will be replaced by monster girls and boys.`; + break; } jQuery("#numberslider-value-monsterchance").text("").append(text).addClass("small-description"); - }; + }; - $(() => { + $(() => { updateText(); $("#numberslider-input-monsterchance").on("input change", function (e) { updateText(); @@ -683,13 +639,21 @@ function updatehistorycontrols() { if (V.options.maxStates === undefined) V.options.maxStates = Config.history.maxStates; else Config.history.maxStates = V.options.maxStates; // update engine config - // option to only save active state into sessionStorage, for better performance - if (V.options.sessionHistory) Config.history.maxSessionStates = V.options.maxStates; - else Config.history.maxSessionStates = 1; - - if (V.options.maxStates === 1) jQuery("#ui-bar-history").hide(); // hide nav panel when it's useless - else { - // or unhide it otherwise + // enable fast rng re-roll on "keypad *" for debug and testing + if (V.debug || V.cheatdisable === "f" || V.testing) Links.disableRNGReload = false; + else Links.disableRNGReload = true; + + // option to reduce the number of states going into sessionStorage, for better performance + if (V.options.maxSessionStates === undefined) V.options.maxSessionStates = Config.history.maxSessionStates; + else Config.history.maxSessionStates = V.options.maxSessionStates; + + // option to still record history without showing the controls, for better debugging + if (V.options.maxStates === 1 || !V.options.historyControls || V.ironmanmode) { + // hide nav panel when it's useless or set to not be displayed + Config.history.controls = false; + jQuery("#ui-bar-history").hide(); + } else if (Config.history.maxStates > 1) { + // or unhide it otherwise, if config allows Config.history.controls = true; jQuery("#ui-bar-history").show(); } @@ -708,7 +672,7 @@ function updateOptions() { const tmpButtons = T.buttons; const tmpKey = T.key; /* numberify_enabled workaround */ T.optionsRefresh = false; - if (!State.restore()) return; // don't do anything if state couldn't be restored + if (!State.restore(true)) return; // don't do anything if state couldn't be restored V.options = optionsData; /* numberify_enabled workaround */ Links.enabled=V.options.numberify_enabled?true:false; tanned(0, "ignoreCoverage"); State.show(); diff --git a/game/03-JavaScript/base-clothing.js b/game/03-JavaScript/base-clothing.js index d7ac1223d..ace817f54 100644 --- a/game/03-JavaScript/base-clothing.js +++ b/game/03-JavaScript/base-clothing.js @@ -162,7 +162,7 @@ function getClothingCost(item, slot) { cost *= lewdCoef; if (V.passage === "School Library Shop") { - cost *= 1.4 + ((V.delinquency - 500) / 5000 + (V.NPCName[V.NPCNameList.indexOf("Sydney")].love - 50) / -500); + cost *= 1.4 + ((V.delinquency - 500) / 5000 + (C.npc.Sydney.love - 50) / -500); } return Math.round(cost); @@ -210,8 +210,8 @@ function convertNormalToOver() { "peacoat", "shadbelly coat", "puffer jacket", - "brown leather jacket", - "black leather jacket", + "leather jacket", + "punk leather jacket", "vampire jacket", ]; @@ -333,7 +333,17 @@ function convertNormalToOver() { window.convertNormalToOver = convertNormalToOver; function getVisibleClothesList() { - const visibleClothes = [V.worn.over_upper, V.worn.over_lower, V.worn.over_head, V.worn.face, V.worn.neck, V.worn.hands, V.worn.handheld, V.worn.legs, V.worn.feet]; + const visibleClothes = [ + V.worn.over_upper, + V.worn.over_lower, + V.worn.over_head, + V.worn.face, + V.worn.neck, + V.worn.hands, + V.worn.handheld, + V.worn.legs, + V.worn.feet, + ]; // over_head doesn't have 'exposed' parameter, but maybe it will some day (in which case remove check for 'naked') if (V.worn.over_head.name === "naked" || V.worn.over_head.exposed >= 2) visibleClothes.push(V.worn.head); if (V.worn.over_upper.exposed >= 2 || V.overupperwetstage >= 3) visibleClothes.push(V.worn.upper); diff --git a/game/03-JavaScript/base.js b/game/03-JavaScript/base.js index f5eb54f38..43c7883aa 100644 --- a/game/03-JavaScript/base.js +++ b/game/03-JavaScript/base.js @@ -216,27 +216,27 @@ function integrityWord(worn, slot) { T.text_output = ""; } } - let colorClass; - switch (kw) { - case "full": - colorClass = "green"; - break; - case "너덜너덜한": - colorClass = "red"; - break; - case "찢긴": - colorClass = "purple"; - break; - case "해어진": - colorClass = "teal"; - break; - default: - colorClass = ""; // default without color - } - if (T.text_output) { - T.text_output = `${T.text_output.trim()} `; - } - return T.text_output; + let colorClass; + switch (kw) { + case "full": + colorClass = "green"; + break; + case "너덜너덜한": + colorClass = "red"; + break; + case "찢긴": + colorClass = "purple"; + break; + case "해어진": + colorClass = "teal"; + break; + default: + colorClass = ""; // default without color + } + if (T.text_output) { + T.text_output = `${T.text_output.trim()} `; + } + return T.text_output; } window.integrityWord = integrityWord; DefineMacroS("integrityWord", integrityWord); @@ -306,6 +306,7 @@ function outfitChecks() { T.middleOutfit = (V.worn.lower.outfitSecondary && V.worn.lower.outfitSecondary[1] === V.worn.upper.name) || false; T.overOutfit = (V.worn.over_lower.outfitSecondary && V.worn.over_lower.outfitSecondary[1] === V.worn.over_upper.name) || false; + T.underBottoms = V.worn.lower.name === "naked" && V.worn.under_lower.type.includes("covered"); T.underNaked = V.worn.under_lower.name === "naked" && V.worn.under_upper.name === "naked"; T.middleNaked = V.worn.lower.name === "naked" && V.worn.upper.name === "naked"; T.overNaked = V.worn.over_lower.name === "naked" && V.worn.over_upper.name === "naked"; @@ -435,73 +436,40 @@ function mobBtnShow() { window.mobBtnShow = mobBtnShow; /** - * This function takes a value, and weights it by exponential curve. - * - * Value should be between 0.0 and 1.0 (use normalise to get a percentage of a max). + * Selects a random item from an array of weighted options. Each option is an array with + * two elements: the item, and its weight. + * Works similar to eventpool, but more generic and lightweight, and works with any data types. * - * An exponent of 1.0 returns 1 every time. + * Options with a higher weight have a higher chance of being chosen. * - * Exponents between 1.0 and 2.0 return a curve favoring higher results (closer to 1) - * - * An exponent of 2.0 will return a flat line distribution, and is identical to random() - * - * Exponents greater than 2.0 return a curve favoring lower results (closer to 0), reaching to 0 at infinity. - * - * For example, see: - * https://www.desmos.com/calculator/87hhrjfixi - * - * @param {number} value Value to be weighted - * @param {number} exp Exponent used to generate the curve - * @returns {number} value weighted against exponential curve + * @param {Array} options Each option is an array where the first item is a value, and the second item is its weight. + * @returns {*} The selected item + * @example + * console.log(weightedRandom(["apple", 1], ["banana", 2], ["cherry", 3])); // Relative probability for these will be: apple: 16.67%, banana: 33.33%, cherry: 50% */ -function expCurve(value, exp) { - return value ** exp / value; -} -window.expCurve = expCurve; - -/** - * This function creates a random float 0.0-1.0, weighted by exponential curve. - * - * A value of 1.0 returns 1 every time. - * - * Values between 1.0 and 2.0 return a curve favoring higher results (closer to 1) - * - * A value of 2.0 will return a flat line distribution, and is identical to random() - * - * Values greater than 2.0 return a curve favoring lower results (closer to 0), reaching to 0 at infinity. - * - * For example, see: - * https://www.desmos.com/calculator/87hhrjfixi - * - * @param {number} exp Exponent used to generate the curve - * @returns {number} random number weighted against exponential curve - */ -function randomExp(exp) { - return expCurve(State.random(), exp); -} -window.randomExp = randomExp; - -/** - * Normalises value to a decimal number 0.0-1.0, a percentage of the range specified in min and max. - * - * @param {number} value The value to be normalised - * @param {number} max The highest value of the range - * @param {number} min The lowest value of the range, default 0 - * @returns {number} Normalised value - */ -function normalise(value, max, min = 0) { - const denominator = max - min; - if (denominator === 0) { - Errors.report("[normalise]: min and max params must be different.", { value, max, min }); - return 0; +function weightedRandom(...options) { + if (!Array.isArray(options) || options.length === 0) { + throw new Error("Options must be a non-empty array."); } - if (denominator < 0) { - Errors.report("[normalise]: max param must be greater than min param.", { value, max, min }); - return 0; + let totalWeight = 0; + const processedOptions = options.map(([value, weight]) => { + if (typeof weight !== "number") { + throw new Error("Weight must be a number."); + } + totalWeight += weight; + return [value, totalWeight]; + }); + + const random = Math.random() * totalWeight; + for (const [value, cumulativeWeight] of processedOptions) { + if (cumulativeWeight >= random) { + return value; + } } - return Math.clamp((value - min) / denominator, 0, 1); + console.error("weightedRandom: Unreachable code reached. Returning " + options[0][0]); + return options[0][0]; } -window.normalise = normalise; +window.weightedRandom = weightedRandom; /** * This macro sets $rng. If the variable $rngOverride is set, $rng will always be set to that. @@ -547,13 +515,14 @@ window.nullable = nullable; * Files are all in img/misc/icon/ * Example: <> * <> does not add a trailing whitespace for formatting. + * <> will cause the icon to layer ontop of the next one */ Macro.add("icon", { handler() { if (!V.options.images) return; const name = typeof this.args[0] === "string" ? this.args[0] : "error"; const iconImg = document.createElement("img"); - iconImg.className = "icon"; + iconImg.className = "icon" + (this.args.includes("infront") ? " infront" : ""); iconImg.src = "img/misc/icon/" + name; this.output.append(iconImg); // append a whitespace for compatibility with old icon behavior diff --git a/game/03-JavaScript/bedroomPills.js b/game/03-JavaScript/bedroom-pills.js similarity index 94% rename from game/03-JavaScript/bedroomPills.js rename to game/03-JavaScript/bedroom-pills.js index 3ffe8f963..3453aab80 100644 --- a/game/03-JavaScript/bedroomPills.js +++ b/game/03-JavaScript/bedroom-pills.js @@ -56,7 +56,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/bottomReduction.png", + icon: "img/misc/icon/pillbottom.png", + frontIcon: "img/misc/icon/smalldownarrow.png", display_condition() { return this.owned() > 0 ? 1 : 0; }, @@ -91,7 +92,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/bottomGrowth.png", + icon: "img/misc/icon/pillbottom.png", + frontIcon: "img/misc/icon/smalluparrow.png", display_condition() { return this.owned() > 0 ? 1 : 0; }, @@ -126,7 +128,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/bottomBlocker.png", + icon: "img/misc/icon/pillbottom.png", + frontIcon: "img/misc/icon/smallcross.png", display_condition() { return this.owned() > 0 ? 1 : 0; }, @@ -161,7 +164,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/breastReduction.png", + icon: "img/misc/icon/pillbreast.png", + frontIcon: "img/misc/icon/smalldownarrow.png", display_condition() { return this.owned() > 0 ? 1 : 0; }, @@ -196,7 +200,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/breastGrowth.png", + icon: "img/misc/icon/pillbreast.png", + frontIcon: "img/misc/icon/smalluparrow.png", display_condition() { return this.owned() > 0 ? 1 : 0; }, @@ -212,7 +217,7 @@ setup.pills = [ { name: "breast blocker", description: - "선택적 에스트로겐 수용체 조절자 (SERM)가, 유방의 성장에 관여하는 단백질 수용체를 차단합니다; 269mg의 테트라오제알포스티글이 함유되어 있습니다.", + "A selective oestrogen receptor modulator (SERM), blocking the protein receptors responsible for breast growth; supplemented by 269mg of Tetraozealpostigyl.", onTakeMessage: "당신은 당신의 가슴 크기를 유지하기 위해 약을 먹는다. 당신은 이 약이 광고처럼 효과적이기를 바란다.", warning_label: '주의사항: 이 약의 투약중 어떤 부작용도 보고되어 있지 않습니다. 24시간 내에 1정 이상 투약하는 것은 효과가 없을 것입니다', @@ -231,7 +236,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/breastBlocker.png", + icon: "img/misc/icon/pillbreast.png", + frontIcon: "img/misc/icon/smallcross.png", display_condition() { return this.owned() > 0 ? 1 : 0; }, @@ -266,7 +272,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/penisReduction.png", + icon: "img/misc/icon/pillpenis.png", + frontIcon: "img/misc/icon/smalldownarrow.png", display_condition() { return V.player.penisExist && this.owned() > 0 ? 1 : 0; }, @@ -301,7 +308,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/penisGrowth.png", + icon: "img/misc/icon/pillpenis.png", + frontIcon: "img/misc/icon/smalluparrow.png", display_condition() { return V.player.penisExist && this.owned() > 0 ? 1 : 0; }, @@ -336,7 +344,8 @@ setup.pills = [ overdose() { return V.sexStats.pills["pills"][this.name].overdose; }, - icon: "img/misc/icon/penisBlocker.png", + icon: "img/misc/icon/pillpenis.png", + frontIcon: "img/misc/icon/smallcross.png", display_condition() { return V.player.penisExist && this.owned() > 0 ? 1 : 0; }, @@ -352,7 +361,7 @@ setup.pills = [ { name: "fertility booster", description: - "1정 당 50mg의 에스트로겐 유사물질인, 클로미펜 시트르산염을 함유하고 있습니다. 이 약은 또한 시상하부에 작용하여 배란을 촉진하는 데 필요한 호르몬을 분비시킵니다. 대부분의 경우 효과적으로 난소에서 난자생성을 유도시킵니다.", + "Each pill contains 50mg of clomiphene citrate, a structural analogue of oestrogens. It also acts on your hypothalamus which secretes the hormones necessary to trigger ovulation. In some cases effectively inducing your ovary to release eggs.", onTakeMessage: "당신은 당신의 생식력을 늘리기 위해 약을 먹는다. 당신은 이 약이 광고처럼 효과적이기를 바란다.", warning_label: "주의사항: 적정용량을 복용하는 동안, 임신 초기와 비슷한 증상을 포함한 약한 부작용이 일어날 수 있습니다. 일일 최대용량을 초과해 복용할 경우 심각한 합병증을 일으킬 수 있습니다. 확신이 들지 않을 시에는, 의사와 상의하십시오.", @@ -395,7 +404,7 @@ setup.pills = [ { name: "contraceptive", description: - "24mg의 에치닐에스트라디올(합성 에스트로겐)과 31mg의 합성 프로게스토겐이 복합된 에스트로프로게스트라피츠가 거의-완벽한 피임 효과를 발휘합니다.", + "Estroprogestatifs associating 24mg of ethinylestradiol(synthetic oestrogen) and 31mg of a synthetic progestin for a near-perfect contraceptive effect.", onTakeMessage: "당신은 피임약을 먹는다. 당신은 이 약이 광고처럼 효과적이기를 바란다.", warning_label: "주의사항: 적정용량을 복용하는 동안 약한 부작용이 일어날 수 있습니다. 일일 최대용량을 초과해 복용할 경우 심각한 합병증을 일으킬 수 있습니다. 확신이 들지 않을 시에는, 의사와 상의하십시오.", @@ -609,18 +618,12 @@ function addElementToGrid(item) { hpiGridContainer.innerHTML + `
-
-
` + - item_koname + - (item.autoTake() === true ? ` [자동]` : "") + - `
-
` + - item.owned() + - `
+
${item.frontIcon ? `` : ""}
+
+ ${itemName} + ${item.autoTake() === true ? ` [Auto]` : ""} +
+
${item.owned()}
`; hpiGridContainer.lastElementChild.setAttribute("onclick", "window.onHomePillItemClick(" + "`" + item.name + "`" + ")"); @@ -633,40 +636,21 @@ function onHomePillItemClick(itemName) { document.getElementById("homeDescPillContainer").style.display = "grid"; for (const item of setup.pills) { if (item.name === itemName) { - document.getElementById("hpi_desc").outerHTML = - `
` + - item.description + - ` -
` + - item.warning_label + - `
+ document.getElementById("hpi_desc").outerHTML = ` +
+ ${item.description} +
${item.warning_label}
-
- `; +
`; window.initPillContextButtons(item); document.getElementById("hpi_desc_img").innerHTML = - `` + + (item.frontIcon + ? `` + : "") + + `` + `
`; window.addIndicators(item); } diff --git a/game/03-JavaScript/canvasmodel-editor.js b/game/03-JavaScript/canvasmodel-editor.js index 264479acf..f03117164 100644 --- a/game/03-JavaScript/canvasmodel-editor.js +++ b/game/03-JavaScript/canvasmodel-editor.js @@ -614,7 +614,7 @@ Macro.add("canvasModelEditor", { ); return; } - const bodyWritings = ["", ...Object.keys(setup.bodywriting)]; + const bodyWritings = ["", ...Object.keys()]; const hairColourOptions = [...Object.keys(setup.colours.hair_map), "custom"]; const xhairColourOptions = ["", ...Object.keys(setup.colours.hair_map), "custom"]; @@ -789,7 +789,7 @@ Macro.add("canvasModelEditor", { "short air vents", "side-pinned", "ruffled", - "bowl" + "bowl", ]), selectOption("hair_fringe_length", ["short", "shoulder", "chest", "navel", "thighs", "feet"]), selectOption("brows_colour", xhairColourOptions), @@ -822,6 +822,7 @@ Macro.add("canvasModelEditor", { booleanOption("hood_down"), booleanOption("alt_position"), booleanOption("alt_position_neck"), + booleanOption("alt_position_face"), booleanOption("alt_sleeve"), selectOption("facewear_layer", ["front", "back"]), ]), diff --git a/game/base-system/clamp.js b/game/03-JavaScript/clamp.js similarity index 80% rename from game/base-system/clamp.js rename to game/03-JavaScript/clamp.js index e2e9b255e..0cc577300 100644 --- a/game/base-system/clamp.js +++ b/game/03-JavaScript/clamp.js @@ -1,5 +1,5 @@ function clampAlexRelations() { - const alex = V.NPCName[V.NPCNameList.indexOf("Alex")]; + const alex = C.npc.Alex; alex.love = Math.clamp(alex.love, -50, 100); alex.dom = Math.clamp(alex.dom, -50, 100); alex.lust = Math.clamp(alex.lust, 0, 100); diff --git a/game/03-JavaScript/clothing-shop-v2.js b/game/03-JavaScript/clothing-shop-v2.js index ff7eb6320..221b92264 100644 --- a/game/03-JavaScript/clothing-shop-v2.js +++ b/game/03-JavaScript/clothing-shop-v2.js @@ -616,7 +616,8 @@ function filterShopGroup(clothingItems) { return true; } else { T.itemGroups[item.shopGroup].push(item.index); - }; + return false; + } }); } window.filterShopGroup = filterShopGroup; diff --git a/game/03-JavaScript/debugMenu.js b/game/03-JavaScript/debug-menu.js similarity index 99% rename from game/03-JavaScript/debugMenu.js rename to game/03-JavaScript/debug-menu.js index c113a2444..ed80afa49 100644 --- a/game/03-JavaScript/debugMenu.js +++ b/game/03-JavaScript/debug-menu.js @@ -1,6 +1,5 @@ /* eslint-disable eqeqeq */ /* eslint-disable no-eval */ -/* eslint-disable no-undef */ /* A standard function to reference to avoid declaring an anonymous function repeatedly. */ const stayOnPassageFn = function () { return V.passage; @@ -613,7 +612,7 @@ setup.debugMenu.eventList = { widgets: [ `<>`, `<>`, - `<>`, + `<>`, `<>`, `<>`, `<>`, diff --git a/game/03-JavaScript/EventDebug.js b/game/03-JavaScript/event-debug.js similarity index 100% rename from game/03-JavaScript/EventDebug.js rename to game/03-JavaScript/event-debug.js diff --git a/game/03-JavaScript/external/object-assign-deep.js b/game/03-JavaScript/external/object-assign-deep.js deleted file mode 100644 index 998458a71..000000000 --- a/game/03-JavaScript/external/object-assign-deep.js +++ /dev/null @@ -1,124 +0,0 @@ -// https://raw.githubusercontent.com/saikojosh/Object-Assign-Deep/master/objectAssignDeep.js - -/* - * OBJECT ASSIGN DEEP - * Allows deep cloning of plain objects that contain primitives, nested plain objects, or nested plain arrays. - */ - -/* - * A unified way of returning a string that describes the type of the given variable. - */ -function getTypeOf(input) { - if (input === null) { - return "null"; - } else if (typeof input === "undefined") { - return "undefined"; - } else if (typeof input === "object") { - return Array.isArray(input) ? "array" : "object"; - } - - return typeof input; -} - -/* - * Branching logic which calls the correct function to clone the given value base on its type. - */ -function cloneValue(value) { - // The value is an object so lets clone it. - if (getTypeOf(value) === "object") { - return quickCloneObject(value); - } - - // The value is an array so lets clone it. - else if (getTypeOf(value) === "array") { - return quickCloneArray(value); - } - - // Any other value can just be copied. - return value; -} - -/* - * Enumerates the given array and returns a new array, with each of its values cloned (i.e. references broken). - */ -function quickCloneArray(input) { - return input.map(cloneValue); -} - -/* - * Enumerates the properties of the given object (ignoring the prototype chain) and returns a new object, with each of - * its values cloned (i.e. references broken). - */ -function quickCloneObject(input) { - const output = {}; - for (const key in input) { - if (!Object.hasOwn(input, key)) { - continue; - } - output[key] = cloneValue(input[key]); - } - return output; -} - -/* - * Does the actual deep merging. - */ -function executeDeepMerge(target, _objects = [], _options = {}) { - const options = { - arrayBehaviour: _options.arrayBehaviour || "replace", // Can be "merge" or "replace". - }; - - // Ensure we have actual objects for each. - const objects = _objects.map(object => object || {}); - const output = target || {}; - - // Enumerate the objects and their keys. - for (let oindex = 0; oindex < objects.length; oindex++) { - const object = objects[oindex]; - const keys = Object.keys(object); - - for (let kindex = 0; kindex < keys.length; kindex++) { - const key = keys[kindex]; - const value = object[key]; - const type = getTypeOf(value); - const existingValueType = getTypeOf(output[key]); - - if (type === "object") { - if (existingValueType !== "undefined") { - const existingValue = existingValueType === "object" ? output[key] : {}; - output[key] = executeDeepMerge( - {}, - [existingValue, quickCloneObject(value)], - options - ); - } else { - output[key] = quickCloneObject(value); - } - } else if (type === "array") { - if (existingValueType === "array") { - const newValue = quickCloneArray(value); - output[key] = - options.arrayBehaviour === "merge" - ? output[key].concat(newValue) - : newValue; - } else { - output[key] = quickCloneArray(value); - } - } else { - output[key] = value; - } - } - } - - return output; -} - -/* - * Merge all the supplied objects into the target object, breaking all references, including those of nested objects - * and arrays, and even objects nested inside arrays. Like Object.assign(), the first parameter is mutated. - * Properties in later objects will always overwrite. - */ -function objectAssignDeep(target, ...objects) { - return executeDeepMerge(target, objects); -} -window.objectAssignDeep = objectAssignDeep; diff --git a/game/03-JavaScript/eyesRelated.js b/game/03-JavaScript/eyes-related.js similarity index 98% rename from game/03-JavaScript/eyesRelated.js rename to game/03-JavaScript/eyes-related.js index 1af8dfcca..66356a2ea 100644 --- a/game/03-JavaScript/eyesRelated.js +++ b/game/03-JavaScript/eyes-related.js @@ -224,7 +224,8 @@ window.defineCustomEyeColourStyle = function () { const colourArray = normalEyes[side].includes("cat_tf_stage") ? V.custom_eyecolours : setup.colours.eyes; for (const colour of colourArray) { /* this does this for left and right */ - if (colour.variable === normalEyes[side]) T[varSideStyle] = "filter: " + window.colorNameTranslate(window.colorNamer(colour.canvasfilter.blend), "hue"); + if (colour.variable === normalEyes[side]) + T[varSideStyle] = "filter: " + window.colorNameTranslate(window.colorNamer(colour.canvasfilter.blend), "hue"); } } } diff --git a/game/03-JavaScript/ingame.js b/game/03-JavaScript/ingame.js index d0743add6..b18aee89e 100644 --- a/game/03-JavaScript/ingame.js +++ b/game/03-JavaScript/ingame.js @@ -1,5 +1,3 @@ -/* eslint-disable prettier/prettier */ -/* eslint-disable no-undef */ function mapMove(moveTo) { const currentPassage = V.passage; const destinationTable = []; @@ -34,14 +32,6 @@ function shopClothingFilterToggleTrait(trait) { } window.shopClothingFilterToggleTrait = shopClothingFilterToggleTrait; -function shopClothingFilterSortOnDescription(traitOne, traitTwo) { - const descriptionOne = Wikifier.wikifyEval(`<>`).textContent.trim(); - const descriptionTwo = Wikifier.wikifyEval(`<>`).textContent.trim(); - - return descriptionOne > descriptionTwo; -} -window.shopClothingFilterSortOnDescription = shopClothingFilterSortOnDescription; - function toggleAllHairTraitsFilter() { const chboxes = $("#hairContainerTraits input:not(:checked)"); if (chboxes.length > 0) chboxes.click(); @@ -53,7 +43,7 @@ window.toggleAllHairTraitsFilter = toggleAllHairTraitsFilter; function wikifier(widget, ...args) { if (widget == null) return document.createDocumentFragment(); return Wikifier.wikifyEval("<<" + widget + (args.length ? " " + args.join(" ") : "") + ">>"); -}; +} window.wikifier = wikifier; function actionsreplace(bodypart) { @@ -76,6 +66,7 @@ function actionsreplace(bodypart) { } window.actionsreplace = actionsreplace; +// prettier-ignore const combatActionColours = { Default: { brat: [ @@ -330,11 +321,12 @@ DefineMacroS("combatDefaults", combatDefaults); * * @param {string} skillname Simple skill name, "anus" "hand" "feet" etc. * @param {number} targetid The targetted NPC's id. + * @param {number} npc The chosen NPC's fullDescription, used solely for check that determine if encounters remain consensual * @param {number} basedifficulty Difficulty of the check, default 1000. * @param {number} multiplier Multiplier on enemy arousal, default 100. * @returns {boolean} */ -function combatSkillCheck(skillname, targetid = 0, basedifficulty = 1000, multiplier = 100) { +function combatSkillCheck(skillname, targetid = 0, npc = "", basedifficulty = 1000, multiplier = 100) { const skill = currentSkillValue(skillname + "skill"); const rng = V.rng * 10; const arousalfactor = V.enemyarousalmax / (V.enemyarousal + 1); @@ -343,6 +335,8 @@ function combatSkillCheck(skillname, targetid = 0, basedifficulty = 1000, multip if (arousalfactor * multiplier + skill + trust + rng >= basedifficulty + anger) { return true; + } else if (["Alex", "Robin"].includes(npc) || (npc === "Sydney" && !V.loveDrunk) || (npc === "Great Hawk" && V.syndromebird) || V.consensualGuaranteed) { + return true; } else { return false; } @@ -353,7 +347,7 @@ function hairdressersReset() { $(() => $("#hairDressers").on("change", ".macro-listbox, .macro-radiobutton, .macro-checkbox", function (e) { Wikifier.wikifyEval("<><><>"); - Wikifier.wikifyEval("<>낼 요금: £<><>< .passage\">>"); + Wikifier.wikifyEval('<>To pay: £<><>< .passage">>'); }) ); } @@ -363,7 +357,7 @@ function hairdressersResetAlt() { $(() => $("#hairDressersSydney").on("click", ".macro-cycle", function (e) { Wikifier.wikifyEval("<><><>"); - Wikifier.wikifyEval("<>낼 요금: £<><>< .passage\">>"); + Wikifier.wikifyEval('<>To pay: £<><>< .passage">>'); }) ); } @@ -400,19 +394,6 @@ function cheatPregnancyNPCReset() { } DefineMacro("cheatPregnancyNPCReset", cheatPregnancyNPCReset); -/** - * Checks if x is equal or higher than min and lower or equal to max - * - * @param {number} x - * @param {any} min - * @param {any} max - * @returns {boolean} - */ -function between(x, min, max) { - return typeof x === "number" && x >= min && x <= max; -} -window.between = between; - function featsPointsMenuReset() { jQuery(document).on("change", "#listbox--upgradenameid", () => { Wikifier.wikifyEval("<>"); @@ -460,38 +441,6 @@ function ordinalSuffixOf(i) { } window.ordinalSuffixOf = ordinalSuffixOf; -function lerp(percent, start, end) { - return Math.clamp(start + (end - start) * percent, start, end); -} -window.lerp = lerp; - -function inverseLerp(value, start, end) { - return Math.clamp((value - start) / (end - start), 0, 1); -} -window.inverseLerp = inverseLerp; - -function formatDecimals(value, decimalPlaces) { - return Number(Math.round(parseFloat(value + "e" + decimalPlaces)) + "e-" + decimalPlaces); -} -window.formatDecimals = formatDecimals; - -function nCr(n, r) { - // https://stackoverflow.com/questions/11809502/which-is-better-way-to-calculate-ncr - if (r > n - r) { - // because C(n, r) == C(n, n - r) - r = n - r; - } - - let ans = 1; - for (let i = 1; i <= r; ++i) { - ans *= n - r + i; - ans /= i; - } - - return ans; -} -window.nCr = nCr; - /** * Given there are {deckCount} cards in the deck and {markedCount} of them have been marked by the player, * calculates the chance that the player will see at least {atLeast} number of marked cards (from the top of the deck), @@ -558,6 +507,7 @@ function calculateMarkedChance(deckCount, markedCount, depth, atLeast, doLog = f window.calculateMarkedChance = calculateMarkedChance; function shuffle(o) { + // prettier-ignore for ( let j, x, i = o.length; i; @@ -617,7 +567,7 @@ function getRobinLocation() { } } else if (V.halloween === 1 && between(Time.hour, 16, 18) && Time.monthDay === 31) { T.robin_location = "halloween"; - } else if ((Time.isWeekEnd()) && between(Time.hour, 9, 16) && C.npc.Robin.trauma < 80) { + } else if (Time.isWeekEnd() && between(Time.hour, 9, 16) && C.npc.Robin.trauma < 80) { T.robin_location = Time.season === "winter" ? "park" : "beach"; } else if (V.englishPlay === "ongoing" && V.englishPlayDays === 0 && Time.hour >= 17 && Time.hour < 21) { T.robin_location = "englishPlay"; @@ -643,11 +593,11 @@ window.setRobinLocationOverride = setRobinLocationOverride; function getRobinCrossdressingStatus(crossdressLevel) { // Note returns 2 if Robin is crossdressing or 0 if not comfortable enough at that location // Traumatised Robin will not crossdress. - if (V.NPCName[V.NPCNameList.indexOf("Robin")].init !== 1) { + if (C.npc.Robin.init !== 1) { return; } T.robin_cd = 0; - if (V.NPCName[V.NPCNameList.indexOf("Robin")].trauma >= 40) { + if (C.npc.Robin.trauma >= 40) { return; } switch (getRobinLocation()) { @@ -672,22 +622,24 @@ function getRobinCrossdressingStatus(crossdressLevel) { } window.getRobinCrossdressingStatus = getRobinCrossdressingStatus; -/* +/* TEMPORARY - remove once obsolete Temporary function until location framework is in place - to detect if a NPC is in the park Uses same checks as other Park NPC checks */ function isInPark(name) { - switch(name.toLowerCase()) { + switch (name.toLowerCase()) { case "kylar": - return V.NPCName[V.NPCNameList.indexOf("Kylar")].state === "active" - && !["rain", "snow"].includes(V.weather) + // prettier-ignore + return C.npc.Kylar.state === "active" + && !["rain", "snow"].includes(V.weather) && Time.dayState === "day" && V.kylarwatched !== 1; case "robin": return getRobinLocation() === "park"; case "whitney": - return ["active", "rescued"].includes(V.NPCName[V.NPCNameList.indexOf("Whitney")].state) - && V.NPCName[V.NPCNameList.indexOf("Whitney")].init === 1 && ["snow", "rain"].includes(V.weather) + // prettier-ignore + return ["active", "rescued"].includes(C.npc.Whitney.state) + && C.npc.Whitney.init === 1 && ["snow", "rain"].includes(V.weather) && Time.dayState === "day" && !Time.schoolTime && V.daily.whitney.park === undefined && V.pillory_tenant.special.name !== "Whitney"; default: @@ -883,7 +835,7 @@ window.DefaultActions = { }; function selectWardrobe(targetLocation = V.wardrobe_location) { - return (!targetLocation || targetLocation === "wardrobe" || !V.wardrobes[targetLocation]) ? V.wardrobe : V.wardrobes[targetLocation]; + return !targetLocation || targetLocation === "wardrobe" || !V.wardrobes[targetLocation] ? V.wardrobe : V.wardrobes[targetLocation]; } window.selectWardrobe = selectWardrobe; @@ -1015,6 +967,7 @@ function clothesReturnLocation(item, type) { if (!V.multipleWardrobes) return "wardrobe"; const isolated = ["asylum", "prison"]; let lastTaken = item.lastTaken; + // prettier-ignore if ( !lastTaken || (V.multipleWardrobes !== "all" && !isolated.includes(lastTaken)) || @@ -1100,7 +1053,11 @@ function isConnectedToHood(slot) { } if ( V.worn[slot].hoodposition && - (V.worn[slot].hoodposition === "down" || (V.worn[slot].hoodposition === "up" && V.worn[slot].outfitPrimary.head !== "broken" && V.worn[slot].outfitPrimary.head !== "split" && V.worn.head.hood === 1)) + (V.worn[slot].hoodposition === "down" || + (V.worn[slot].hoodposition === "up" && + V.worn[slot].outfitPrimary.head !== "broken" && + V.worn[slot].outfitPrimary.head !== "split" && + V.worn.head.hood === 1)) ) { return true; } @@ -1127,7 +1084,11 @@ function clothesIndex(slot, itemToIndex) { let matches = setup.clothes[slot].filter(item => item.variable === itemToIndex.variable); if (matches.length === 0) { /* try to find and item that had its variable changed */ - matches = setup.clothes[slot].filter(item => Array.isArray(item.oldVariable) && item.oldVariable.find(oldVariableItem => oldVariableItem.name === itemToIndex.name && oldVariableItem.variable === itemToIndex.variable)); + matches = setup.clothes[slot].filter( + item => + Array.isArray(item.oldVariable) && + item.oldVariable.find(oldVariableItem => oldVariableItem.name === itemToIndex.name && oldVariableItem.variable === itemToIndex.variable) + ); oldVariable = true; } if (matches.length === 1) { @@ -1140,17 +1101,18 @@ function clothesIndex(slot, itemToIndex) { itemToIndex.variable = recovery.variable; itemToIndex.set = recovery.set; itemToIndex.iconFile = recovery.iconFile; - if(recovery.outfitPrimary) { + if (recovery.outfitPrimary) { Object.entries(recovery.outfitPrimary).forEach(([key, value]) => { - if(itemToIndex.outfitPrimary && (itemToIndex.outfitPrimary[key] === "broken" || itemToIndex.outfitPrimary[key] === "split")){ + if (itemToIndex.outfitPrimary && (itemToIndex.outfitPrimary[key] === "broken" || itemToIndex.outfitPrimary[key] === "split")) { // Do Nothing } else { itemToIndex.outfitPrimary[key] = value; } - }) + }); itemToIndex.outfitPrimary = recovery.outfitPrimary; } - if(recovery.outfitSecondary && itemToIndex.outfitSecondary[1] !== "broken" && itemToIndex.outfitSecondary[1] !== "split") itemToIndex.outfitSecondary[1] = recovery.outfitSecondary[1]; + if (recovery.outfitSecondary && itemToIndex.outfitSecondary[1] !== "broken" && itemToIndex.outfitSecondary[1] !== "split") + itemToIndex.outfitSecondary[1] = recovery.outfitSecondary[1]; } console.log(`attempting to recover the mismatch, new index is '${recovery.index}'`); return recovery.index; @@ -1176,8 +1138,10 @@ function currentSkillValue(skill, disableModifiers = 0) { // Prevents infinate loops, any call to `currentSkillValue` in this function should be written like 'currentSkillValue("skillName", disableModifiers + 1)' if (disableModifiers >= 2) return result; if ( + // prettier-ignore [ - "skulduggery", "physique", "danceskill", "swimmingskill", "athletics", "willpower", "tending", "science", "maths", "english", "history", "housekeeping"].includes(skill) && + "skulduggery", "physique", "danceskill", "swimmingskill", "athletics", "willpower", "tending", "science", "maths", "english", "history", "housekeeping" + ].includes(skill) && V.moorLuck > 0 ) { result = Math.floor(result * (1 + V.moorLuck / 100)); @@ -1193,10 +1157,13 @@ function currentSkillValue(skill, disableModifiers = 0) { case 2: T.pregnancyModifier = 60; break; - case 3: case 4: case 5: + case 3: + case 4: + case 5: T.pregnancyModifier = 78; break; - case 6: case 7: + case 6: + case 7: T.pregnancyModifier = 96; break; default: @@ -1287,42 +1254,45 @@ function currentSkillValue(skill, disableModifiers = 0) { if (V.worn.head.type.includes("maid")) { result = Math.floor(result * 1.05); } + if (V.worn.handheld.type.includes("maid")) { + result = Math.floor(result * 1.05); + } break; case "vaginalskill": if (V.earSlime.growth > 100) { if (V.earSlime.focus === "pregnancy") { - result = Math.floor(result * (1 + ((V.earSlime.growth - 100) / 500))); + result = Math.floor(result * (1 + (V.earSlime.growth - 100) / 500)); } else if (V.earSlime.focus === "impregnation") { - result = Math.floor(result * (1 - ((V.earSlime.growth - 100) / 400))); + result = Math.floor(result * (1 - (V.earSlime.growth - 100) / 400)); } } if (playerHeatMinArousal()) { - result = Math.floor(result * (1 + (Math.clamp(playerHeatMinArousal(), 0, 4000) / 20000))); + result = Math.floor(result * (1 + Math.clamp(playerHeatMinArousal(), 0, 4000) / 20000)); } break; case "penileskill": if (V.earSlime.growth > 100) { if (V.earSlime.focus === "impregnation") { - result = Math.floor(result * (1 + ((V.earSlime.growth - 100) / 500))); + result = Math.floor(result * (1 + (V.earSlime.growth - 100) / 500)); } else if (V.earSlime.focus === "pregnancy") { - result = Math.floor(result * (1 - ((V.earSlime.growth - 100) / 400))); + result = Math.floor(result * (1 - (V.earSlime.growth - 100) / 400)); } } if (playerRutMinArousal()) { - result = Math.floor(result * (1 + (Math.clamp(playerRutMinArousal(), 0, 4000) / 20000))); + result = Math.floor(result * (1 + Math.clamp(playerRutMinArousal(), 0, 4000) / 20000)); } break; case "analskill": if (V.earSlime.growth > 100 && !V.player.vaginaExist && V.earSlime.focus === "pregnancy") { - result = Math.floor(result * (1 + ((V.earSlime.growth - 100) / 500))); + result = Math.floor(result * (1 + (V.earSlime.growth - 100) / 500)); } if (playerHeatMinArousal() && canBeMPregnant()) { - result = Math.floor(result * (1 + (Math.clamp(playerHeatMinArousal(), 0, 4000) / 20000))); + result = Math.floor(result * (1 + Math.clamp(playerHeatMinArousal(), 0, 4000) / 20000)); } break; case "seductionskill": if (V.earSlime.growth > 50 && !V.earSlime.defyCooldown) { - result = Math.floor(result * (1 + ((V.earSlime.growth - 50) / 600))); + result = Math.floor(result * (1 + (V.earSlime.growth - 50) / 600)); } break; } @@ -1337,18 +1307,20 @@ window.playerIsPenetrated = playerIsPenetrated; /** * Overloads: + * * (minutes) * getTimeString(hours, minutes) * Examples: + * * getTimeString(20) returns "0:20" * getTimeString(1,5) returns "1:05". * * @param {...any} args */ function getTimeString(...args) { - if(args[0] == null) return; + if (args[0] == null) return; const hours = args[1] != null ? args[0] : 0; - const minutes = Math.max(args[1] != null ? args[1] : args[0], 0) + (hours * 60); + const minutes = Math.max(args[1] != null ? args[1] : args[0], 0) + hours * 60; return Math.clamp(Math.trunc(minutes / 60), 0, 23) + ":" + ("0" + Math.trunc(minutes % 60)).slice(-2); } window.getTimeString = getTimeString; @@ -1431,7 +1403,7 @@ function npcClothes(npc, type) { window.npcClothes = npcClothes; function waterproofCheck(clothing) { - return clothing.type.includesAny("swim", "stealthy", "rainproof"); + return clothing.type.includesAny("swim", "stealthy", "rainproof", "waterproof"); } window.waterproofCheck = waterproofCheck; @@ -1458,7 +1430,8 @@ function getMoonState() { let moonstate = 0; T.todaysMoonState = getTodaysMoonState(); - if (Time.nightState === T.todaysMoonState) { // if the current time of night matches the time a blood moon will happen, set moonstate + if (Time.nightState === T.todaysMoonState) { + // if the current time of night matches the time a blood moon will happen, set moonstate moonstate = T.todaysMoonState; } // V.moonstate = moonstate; //commenting this out to make sure this function doesn't modify save variables @@ -1513,6 +1486,7 @@ function checkTFparts() { } window.checkTFparts = checkTFparts; +// prettier-ignore function getSexesFromRandomGroup() { if (maleChance() <= 0) { /* Only females. */ if (V.dgchance <= 0) return SexTypes.ALL_FEMALES; /* All females, no dickgirls. Always vaginal. */ @@ -1640,7 +1614,7 @@ function getHalloweenCostume() { return "gothic"; } else if (upper.name === "nun's habit" && lower.name === "nun's habit skirt") { return "nun"; - } else if (upper.name.includes("maid") && lower.name.includes("maid")) { + } else if (upper.type.includes("maid") && lower.type.includes("maid")) { return "maid"; } else if (upper.name.includes("christmas") && lower.name.includes("christmas")) { return "christmas"; @@ -1654,7 +1628,10 @@ function getHalloweenCostume() { return "monk"; } else if (upper.name === "padded football shirt" && lower.name === "football shorts") { return "football"; - } else if (upper.name === "belly dancer's top" && lower.name === "belly dancer's bottoms") { + } else if ( + (upper.name === "belly dancer's top" && lower.name === "belly dancer's bottoms") || + (upper.name === "harem vest" && lower.name === "harem pants") + ) { return "belly dancer"; } else if (V.worn.head.name === "cowboy hat" && lower.name === "cowboy chaps" && V.worn.feet.name === "cowboy boots") { return "cowboy"; @@ -1671,7 +1648,7 @@ function getHalloweenCostume() { } else if (upper.name === "futuristic bodysuit" && lower.name === "futuristic bodysuit pants") { return "futuresuit"; } else if (upper.name.includes("nurse") && lower.name.includes("nurse")) { - return "nurse"; + return "nurse"; } else if (face.name === "eyepatch") { return "eyepatch"; } else if (face.name === "medical eyepatch") { @@ -1681,7 +1658,7 @@ function getHalloweenCostume() { } else if (upper.name === "rag top" && lower.name === "rag skirt") { return "rags"; - /* Transformations */ + /* Transformations */ } else if (T.tf.angelHalo && T.tf.angelWings) { return "angel TF"; } else if (T.tf.wolfEars && T.tf.wolfTail) { @@ -1699,7 +1676,7 @@ function getHalloweenCostume() { } else if (T.tf.foxEars && T.tf.foxTail) { return "fox TF"; - /* Misc outcomes */ + /* Misc outcomes */ } else if ( V.worn.upper.type.includes("costume") || V.worn.lower.type.includes("costume") || @@ -1917,90 +1894,79 @@ function dailyConvert() { } window.dailyConvert = dailyConvert; -function convertHairLengthToStage(hair, length){ - if (!hair || !length) - throw new Error(`Hair AND Length must be provided to be converted: ${hair} / ${length}`); +function convertHairLengthToStage(hair, length) { + if (!hair || !length) throw new Error(`Hair AND Length must be provided to be converted: ${hair} / ${length}`); if (hair === "fringe") { - if (length >= 900) - return "feet"; - else if (length >= 700) - return "thighs"; - else if (length >= 600) - return "navel"; - else if (length >= 400) - return "chest"; - else if (length >= 200) - return "shoulder"; - else - return "short"; - } - else if (hair === "sides") { - if (length >= 900) - return "feet"; - else if (length >= 700) - return "thighs"; - else if (length >= 600) - return "navel"; - else if (length >= 400) - return "chest"; - else if (length >= 200) - return "shoulder"; - else - return "short"; + if (length >= 900) return "feet"; + else if (length >= 700) return "thighs"; + else if (length >= 600) return "navel"; + else if (length >= 400) return "chest"; + else if (length >= 200) return "shoulder"; + else return "short"; + } else if (hair === "sides") { + if (length >= 900) return "feet"; + else if (length >= 700) return "thighs"; + else if (length >= 600) return "navel"; + else if (length >= 400) return "chest"; + else if (length >= 200) return "shoulder"; + else return "short"; } } window.convertHairLengthToStage = convertHairLengthToStage; -function calculateSemenReleased(){ - if(T.deniedOrgasm) return 0; +function calculateSemenReleased() { + if (T.deniedOrgasm) return 0; let released = 30; - released += (V.semen_volume / 30); + released += V.semen_volume / 30; - if(V.femaleclimax === 1) released /= 30; - if(V.orgasmtrait >= 1) released *= 2.5; - if(V.cow >= 6) released *= 2; + if (V.femaleclimax === 1) released /= 30; + if (V.orgasmtrait >= 1) released *= 2.5; + if (V.cow >= 6) released *= 2; /* if the player doesn't have enough semen, set $_semen_released to whatever they have left */ - if(V.semen_amount < released) released = V.semen_amount; - if(parseFloat(released.toFixed(1)) === 0 && V.semen_amount < 0.1) V.semen_amount = 0; // Prevents really low floating numbers + if (V.semen_amount < released) released = V.semen_amount; + if (parseFloat(released.toFixed(1)) === 0 && V.semen_amount < 0.1) V.semen_amount = 0; // Prevents really low floating numbers return parseFloat(released.toFixed(1)); } window.calculateSemenReleased = calculateSemenReleased; -function npcSemenMod(penisSize){ - switch(penisSize) { - case 4: return "large"; - case 1: return "tiny"; - default: return ""; +function npcSemenMod(penisSize) { + switch (penisSize) { + case 4: + return "large"; + case 1: + return "tiny"; + default: + return ""; } } window.npcSemenMod = npcSemenMod; -function maleChance(override){ +function maleChance(override) { if (V.maleChanceSplit === "f") return V.malechance; const appearence = override || V.player.gender_appearance; if (appearence === "m") return V.maleChanceMale; if (appearence === "f") return V.maleChanceFemale; - return 50; + return 50; } window.maleChance = maleChance; // gender of the npc, rng (between 1 and 100) of their generation -function attractedToBothChance(gender, rng){ +function attractedToBothChance(gender, rng) { if (gender === "m") return maleChance("m") >= rng && maleChance("f") >= rng; return maleChance("m") < rng && maleChance("f") < rng; } window.attractedToBothChance = attractedToBothChance; -function beastMaleChance(override){ +function beastMaleChance(override) { if (V.beastMaleChanceSplit === "f") return V.beastmalechance; const appearence = override || V.player.gender_appearance; if (appearence === "m") return V.beastMaleChanceMale; if (appearence === "f") return V.beastMaleChanceFemale; - return 50; + return 50; } window.beastMaleChance = beastMaleChance; @@ -2008,7 +1974,7 @@ const crimeSum = (prop, ...crimeTypes) => { if (crimeTypes.length === 0) { crimeTypes = Object.keys(setup.crimeNames); } - + return crimeTypes.reduce((result, crimeType) => result + V.crime[crimeType][prop], 0); }; @@ -2026,7 +1992,7 @@ window.crimeSumCountHistory = (...args) => crimeSum("countHistory", ...args); */ function onBrowserTabClose(event) { event.preventDefault(); - event.returnValue = 'Are you sure you want to leave?'; // the string here isn't important, it's mostly not considered by the browser. + event.returnValue = "Are you sure you want to leave?"; // the string here isn't important, it's mostly not considered by the browser. } /** @@ -2034,18 +2000,17 @@ function onBrowserTabClose(event) { * * @returns {void} */ -function toggleConfirmDialogUponTabClose(){ +function toggleConfirmDialogUponTabClose() { if (V.options.confirmDialogUponTabClose === true) { - window.addEventListener('beforeunload', onBrowserTabClose); - } - else if (V.options.confirmDialogUponTabClose === false) { - window.removeEventListener('beforeunload', onBrowserTabClose); + window.addEventListener("beforeunload", onBrowserTabClose); + } else if (V.options.confirmDialogUponTabClose === false) { + window.removeEventListener("beforeunload", onBrowserTabClose); } } window.toggleConfirmDialogUponTabClose = toggleConfirmDialogUponTabClose; -function numberOfEarSlime(){ +function numberOfEarSlime() { let result = 0; if (V.parasite.left_ear.name === "slime") result++; if (V.parasite.right_ear.name === "slime") result++; @@ -2053,10 +2018,44 @@ function numberOfEarSlime(){ } window.numberOfEarSlime = numberOfEarSlime; -function earSlimeMakingMundaneRequests(){ +function earSlimeMakingMundaneRequests() { if (!numberOfEarSlime()) return false; // First rape requests - if (V.earSlime.growth + (V.earSlime.promiscuity * 10) >= 80) return false; + if (V.earSlime.growth + V.earSlime.promiscuity * 10 >= 80) return false; return true; } window.earSlimeMakingMundaneRequests = earSlimeMakingMundaneRequests; + +function minArousal() { + let result = playerHeatMinArousal() + playerRutMinArousal(); + + result += Object.values(V.worn).reduce((prev, curr) => { + if (curr.type.includes("fetish")) return prev + 150; + return prev; + }, 0); + + return Math.clamp(result, 0, 5000); +} +window.minArousal = minArousal; + +function minPain() { + let result = 0; + + if (V.lactating && V.breastfeedingdisable === "f" && V.milkFullPain > 200) { + result += Math.ceil((V.milkFullPain - 200) / 5); + if (!V.daily.milkFullPainMessage) { + V.milkFullPainMessage = 1; + V.effectsmessage = 1; + } + } + + if ( + V.earSlime.defyCooldown && + (V.worn.genitals.name === "chastity parasite" || V.parasite.penis.name === "parasite" || V.parasite.clit.name === "parasite") + ) { + result += 25; + } + + return Math.clamp(result, 0, 50); +} +window.minPain = minPain; diff --git a/game/03-JavaScript/jQuery/tooltip.js b/game/03-JavaScript/jQuery/tooltip.js new file mode 100644 index 000000000..3484d1e3e --- /dev/null +++ b/game/03-JavaScript/jQuery/tooltip.js @@ -0,0 +1,293 @@ +/* eslint-disable no-new */ + +/* + The main purpose for this jQuery plugin was to enable tooltips for dynamically created elements, by using jQuery + However, I also added a [tooltip] attribute to be used with an html element, which is more flexible than the old tooltip. + + Example usage: (jquery) + jqueryElement.tooltip({ + message: "Here is a tooltip", + delay: 200, + position: "cursor", + }); + + Enable or disable it: + jqueryElement.tooltip("enable"); + jqueryElement.tooltip("disable"); + + Change message to an already existing tooltip: + jqueryElement.tooltip({ message: "New message" }); + + + Example usage: (html) +
+ Content here +
+ + Example usage (with added span for separate styles for the tooltip - and sugarcube variable) +
+ Pepper sprays +
+ + Example usage (with customised settings): +
+ Pepper sprays +
+ + + --------------------------------------------------- + Styling: (tooltip.css) + .tooltip-popup - The container for the tooltip + .tooltip-header - An optional title property + .tooltip-body - The tooltip text + - Anchor styling can be changed with the property "anchorStyle" (anchor = the object to hover over to display the tooltip) + + Settings: + title: A bigger title text - default null + message: The actual tooltip content + anchorStyle: Optional css class for the anchor + position: Position of the tooltip. Options: cursor, top, bottom, left, right, bottomRight, bottomLeft, topRight, topLeft + cursor: Cursor styling when hovering over the anchor + delay: Optional delay - default 150ms) + width: Optional width of the tooltip. If set to null, it will resize itself based on the content + maxWidth: Optional max width of the tooltip. When it reaches this width, text will wrap to the next row +*/ + +const tooltipRegistry = []; + +/* Clears tooltips on passage start - in case a tooltip is displayed during passage change */ +$(document).on(":passageinit", () => { + tooltipRegistry.forEach(function (tooltipElement) { + $(tooltipElement).trigger("mouseleave.tooltip"); + }); + tooltipRegistry.splice(0, tooltipRegistry.length); +}); + +$(document).on(":passageend", () => { + initializeTooltips(); +}); + +/* + This is basically a failsafe for the shop (and other places with <>) + If a popup is displayed while a <> widget is called, we remove it here +*/ +function initializeTooltips() { + $(".tooltip-popup").remove(); + $(() => { + $("[tooltip]").each(function () { + const message = $("
"); + new Wikifier(message, $(this).attr("tooltip")); + + // Default attribute settings + const defaultSettings = { + title: "", + message, + anchorStyle: null, + position: "cursor", + cursor: "help", + delay: 150, + width: null, + maxWidth: 450, + }; + + /* + Extracts the attributes that are prefixed with "tooltip", in order to customise the tooltips from html + Any of the above settings can be customised + */ + $.each(this.attributes, function () { + if (!this.name.startsWith("tooltip-")) return; + if (!Object.hasOwn(defaultSettings, this.name.substring(8))) return; + + const key = this.name.substring(8); + if (isNaN(this.value)) return; + defaultSettings[key] = parseFloat(this.value); + }); + + $(this).tooltip(defaultSettings); + }); + }); +} +window.initializeTooltips = initializeTooltips; + +/* + Extends jQuery to allow custom tooltips for any jQuery objects +*/ +$.fn.tooltip = function (options = {}) { + const initializeSettings = () => { + const existingSettings = this.data("tooltip-settings"); + if (existingSettings) { + $.extend(existingSettings, options); + return existingSettings; + } + + const defaults = { + title: "", + message: "", + delay: 150, + position: "cursor", + cursor: "help", + anchorStyle: null, + width: null, + maxWidth: null, + }; + return $.extend({}, defaults, options); + }; + + const show = function () { + const settings = initializeSettings.call(this); + this.data("tooltip-settings", settings); + const $this = $(this); + const disabled = $this.data("tooltip-disabled"); + if (disabled) return; + + let tooltip = $this.data("tooltip-instance"); + + if (settings.position.toLowerCase() === "cursor") { + $this.on("mousemove.tooltip", function (event) { + $this.data("cursorPosition", { x: event.pageX, y: event.pageY }); + updatePosition.call($this, tooltip); + }); + } + + // Optionally delay the tooltip, if a delay is set + clearTimeout($this.data("tooltip-timeout")); + const timeout = setTimeout(() => { + if (!$.contains(document, $this[0])) return; + tooltip = $("
").addClass("tooltip-popup"); + const header = $("
").addClass("tooltip-header").html(settings.title); + const body = $("
").addClass("tooltip-body").html(settings.message); + tooltip.append(header, body); + if (settings.width) tooltip.css("width", settings.width); + if (settings.maxWidth) tooltip.css("maxWidth", settings.maxWidth); + $("body").append(tooltip); + $this.data("tooltip-instance", tooltip); + updatePosition.call($this, tooltip); + }, settings.delay); + $this.data("tooltip-timeout", timeout); + + // Handler to update the tooltip position + const resizeHandler = () => { + if (settings.position.toLowerCase() !== "cursor") { + updatePosition.call($(this), tooltip); + } + }; + $(this).data("resizeHandler", resizeHandler); + $(window).on("resize", resizeHandler); + tooltipRegistry.push(this); + }; + + const hide = function () { + const settings = this.data("tooltip-settings"); + const $this = $(this); + const tooltip = $this.data("tooltip-instance"); + clearTimeout($this.data("tooltip-timeout")); + if (tooltip) { + tooltip.remove(); + $this.removeData("tooltip-instance"); + const index = tooltipRegistry.indexOf(this); + if (index > -1) { + tooltipRegistry.splice(index, 1); + } + } + if (settings.position.toLowerCase() === "cursor") { + $this.off("mousemove.tooltip"); + } + + const resizeHandler = $(this).data("resizeHandler"); + if (resizeHandler) { + $(window).off("resize", resizeHandler); + } + }; + + const updateTooltip = function () { + const $this = $(this); + const settings = $this.data("tooltip-settings"); + const tooltip = $this.data("tooltip-instance"); + if (settings && tooltip) { + tooltip.find(".tooltip-header").html(settings.title); + tooltip.find(".tooltip-body").html(settings.message); + updatePosition.call($this, tooltip); + } + }; + + const updatePosition = function (tooltipInstance) { + if (!tooltipInstance) return; + + const distance = 3; + const offset = this.offset(); + const { width, height } = this.get(0).getBoundingClientRect(); + const cursorPosition = this.data("cursorPosition") || {}; + const offsetX = 15; + const offsetY = 15; + const settings = $(this).data("tooltip-settings"); + + const setPosition = (left, top) => { + tooltipInstance.css("transform", `translate(${left}px, ${top}px)`); + }; + + switch (settings.position.toLowerCase()) { + case "cursor": + setPosition(cursorPosition.x + offsetX, cursorPosition.y + offsetY); + break; + case "top": + setPosition(offset.left + width / 2 - tooltipInstance.outerWidth() / 2, offset.top - tooltipInstance.outerHeight() - distance); + break; + case "bottom": + setPosition(offset.left + width / 2 - tooltipInstance.outerWidth() / 2, offset.top + height + distance); + break; + case "left": + setPosition(offset.left - tooltipInstance.outerWidth() - distance, offset.top + height / 2 - tooltipInstance.outerHeight() / 2); + break; + case "right": + setPosition(offset.left + width + distance, offset.top + height / 2 - tooltipInstance.outerHeight() / 2); + break; + case "bottomRight": + setPosition(offset.left + width + distance, offset.top + height + distance); + break; + case "bottomLeft": + setPosition(offset.left - tooltipInstance.outerWidth() - distance, offset.top + height + distance); + break; + case "topRight": + setPosition(offset.left + width + distance, offset.top - tooltipInstance.outerHeight() - distance); + break; + case "topLeft": + setPosition(offset.left - tooltipInstance.outerWidth() - distance, offset.top - tooltipInstance.outerHeight() - distance); + break; + } + }; + + // Enable or Disable tooltip + if (options === "disable" || options === "enable") { + this.each(function () { + const $this = $(this); + $this.data("tooltip-disabled", options === "disable"); + + if (options === "disable") { + $this.trigger("mouseleave.tooltip"); + } else if ($this.is(":hover")) { + $this.trigger("mouseenter.tooltip"); + updateTooltip.call($this); + } + }); + return this; + } + + // Event Handlers + this.on("mouseenter.tooltip", function () { + show.call($(this)); + }).on("mouseleave.tooltip", function () { + hide.call($(this)); + }); + + // Main logic + this.each(function () { + const $this = $(this); + const settings = initializeSettings.call($this); + $this.data("tooltip-settings", settings); + + if (settings.cursor) $this.css("cursor", settings.cursor); + if (settings.anchorStyle) $this.addClass(settings.anchorStyle); + }); + + return this; +}; diff --git a/game/03-JavaScript/link-override.js b/game/03-JavaScript/link-override.js index 78ce2ac83..98f74382c 100644 --- a/game/03-JavaScript/link-override.js +++ b/game/03-JavaScript/link-override.js @@ -128,7 +128,7 @@ Wikifier.Parser.add({ const setFn = Object.hasOwn(markup, "setter") ? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter)) : null; // Debug view setup. - const output = (Config.debug ? new DebugView(w.output, "link-markup", "[[link]]", w.source.slice(w.matchStart, w.nextMatch)) : w).output; + const output = (Config.debug ? new DebugView(w.output, "link-markup", "[[Link]]", w.source.slice(w.matchStart, w.nextMatch)) : w).output; if (markup.forceInternal || !Wikifier.isExternalLink(link)) { /* Wikifier.createInternalLink(output, link, text, setFn); */ diff --git a/game/03-JavaScript/math.js b/game/03-JavaScript/math.js new file mode 100644 index 000000000..e6e8a2a36 --- /dev/null +++ b/game/03-JavaScript/math.js @@ -0,0 +1,175 @@ +/** + * Interpolates between two objects, obj1 and obj2, based on a given factor + * If a property is a number and exists in both objects, it interpolates the values + * The result includes all properties from both objects + * Non-numeric properties and properties not shared between objects are copied as is + * + * @param {object} obj1 The first object + * @param {object} obj2 The second object + * @param {number} factor The interpolation factor (0 to 1). + * @returns {object} A new object with interpolated number values and other properties from both obj1 and obj2 + */ +function interpolateObject(obj1, obj2, factor) { + const result = {}; + + for (const key in obj1) { + if (Object.hasOwn(obj1, key)) { + if (typeof obj1[key] === "number" && Object.hasOwn(obj2, key) && typeof obj2[key] === "number") { + result[key] = interpolate(obj1[key], obj2[key], factor); + } else if (typeof obj1[key] === "object" && Object.hasOwn(obj2, key) && typeof obj2[key] === "object") { + result[key] = interpolateObject(obj1[key], obj2[key], factor); + } else { + result[key] = obj1[key]; + } + } + } + + for (const key in obj2) { + if (Object.hasOwn(obj2, key) && !Object.hasOwn(result, key)) { + result[key] = obj2[key]; + } + } + return result; +} + +window.interpolateObject = interpolateObject; + +/** + * Linearly interpolates between two values based on a given factor + * + * @param {number} value1 + * @param {number} value2 + * @param {number} factor Interpolation factor (0 to 1) + * @returns {number} + */ +function interpolate(value1, value2, factor) { + return value1 + (value2 - value1) * factor; +} +window.interpolate = interpolate; + +function lerp(percent, start, end) { + return Math.clamp(start + (end - start) * percent, start, end); +} +window.lerp = lerp; + +function inverseLerp(value, start, end) { + return Math.clamp((value - start) / (end - start), 0, 1); +} +window.inverseLerp = inverseLerp; + +function formatDecimals(value, decimalPlaces) { + return Number(Math.round(parseFloat(value + "e" + decimalPlaces)) + "e-" + decimalPlaces); +} +window.formatDecimals = formatDecimals; + +function nCr(n, r) { + // https://stackoverflow.com/questions/11809502/which-is-better-way-to-calculate-ncr + if (r > n - r) { + // because C(n, r) == C(n, n - r) + r = n - r; + } + + let ans = 1; + for (let i = 1; i <= r; ++i) { + ans *= n - r + i; + ans /= i; + } + + return ans; +} +window.nCr = nCr; + +/** + * Checks if x is equal or higher than min and lower or equal to max + * + * @param {number} x + * @param {any} min + * @param {any} max + * @returns {boolean} + */ +function between(x, min, max) { + return typeof x === "number" && x >= min && x <= max; +} +window.between = between; + +/** + * This function takes a value, and weights it by exponential curve. + * + * Value should be between 0.0 and 1.0 (use normalise to get a percentage of a max). + * + * An exponent of 1.0 returns 1 every time. + * + * Exponents between 1.0 and 2.0 return a curve favoring higher results (closer to 1) + * + * An exponent of 2.0 will return a flat line distribution, and is identical to random() + * + * Exponents greater than 2.0 return a curve favoring lower results (closer to 0), reaching to 0 at infinity. + * + * For example, see: + * https://www.desmos.com/calculator/87hhrjfixi + * + * @param {number} value Value to be weighted + * @param {number} exp Exponent used to generate the curve + * @returns {number} value weighted against exponential curve + */ +function expCurve(value, exp) { + return value ** exp / value; +} +window.expCurve = expCurve; + +/** + * This function creates a random float 0.0-1.0, weighted by exponential curve. + * + * A value of 1.0 returns 1 every time. + * + * Values between 1.0 and 2.0 return a curve favoring higher results (closer to 1) + * + * A value of 2.0 will return a flat line distribution, and is identical to random() + * + * Values greater than 2.0 return a curve favoring lower results (closer to 0), reaching to 0 at infinity. + * + * For example, see: + * https://www.desmos.com/calculator/87hhrjfixi + * + * @param {number} exp Exponent used to generate the curve + * @returns {number} random number weighted against exponential curve + */ +function randomExp(exp) { + return expCurve(State.random(), exp); +} +window.randomExp = randomExp; + +/** + * Normalises value to a decimal number 0.0-1.0, a percentage of the range specified in min and max. + * + * @param {number} value The value to be normalised + * @param {number} max The highest value of the range + * @param {number} min The lowest value of the range, default 0 + * @returns {number} Normalised value + */ +function normalise(value, max, min = 0) { + const denominator = max - min; + if (denominator === 0) { + Errors.report("[normalise]: min and max params must be different.", { value, max, min }); + return 0; + } + if (denominator < 0) { + Errors.report("[normalise]: max param must be greater than min param.", { value, max, min }); + return 0; + } + return Math.clamp((value - min) / denominator, 0, 1); +} +window.normalise = normalise; + +/** + * Returns a rounded number, with number of decimals based on the second parameter + * + * @param {number} number + * @param {number} decimals + * @returns {number} new number + */ +function round(number, decimals) { + const multiplier = 10 ** decimals; + return Math.round(number * multiplier) / multiplier; +} +window.round = round; diff --git a/game/03-JavaScript/named-npc.js b/game/03-JavaScript/named-npc.js index 16b04408d..32d3baccc 100644 --- a/game/03-JavaScript/named-npc.js +++ b/game/03-JavaScript/named-npc.js @@ -32,7 +32,7 @@ function statusCheck(name) { window.statusCheck = statusCheck; function sydneyStatusCheck() { - const sydney = V.NPCName[V.NPCNameList.indexOf("Sydney")]; + const sydney = C.npc.Sydney; if (sydney.purity >= 50 && sydney.lust >= 60) T.sydneyStatus = "pureLust"; else if (sydney.corruption >= 10 && sydney.lust >= 20) T.sydneyStatus = "corruptLust"; diff --git a/game/03-JavaScript/new-version-check.js b/game/03-JavaScript/new-version-check.js index 51390e20b..63c14cb6a 100644 --- a/game/03-JavaScript/new-version-check.js +++ b/game/03-JavaScript/new-version-check.js @@ -1,4 +1,6 @@ window.checkNewVersion = function () { + // Not working as of current, completely disabled for now + if (!window.testCheckNewVersion) return; const apiKey = "AIzaSyCu0F1pUOocM6R0wJ4tmug6SPTX19JRVQc"; // Google API key, restricted to Blogspot const dolBlogId = "7051760000701686365"; // Blogspot BlogID @@ -15,6 +17,8 @@ window.checkNewVersion = function () { }; function handleVersionResponse(response) { + // Not working as of current, completely disabled for now + if (!window.testCheckNewVersion) return; const titleRE = /Degrees of Lewdity\.*? version (\d+\.\d+(?:\.\d+(?:\.\d+)?)?)/; let version, postDate; @@ -51,3 +55,32 @@ function handleVersionResponse(response) { } } } + +/* +Deprecated widget +<> +
+ New version is available! Degrees of Lewdity $newVersionData.version was released + <= 24 ? $newVersionData.days + " days" : $newVersionData.hours + " hours">> ago. + +
+ UPDATE! + + + +
+
+ < { linkifyDivs('#new-version-notification'); })>> +<
> +*/ diff --git a/game/03-JavaScript/npcCompressor.js b/game/03-JavaScript/npc-compressor.js similarity index 100% rename from game/03-JavaScript/npcCompressor.js rename to game/03-JavaScript/npc-compressor.js diff --git a/game/03-JavaScript/save.js b/game/03-JavaScript/save.js index d2a21e197..71995a4ce 100644 --- a/game/03-JavaScript/save.js +++ b/game/03-JavaScript/save.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ const DoLSave = ((Story, Save) => { "use strict"; @@ -645,7 +644,11 @@ const importSettingsData = function (data) { // eslint-disable-next-line no-var for (let j = 0; j < listKey.length; j++) { // Overwrite to allow for "none" default value in the start passage to allow for rng to decide - if (V.passage === "Start" && ["pronoun", "gender"].includes(listKey[j]) && S.npc[V.NPCNameList[i]][listKey[j]] === "none") { + if ( + V.passage === "Start" && + ["pronoun", "gender", "skincolour"].includes(listKey[j]) && + S.npc[V.NPCNameList[i]][listKey[j]] === "none" + ) { V.NPCName[i][listKey[j]] = S.npc[V.NPCNameList[i]][listKey[j]]; } else if (validateValue(listObject[listKey[j]], S.npc[V.NPCNameList[i]][listKey[j]])) { V.NPCName[i][listKey[j]] = S.npc[V.NPCNameList[i]][listKey[j]]; @@ -764,7 +767,7 @@ function exportSettings(data, type) { S.npc[V.NPCNameList[i]] = {}; for (let j = 0; j < listKey.length; j++) { // Overwrite to allow for "none" default value in the start passage to allow for rng to decide - if (V.passage === "Start" && ["pronoun", "gender"].includes(listKey[j]) && V.NPCName[i][listKey[j]] === "none") { + if (V.passage === "Start" && ["pronoun", "gender", "skincolour"].includes(listKey[j]) && V.NPCName[i][listKey[j]] === "none") { S.npc[V.NPCNameList[i]][listKey[j]] = V.NPCName[i][listKey[j]]; } else if (validateValue(listObject[listKey[j]], V.NPCName[i][listKey[j]])) { S.npc[V.NPCNameList[i]][listKey[j]] = V.NPCName[i][listKey[j]]; @@ -1179,6 +1182,8 @@ function settingsObjects(type) { lightCombat: { min: 0, max: 1, decimals: 2, displayName: "Combat light:" }, lightTFColor: { min: 0, max: 1, decimals: 2, displayName: "Angel/Devil TF colour components:" }, maxStates: { min: 1, max: 20, decimals: 0, displayName: "History depth:" }, + maxSessionStates: { min: 1, max: 20, decimals: 0, displayName: "Session history depth:" }, + historyControls: { bool: true, displayName: "Show history controls:" }, newWardrobeStyle: { bool: true, displayName: "Use the new wardrobe style:" }, useNarrowMarket: { bool: true, displayName: "Use 'narrow screen' version of market inventory:" }, skipStatisticsConfirmation: { bool: true, displayName: "Skip confirmation when viewing extra stats:" }, @@ -1231,6 +1236,11 @@ function settingsObjects(type) { result = { pronoun: { strings: ["m", "f"], displayName: "Pronoun: ", textMap: { none: "N/A", m: "Male", f: "Female" } }, gender: { strings: ["m", "f"], displayName: "Genitalia: ", textMap: { none: "N/A", m: "Penis", f: "Vagina" } }, + skincolour: { + strings: ["white", "black", "ghost"], + displayName: "Skin colour: ", + textMap: { none: "N/A", white: "Pale", black: "Dark", ghost: "Ghostly Pale" }, + }, penissize: { min: 0, max: 4, decimals: 0, displayName: "Penis size: ", textMap: { 0: "N/A", 1: "Tiny", 2: "Average", 3: "Thick", 4: "Huge" } }, breastsize: { min: 0, diff --git a/game/03-JavaScript/sextoys.js b/game/03-JavaScript/sextoys.js index ff4414bf2..bcc5a17ab 100644 --- a/game/03-JavaScript/sextoys.js +++ b/game/03-JavaScript/sextoys.js @@ -76,3 +76,62 @@ function playerHasButtPlug() { // V.worn.butt_plug.worn is just as a safeguard for now } window.playerHasButtPlug = playerHasButtPlug; + +function existsInSexToyInventory(name, colour) { + if (V.player.inventory.sextoys[name] && V.player.inventory.sextoys[name].length > 0) { + if (!colour || colour === "any") { + return true; + } + for (const item of V.player.inventory.sextoys[name]) { + if (item.colour === colour) { + return true; + } + } + } + return false; +} +window.existsInSexToyInventory = existsInSexToyInventory; + +function sexToysInventoryDelete(name, colour) { + if (!existsInSexToyInventory(name, colour)) { + console.log("Item does not exist in the inventory."); + return; + } + if (colour && colour !== "any") { + let foundIndex = -1; + // Find the index of the first item with the specified name and colour + for (let i = 0; i < V.player.inventory.sextoys[name].length; i++) { + if (V.player.inventory.sextoys[name][i].colour === colour) { + foundIndex = i; + break; + } + } + if (foundIndex === -1) { + console.log(`Item ${name} ${colour} does not exist in the inventory.`); + return; + } + const item = V.player.inventory.sextoys[name][foundIndex]; + if (item.worn && item.type.includes("anal")) { + delete V.worn.butt_plug; + } + if (item.worn && item.type.includes("strap-on")) { + V.worn.under_lower = clone(setup.clothes.under_lower[0]); + setLowerVisibility(true); + } + V.player.inventory.sextoys[name].splice(foundIndex, 1); + console.log(`Deleted ${name} ${colour} from inventory.`); + return; + } + // If no specific colour is specified or colour is 'any', then remove the first item with the specified name + const item = V.player.inventory.sextoys[name][0]; + if (item.worn && item.type.includes("anal")) { + delete V.worn.butt_plug; + } + if (item.worn && item.type.includes("strap-on")) { + V.worn.under_lower = clone(setup.clothes.under_lower[0]); + setLowerVisibility(true); + } + V.player.inventory.sextoys[name].splice(0, 1); + console.log(`Deleted ${name} from inventory.`); +} +window.sexToysInventoryDelete = sexToysInventoryDelete; diff --git a/game/base-system/time/timeMacros.js b/game/03-JavaScript/time-macros.js similarity index 82% rename from game/base-system/time/timeMacros.js rename to game/03-JavaScript/time-macros.js index 47f17c69c..40473adce 100644 --- a/game/base-system/time/timeMacros.js +++ b/game/03-JavaScript/time-macros.js @@ -1,7 +1,6 @@ -/* eslint-disable no-undef */ function timeAfterXHours(hours) { const date = new DateTime(Time.date); - date.addSeconds(hours * Time.secondsPerHour); + date.addSeconds(hours * TimeConstants.secondsPerHour); return ampm(date.hour, date.minute); } DefineMacroS("timeAfterXHours", timeAfterXHours); @@ -33,9 +32,9 @@ Macro.add("advancetohour", { }); function passTimeUntil(hour, minute = 0) { - const currentSeconds = Time.hour * Time.secondsPerHour + Time.minute * Time.secondsPerMinute; - const targetSeconds = hour * Time.secondsPerHour + minute * Time.secondsPerMinute; - const secondsToPass = (targetSeconds - currentSeconds + Time.secondsPerDay) % Time.secondsPerDay; + const currentSeconds = Time.hour * TimeConstants.secondsPerHour + Time.minute * TimeConstants.secondsPerMinute; + const targetSeconds = hour * TimeConstants.secondsPerHour + minute * TimeConstants.secondsPerMinute; + const secondsToPass = (targetSeconds - currentSeconds + TimeConstants.secondsPerDay) % TimeConstants.secondsPerDay; return passTime(secondsToPass, "sec"); } Macro.add("passTimeUntil", { @@ -50,16 +49,16 @@ Macro.add("passTimeUntil", { const secondsMapper = { sec: 1, seconds: 1, - min: Time.secondsPerMinute, - mins: Time.secondsPerMinute, - minute: Time.secondsPerMinute, - minutes: Time.secondsPerMinute, - hour: Time.secondsPerHour, - hours: Time.secondsPerHour, - day: Time.secondsPerDay, - days: Time.secondsPerDay, - week: Time.secondsPerDay * 7, - weeks: Time.secondsPerDay * 7, + min: TimeConstants.secondsPerMinute, + mins: TimeConstants.secondsPerMinute, + minute: TimeConstants.secondsPerMinute, + minutes: TimeConstants.secondsPerMinute, + hour: TimeConstants.secondsPerHour, + hours: TimeConstants.secondsPerHour, + day: TimeConstants.secondsPerDay, + days: TimeConstants.secondsPerDay, + week: TimeConstants.secondsPerDay * 7, + weeks: TimeConstants.secondsPerDay * 7, }; /** diff --git a/game/base-system/time/time.js b/game/03-JavaScript/time.js similarity index 88% rename from game/base-system/time/time.js rename to game/03-JavaScript/time.js index 53acd8913..39b57f25f 100644 --- a/game/base-system/time/time.js +++ b/game/03-JavaScript/time.js @@ -51,13 +51,50 @@ */ /* eslint-disable jsdoc/require-description-complete-sentence */ -/* eslint-disable no-undef */ const Time = (() => { - const secondsPerDay = 86400; - const secondsPerHour = 3600; - const secondsPerMinute = 60; - const standardYearMonths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - const leapYearMonths = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + const moonPhases = { + new: { + start: 0, + end: 0.03, + endAlt: 1, + description: "New Moon", + }, + waxingCrescent: { + start: 0.03, + end: 0.22, + description: "Waxing Crescent", + }, + firstQuarter: { + start: 0.22, + end: 0.28, + description: "First Quarter", + }, + waxingGibbous: { + start: 0.28, + end: 0.47, + description: "Waxing Gibbous", + }, + full: { + start: 0.47, + end: 0.53, + description: "Full Moon", + }, + waningGibbous: { + start: 0.53, + end: 0.72, + description: "Waning Gibbous", + }, + lastQuarter: { + start: 0.72, + end: 0.78, + description: "Last Quarter", + }, + waningCrescent: { + start: 0.78, + end: 0.97, + description: "Waning Crescent", + }, + }; const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; const daysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; @@ -66,7 +103,7 @@ const Time = (() => { let currentDate = {}; function set(timeStamp = V.timeStamp) { - currentDate = new DateTime((V.startDate || 0) + timeStamp); + currentDate = new DateTime((V.startDate || 0) + (timeStamp || 0)); V.timeStamp = timeStamp; } /* @@ -109,11 +146,18 @@ const Time = (() => { if (!hours) return fragment; fragment.append(hourPassed(hours)); - if (!V.statFreeze && currentDate.hour >= 6 && !V.daily.dawnCheck) { + if ( + !V.daily.dawnCheck && + ((prevDate.hour < 6 && (currentDate.hour >= 6 || currentDate.day !== prevDate.day)) || (currentDate.day !== prevDate.day && currentDate.hour >= 6)) + ) { V.daily.dawnCheck = true; fragment.append(dawnCheck()); } - if (!V.statFreeze && currentDate.hour >= 12 && !V.daily.noonCheck) { + if ( + !V.daily.noonCheck && + ((prevDate.hour < 12 && (currentDate.hour >= 12 || currentDate.day !== prevDate.day)) || + (currentDate.day !== prevDate.day && currentDate.hour >= 12)) + ) { V.daily.noonCheck = true; fragment.append(noonCheck()); } @@ -132,24 +176,25 @@ const Time = (() => { return fragment; } - function nextSchoolTermStartDate(date) { + function getNextSchoolTermStartDate(date) { const newDate = new DateTime(date); - if (!holidayMonths.includes(newDate.month) && date.day < newDate.getFirstWeekdayOfMonth(2).day) { - return newDate.getFirstWeekdayOfMonth(2); + while (holidayMonths.includes(newDate.month)) { + newDate.addMonths(1); } - newDate.addMonths(holidayMonths.find(e => e >= newDate.month) - newDate.month + 1); return newDate.getFirstWeekdayOfMonth(2); } - function nextSchoolTermEndDate(date) { + function getNextSchoolTermEndDate(date) { const newDate = new DateTime(date); newDate.addMonths(holidayMonths.find(e => e >= newDate.month) - newDate.month); return newDate.getFirstWeekdayOfMonth(2).addDays(-3).addHours(15); } function isSchoolTerm(date) { - const termEndDate = nextSchoolTermEndDate(date).addDays(1); + let termEndDate = getNextSchoolTermEndDate(date); + termEndDate = new DateTime(termEndDate.year, termEndDate.month, termEndDate.day); + termEndDate.addDays(1); const firstMonday = date.getFirstWeekdayOfMonth(2); const prevMonth = ((date.month - 2 + 12) % 12) + 1; @@ -168,6 +213,56 @@ const Time = (() => { return isSchoolDay(date) && date.hour > 8 && date.hour < 15; } + function getDayOfYear(date) { + const start = new DateTime(date.year, 1, 1); + const diff = date.timeStamp - start.timeStamp; + return Math.floor(diff / TimeConstants.secondsPerDay); + } + + function getSecondsSinceMidnight(date) { + return date.hour * TimeConstants.secondsPerHour + date.minute * TimeConstants.secondsPerMinute; + } + + // Current moon phase + function currentMoonPhase(date) { + const phaseFraction = date.moonPhaseFraction; + for (const phase in moonPhases) { + const range = moonPhases[phase]; + if ((phaseFraction >= range.start && phaseFraction < range.end) || (range.endAlt && phaseFraction >= 0.97)) { + return phase; + } + } + } + // Date of previous occurrence of a specific moon phase + function previousMoonPhase(targetPhase) { + const date = new DateTime(currentDate.year, currentDate.month, currentDate.day, 0, 0); + date.setTime(0, 0); + do { + date.addDays(-1); + const currentPhase = currentMoonPhase(date); + if (currentPhase === targetPhase) { + return date; + } + } while (true); + } + + // Date of next occurrence of a specific moon phase + function nextMoonPhase(targetPhase) { + const date = new DateTime(currentDate.year, currentDate.month, currentDate.day, 0, 0); + do { + date.addDays(1); + const currentPhase = currentMoonPhase(date); + if (currentPhase === targetPhase) { + return date; + } + } while (true); + } + + function isBloodMoon(date) { + date = date ?? currentDate; + return (date.day === date.lastDayOfMonth && date.hour >= 21) || (date.day === 1 && date.hour < 6); + } + return Object.create({ get date() { return currentDate; @@ -203,7 +298,7 @@ const Time = (() => { return currentDate.year; }, get days() { - return Math.floor((currentDate.timeStamp - this.startDate.timeStamp) / Time.secondsPerDay); + return Math.floor((currentDate.timeStamp - this.startDate.timeStamp) / TimeConstants.secondsPerDay); }, get season() { return this.month > 11 || this.month < 3 ? "winter" : this.month > 8 ? "autumn" : this.month > 5 ? "summer" : "spring"; @@ -233,21 +328,42 @@ const Time = (() => { }, get dayState() { const hour = this.hour; - return hour < 6 || hour >= 21 ? "night" : hour >= 18 ? "dusk" : hour >= 9 ? "day" : "dawn"; + if (hour < 6 || hour >= 21) { + return "night"; + } + if (hour >= 18) { + return "dusk"; + } + return hour >= 9 ? "day" : "dawn"; }, get nightState() { const hour = this.hour; - return hour < 6 ? "morning" : hour >= 9 ? "evening" : undefined; + if (hour < 6) { + return "morning"; + } + if (hour >= 9) { + return "evening"; + } + return undefined; }, get nextSchoolTermStartDate() { - return nextSchoolTermStartDate(currentDate); + return getNextSchoolTermStartDate(currentDate); }, get nextSchoolTermEndDate() { - return nextSchoolTermEndDate(currentDate); + return getNextSchoolTermEndDate(currentDate); }, get lastDayOfMonth() { return currentDate.lastDayOfMonth; }, + get dayOfYear() { + return getDayOfYear(currentDate); + }, + get secondsSinceMidnight() { + return getSecondsSinceMidnight(currentDate); + }, + get currentMoonPhase() { + return currentMoonPhase(currentDate); + }, set, setDate, setTime, @@ -256,17 +372,18 @@ const Time = (() => { isSchoolTerm, isSchoolDay, isSchoolTime, + getDayOfYear, + getSecondsSinceMidnight, + nextMoonPhase, + previousMoonPhase, + isBloodMoon, - secondsPerDay, - secondsPerHour, - secondsPerMinute, - standardYearMonths, - leapYearMonths, + moonPhases, monthNames, daysOfWeek, - getNextSchoolTermStartDate: nextSchoolTermStartDate, - getNextSchoolTermEndDate: nextSchoolTermEndDate, + getNextSchoolTermStartDate, + getNextSchoolTermEndDate, getNextWeekdayDate: weekDay => currentDate.getNextWeekdayDate(weekDay), getPreviousWeekdayDate: weekDay => currentDate.getPreviousWeekdayDate(weekDay), isWeekEnd: () => currentDate.weekEnd, @@ -377,10 +494,10 @@ function weekPassed() { if (V.brothelVending.weeksEmpty >= 4) V.brothelVending.status = "sold"; } - fragment.append(wikifier("world_corruption", "soft", V.world_corruption_hard)); + statChange.worldCorruption("soft", V.world_corruption_hard); delete V.weekly; - V.weekly = { theft: {}, sewers: {} }; + V.weekly = clone(setup.weeklyObject); return fragment; } @@ -392,7 +509,6 @@ function dayPassed() { if (V.statFreeze) return fragment; fragment.append(wikifier("seenPassageChecks")); - fragment.append(wikifier("earnAllFeats", true)); fragment.append(wikifier("prison_day")); fragment.append(wikifier("clearNPC", "pharmNurse")); fragment.append(wikifier("weather_select")); @@ -407,7 +523,7 @@ function dayPassed() { V.promiscuity = Math.max(V.promiscuity - 1, 0); V.deviancy = Math.max(V.deviancy - 1, 0); } - if (V.locker_suspicion > 0) fragment.append(wikifier("locker_suspicion", -1)); + if (V.locker_suspicion > 0) statChange.lockerSuspicion(-1); if (V.whitneyromance || C.npc.Whitney.dom >= 20) { V.bullytimer += 20; V.bullytimeroutside += 10; @@ -451,7 +567,7 @@ function dayPassed() { if (V.temple_quarters >= 1) V.temple_quarters = Math.clamp(V.temple_quarters - 10, 0, 100); if (V.temple_chastity_timer > 0) V.temple_chastity_timer--; if (V.temple_rank !== "prospective" && V.temple_rank !== "initiate") { - if (V.grace >= 1 && !V.daily.graceUp) fragment.append(wikifier("grace", -2)); + if (V.grace >= 1 && !V.daily.graceUp) statChange.grace(-2); } if (V.temple_evaluation) { V.temple_evaluation--; @@ -485,8 +601,8 @@ function dayPassed() { if (numberOfEarSlime()) { // Daily Corruption - if (V.earSlime.growth < 50) fragment.append(wikifier("corruption", -1)); - fragment.append(wikifier("corruption", numberOfEarSlime(), true)); + if (V.earSlime.growth < 50) statChange.corruption(-1); + statChange.corruption(numberOfEarSlime(), true); earSlimeDaily(); } @@ -521,6 +637,11 @@ function dayPassed() { V.pound.tasks = []; } V.renttime--; + // Not seen bailey for more than 2 weeks, tracks missed rent + if (V.renttime < 0 && V.renttime % 7 === 0) { + V.baileyRefusedToPayTotal += V.rentmoney + (V.babyRent || 0); + V.baileyRefusedToPayTotalStat += V.rentmoney + (V.babyRent || 0); + } if (V.flashbacktown > 0) V.flashbacktown--; if (V.flashbackhome > 0) V.flashbackhome--; @@ -629,7 +750,7 @@ function dayPassed() { const rng = random(Math.min(1, V.brothelVending.condoms), Math.min(10, V.brothelVending.condoms)); V.brothelVending.condoms -= rng; V.brothelVending.condomsSold += rng; - V.brothelVending.condomsToRefill = 200 - (V.brothelVending.condoms); + V.brothelVending.condomsToRefill = 200 - V.brothelVending.condoms; V.brothelVending.total = (V.brothelVending.total || 0) + rng; } @@ -637,7 +758,7 @@ function dayPassed() { const rng = random(Math.min(1, V.brothelVending.lube), Math.min(10, V.brothelVending.lube)); V.brothelVending.lube -= rng; V.brothelVending.lubeSold += rng; - V.brothelVending.lubeToRefill = 200 - (V.brothelVending.lube); + V.brothelVending.lubeToRefill = 200 - V.brothelVending.lube; V.brothelVending.total = (V.brothelVending.total || 0) + rng; } @@ -667,20 +788,7 @@ function dayPassed() { if (V.pillory_tenant.exists && V.pillory_tenant.endDate < V.timeStamp) fragment.append(wikifier("clear_pillory")); delete V.daily; - V.daily = { - school: { attended: {} }, - whitney: {}, - robin: {}, - kylar: {}, - morgan: {}, - eden: {}, - alex: {}, - sydney: {}, - ex: {}, - pharm: {}, - prison: {}, - livestock: {}, - }; + V.daily = clone(setup.dailyObject); if (Number.isInteger(V.challengetimer)) { V.challengetimer--; @@ -709,6 +817,13 @@ function dayPassed() { delete V.pirate_attack; } + /* Set flag to determine Kylar's position at lunch */ + V.daily.kylar.libraryStalk = rollKylarLibraryStalkFlag(); + + if (V.whitney_roof) { + delete V.whitney_roof; + } + return fragment; } @@ -719,10 +834,10 @@ function hourPassed(hours) { if (V.statFreeze) return fragment; for (let i = 0; i < hours; i++) { - if (V.innocencestate === 1 && V.control <= 0) fragment.append(wikifier("awareness", 1)); - fragment.append(wikifier("control", 1)); + if (V.innocencestate === 1 && V.control <= 0) statChange.awareness(1); + statChange.control(1); fragment.append(wikifier("orgasmHourlyRecovery")); - fragment.append(wikifier("arousal", 0, "time")); + statChange.arousal(0, "time"); fragment.append(wikifier("wetnessCalculate")); fragment.append(wikifier("bimboCheck", "upper")); fragment.append(wikifier("bimboCheck", "lower")); @@ -730,7 +845,7 @@ function hourPassed(hours) { if (V.ejactrait >= 1) V.stress -= (V.goocount + V.semencount) * 10; if (V.kylarwatched) V.kylarwatchedtimer--; - if (V.parasite.nipples.name) fragment.append(wikifier("milkvolume", 1)); + if (V.parasite.nipples.name) statChange.milkvolume(1); if (V.worn.head.name === "hairpin" || V.sexStats.pills.pills["Hair Growth Formula"].doseTaken) { let count = 0 + (V.worn.head.name === "hairpin" && random(0, 100) >= 75 ? 1 : 0); count += V.sexStats.pills.pills["Hair Growth Formula"].doseTaken ? 1 : 0; @@ -755,7 +870,8 @@ function hourPassed(hours) { } V.openinghours = Time.hour >= 8 && Time.hour < 21 ? 1 : 0; - fragment.append(wikifier("earnAllFeats")); + const feats = earnHourlyFeats(); + if (feats) fragment.append(feats); temperatureHour(); @@ -808,20 +924,27 @@ function minutePassed(minutes) { V.stress = Math.min(V.stress, V.stressmax); // Effects - if (V.drunk > 0) fragment.append(wikifier("alcohol", -minutes)); - if (V.hallucinogen > 0) fragment.append(wikifier("hallucinogen", -minutes)); - if (V.drugged > 0) fragment.append(wikifier("drugs", -minutes)); - if (minutes < 1200) fragment.append(wikifier("tiredness", minutes * (V.drunk > 0 ? 2 : 1), "pass")); - fragment.append(wikifier("pain", minutes, -1)); + if (V.drunk > 0) statChange.alcohol(-minutes); + if (V.hallucinogen > 0) statChange.hallucinogen(-minutes); + if (V.drugged > 0) statChange.drugs(-minutes); + if (minutes < 1200) statChange.tiredness(minutes * (V.drunk > 0 ? 2 : 1), "pass"); + statChange.pain(minutes, -1); // Arousal const arousalMultiplier = V.backgroundTraits.includes("lustful") ? 0.2 * (12 - Math.floor(V.purity / 80)) + 1 + (V.purity <= 50 ? 1 : 0) : -10; - fragment.append(wikifier("arousal", minutes * arousalMultiplier + getArousal(minutes))); + statChange.arousal(minutes * arousalMultiplier + getArousal(minutes)); V.timeSinceArousal = V.arousal < V.arousalmax / 4 ? V.timeSinceArousal + minutes : 1; if (V.player.vaginaExist) fragment.append(passArousalWetness(minutes)); // Tanning - if (Time.dayState === "day" && V.weather === "clear" && V.outside && V.location !== "forest" && !V.worn.head.type.includes("shade")) { + if ( + Time.dayState === "day" && + V.weather === "clear" && + V.outside && + V.location !== "forest" && + !V.worn.head.type.includes("shade") && + !V.worn.handheld.type.includes("shade") + ) { fragment.append(wikifier("tanned", minutes / (Time.season === "winter" ? 4 : Time.season === "summer" ? 1 : 2))); } @@ -839,6 +962,8 @@ function minutePassed(minutes) { function noonCheck() { const fragment = document.createDocumentFragment(); + if (V.statFreeze) return fragment; + delete V.bartend_info; delete V.bartend_info_other; if (V.per_npc.bartend) fragment.append(wikifier("clearNPC", "bartend")); @@ -863,6 +988,8 @@ function noonCheck() { function dawnCheck() { const fragment = document.createDocumentFragment(); + if (V.statFreeze) return fragment; + V.robinwakeday = 0; V.wolfwake = 0; V.edenwake = 0; @@ -921,7 +1048,7 @@ function dailyNPCEffects() { if (C.npc.Robin.trauma > 0) fragment.append(wikifier("npcincr", "Robin", "trauma", -1)); if (V.robindebtevent === 0) V.robinmissing = 0; - if (V.robinpaid >= 1) fragment.append(wikifier("trauma", -25)); + if (V.robinpaid >= 1) statChange.trauma(-25); if (V.robinromance === 1 && C.npc.Robin.dom >= 40) fragment.append(wikifier("npcincr", "Robin", "lust", 1)); if (V.robinPilloryFail) { delete V.robinPilloryFail; @@ -1092,7 +1219,7 @@ function dailyPlayerEffects() { V.hairlength += 3; V.fringelength += 3; fragment.append(wikifier("calchairlengthstage")); - fragment.append(wikifier("beauty", 100 - (V.trauma / V.traumamax) * 100)); + statChange.skill("beauty", 100 - (V.trauma / V.traumamax) * 100); fragment.append(wikifier("bimboUpdate")); if (V.orgasmstat >= 1000 && V.orgasmtrait === 0) { @@ -1167,12 +1294,12 @@ function dailyPlayerEffects() { } } - fragment.append(wikifier("insecurity", "penis_tiny", -1)); - fragment.append(wikifier("insecurity", "penis_small", -1)); - fragment.append(wikifier("insecurity", "penis_big", -1)); - fragment.append(wikifier("insecurity", "breasts_tiny", -1)); - fragment.append(wikifier("insecurity", "breasts_small", -1)); - fragment.append(wikifier("insecurity", "breasts_big", -1)); + statChange.insecurity("penis_tiny", -1); + statChange.insecurity("penis_small", -1); + statChange.insecurity("penis_big", -1); + statChange.insecurity("breasts_tiny", -1); + statChange.insecurity("breasts_small", -1); + statChange.insecurity("breasts_big", -1); V.insecurity_penis_tiny = Math.clamp(V.insecurity_penis_tiny, 0, 1000); V.insecurity_penis_small = Math.clamp(V.insecurity_penis_small, 0, 1000); @@ -1218,7 +1345,7 @@ function dailyTransformationEffects() { if (V.featsPurityBoost) dailyPurity += V.featsPurityBoost; if (V.fallenangel >= 2) dailyPurity -= 10; if (V.player.virginity.vaginal === true && V.player.virginity.penile === true) dailyPurity += 2; - fragment.append(wikifier("purity", dailyPurity)); + statChange.purity(dailyPurity); if (V.purity >= 1000) fragment.append(wikifier("transform", "angel", 1)); else fragment.append(wikifier("transform", "angel", -1)); @@ -1242,7 +1369,7 @@ function dailyLiquidEffects() { let amount = V.player.penissize - 1; if (V.semen_volume <= 24) amount++; amount -= Math.floor(V.semen_volume / 250); - fragment.append(wikifier("semenvolume", amount)); + statChange.semenvolume(amount); } else { V.semen_volume = 0; V.semen_amount = 0; @@ -1252,11 +1379,11 @@ function dailyLiquidEffects() { if (V.earSlime.growth >= 75 && V.earSlime.focus === "impregnation") pressureReduction -= 2; if (V.earSlime.growth >= 75 && V.earSlime.focus === "pregnancy") pressureReduction += 2; if (pressureReduction < 0) { - fragment.append(wikifier("milkvolume", pressureReduction * 2)); - fragment.append(wikifier("lactation_pressure", pressureReduction)); + statChange.milkvolume(pressureReduction * 2); + statChange.lactationPressure(pressureReduction); } - if (V.purity + V.semen_volume < 980) fragment.append(wikifier("semenvolume", 3)); + if (V.purity + V.semen_volume < 980) statChange.semenvolume(3); if (V.purity + V.milk_volume < 1000) V.milk_volume += 10; if (V.lactating) { @@ -1415,11 +1542,11 @@ function dailySchoolEffects() { V.englishPlay = "missed"; } } - if (V.schooltrait >= 4) fragment.append(wikifier("trauma", -50)); - else if (V.schooltrait === 3) fragment.append(wikifier("trauma", -40)); - else if (V.schooltrait === 2) fragment.append(wikifier("trauma", -30)); - else if (V.schooltrait === 1) fragment.append(wikifier("trauma", -20)); - else fragment.append(wikifier("trauma", -10)); + if (V.schooltrait >= 4) statChange.trauma(-50); + else if (V.schooltrait === 3) statChange.trauma(-40); + else if (V.schooltrait === 2) statChange.trauma(-30); + else if (V.schooltrait === 1) statChange.trauma(-20); + else statChange.trauma(-10); if (Time.isSchoolDay(Time.yesterday) && V.location !== "prison") { const attended = Object.keys(V.daily.school.attended).length; @@ -1449,7 +1576,7 @@ function dailySchoolEffects() { if (C.npc.Winter.love >= V.npclovehigh) delinquencyDecay++; if (C.npc.Mason.love >= V.npclovehigh) delinquencyDecay++; if (V.lessonmissedtext) delinquencyDecay = Math.floor(delinquencyDecay / 2); - fragment.append(wikifier("delinquency", -delinquencyDecay / 4)); + statChange.delinquency(-delinquencyDecay / 4); if (V.schoolfameblackmail !== undefined) V.schoolfameblackmail++; } @@ -1661,30 +1788,30 @@ function passWater(passMinutes) { const fragment = document.createDocumentFragment(); if (V.outside && V.weather === "clear") { - if (V.upperwet) fragment.append(wikifier("upperwet", -passMinutes * 2)); - if (V.lowerwet) fragment.append(wikifier("lowerwet", -passMinutes * 2)); - if (V.underlowerwet) fragment.append(wikifier("underlowerwet", -passMinutes * (V.worn.lower.type.includes("naked") ? 2 : 1))); - if (V.underupperwet) fragment.append(wikifier("underupperwet", -passMinutes * (V.worn.upper.type.includes("naked") ? 2 : 1))); + if (V.upperwet) statChange.wet("upper", -passMinutes * 2); + if (V.lowerwet) statChange.wet("lower", -passMinutes * 2); + if (V.underlowerwet) statChange.wet("underlower", -passMinutes * (V.worn.lower.type.includes("naked") ? 2 : 1)); + if (V.underupperwet) statChange.wet("underupper", -passMinutes * (V.worn.upper.type.includes("naked") ? 2 : 1)); } else if (V.outside && V.weather === "rain" && !V.worn.head.type.includes("rainproof") && !V.worn.handheld.type.includes("rainproof")) { if (!V.worn.upper.type.includes("naked") && !waterproofCheck(V.worn.upper) && !waterproofCheck(V.worn.over_upper)) { - fragment.append(wikifier("upperwet", passMinutes)); + statChange.wet("upper", passMinutes); } if (!V.worn.lower.type.includes("naked") && !waterproofCheck(V.worn.lower) && !waterproofCheck(V.worn.over_lower)) { - fragment.append(wikifier("lowerwet", passMinutes)); + statChange.wet("lower", passMinutes); } // eslint-disable-next-line prettier/prettier if (!V.worn.under_lower.type.includes("naked") && !waterproofCheck(V.worn.under_lower) && !waterproofCheck(V.worn.lower) && !waterproofCheck(V.worn.over_lower)) { - fragment.append(wikifier("underlowerwet", passMinutes)); + statChange.wet("underlower", passMinutes); } // eslint-disable-next-line prettier/prettier if (!V.worn.under_upper.type.includes("naked") && !waterproofCheck(V.worn.under_upper) && !waterproofCheck(V.worn.upper) && !waterproofCheck(V.worn.over_upper)) { - fragment.append(wikifier("underupperwet", passMinutes)); + statChange.wet("underupper", passMinutes); } } else { - if (V.upperwet) fragment.append(wikifier("upperwet", -passMinutes)); - if (V.lowerwet) fragment.append(wikifier("lowerwet", -passMinutes)); - if (V.underlowerwet) fragment.append(wikifier("underlowerwet", -passMinutes)); - if (V.underupperwet) fragment.append(wikifier("underupperwet", -passMinutes)); + if (V.upperwet) statChange.wet("upper", -passMinutes); + if (V.lowerwet) statChange.wet("lower", -passMinutes); + if (V.underlowerwet) statChange.wet("underlower", -passMinutes); + if (V.underupperwet) statChange.wet("underupper", -passMinutes); } return fragment; @@ -1721,8 +1848,8 @@ function passArousalWetness(passMinutes) { // Expected rate: between 1 and 2.61, usually around 1.8 const change = Math.clamp(1 + Math.log10(V.vaginaArousalWetness - 59), 1, 3); if (!V.worn.under_lower.type.includes("naked") && !V.worn.under_lower.type.includes("swim")) { - fragment.append(wikifier("underlowerwet", Math.round(change * passMinutes))); - fragment.append(wikifier("underlowerwet", Math.clamp(V.underlowerwet, 0, 100 + passMinutes))); + statChange.wet("underlower", Math.round(change * passMinutes)); + statChange.wet("underlower", Math.clamp(V.underlowerwet, 0, 100 + passMinutes)); V.pantiesSoaked = V.underlowerwet >= 100; } } @@ -1755,9 +1882,9 @@ function getArousal(passMinutes) { if (V.parasite.right_thigh.name) addedArousal += minuteMultiplier; if (V.drugged > 1) addedArousal += minuteMultiplier; if (playerHasButtPlug()) addedArousal += minuteMultiplier; - if (V.parasite.left_ear.name === "slime" && random(1, 10) >= 9) wikifier("drugs", Math.min(60, passMinutes)); - if (V.parasite.right_ear.name === "slime" && random(1, 10) >= 9) wikifier("drugs", Math.min(60, passMinutes)); - if (V.earSlime.growth > 100 && random(1, 10) >= 9) wikifier("drugs", Math.min(60, passMinutes)); + if (V.parasite.left_ear.name === "slime" && random(1, 10) >= 9) statChange.drugs(Math.min(60, passMinutes)); + if (V.parasite.right_ear.name === "slime" && random(1, 10) >= 9) statChange.drugs(Math.min(60, passMinutes)); + if (V.earSlime.growth > 100 && random(1, 10) >= 9) statChange.drugs(Math.min(60, passMinutes)); if ( V.worn.genitals.name === "chastity parasite" || diff --git a/game/03-JavaScript/ui-sidebar-swipe.js b/game/03-JavaScript/ui-sidebar-swipe.js new file mode 100644 index 000000000..9622f0546 --- /dev/null +++ b/game/03-JavaScript/ui-sidebar-swipe.js @@ -0,0 +1,49 @@ +/* Sidebar swipe */ +document.addEventListener("touchstart", handleTouchStart, false); +document.addEventListener("touchmove", handleTouchMove, false); + +let xDown = null; +let yDown = null; + +function handleTouchStart(ev) { + const firstTouch = ev.touches[0]; + xDown = firstTouch.clientX; + yDown = firstTouch.clientY; +} + +function handleTouchMove(ev) { + if (!xDown || !yDown) return; + + // Activate the swipe only when finger is over the UI Bar + const stowedWidth = 60; // appx. width of stowed UI Bar in px + const unstowedWidth = 300; // same for unstowed UI Bar + // actually, let's just use a threshold instead + // if (UIBar.isStowed() && xDown > stowedWidth || xDown > unstowedWidth) return; + + const xUp = ev.touches[0].clientX; + const yUp = ev.touches[0].clientY; + + const xDiff = xDown - xUp; + const yDiff = yDown - yUp; + + // distance to swipe before triggering action + const threshold = 100; + if (Math.abs(xDiff) < threshold) return; + + if (Math.abs(xDiff) > Math.abs(yDiff)) { + // most significant + if (xDiff > 0) UIBar.stow(); // left swipe + else UIBar.unstow(); // right swipe + // allow opening and then closing sidebar without breaking the touch + xDown = xUp; + yDown = yUp; + return; + } + else { + if (yDiff > 0); // up swipe + else; // down swipe + // nothing yet, just reset the values + xDown = null; + yDown = null; + } +} diff --git a/game/04-Variables/canvasmodel-00-data.js b/game/04-Variables/canvasmodel-00-data.js index 691ae4e6c..62c3498e3 100644 --- a/game/04-Variables/canvasmodel-00-data.js +++ b/game/04-Variables/canvasmodel-00-data.js @@ -33,7 +33,6 @@ const ZIndices = { tears: 55, hair: 60, penis_chastity: 64, - legs: 66.6, // above underParasite but below under_lower pbhair: 64, penisunderclothes: 64.3, pbhairballsunderclothes: 64.6, @@ -42,7 +41,9 @@ const ZIndices = { pbhairballs: 104.3, // when exposed parasite: 104.6, // when exposed underParasite: 66, + legs: 66.6, under_lower: 67, + legs_high: 67.6, under_lower_top: 68, under_upper: 70, under_upper_top: 72, @@ -54,6 +55,7 @@ const ZIndices = { under: 75, feet: 85, lower: 90, + lower_tucked_feet: 95, lower_top: 92, upper_arms: 94, lower_belly: 94.5, diff --git a/game/04-Variables/canvasmodel-main.js b/game/04-Variables/canvasmodel-main.js index 4f4ef0780..2352a7dd8 100644 --- a/game/04-Variables/canvasmodel-main.js +++ b/game/04-Variables/canvasmodel-main.js @@ -6,7 +6,6 @@ /* eslint-disable camelcase */ /* eslint-disable spaced-comment */ /* eslint-disable no-useless-return */ -/* eslint-disable no-undef */ /* eslint-disable prefer-const */ /* eslint-disable prettier/prettier */ /* eslint-disable dot-notation */ @@ -181,6 +180,7 @@ replace (?= 22 ? "_big.png" : ".png"); + options.shirt_move_left_src = "img/clothes/belly/mask_shirt_left" + (options.belly >= 22 ? "_big.png" : ".png"); + options.shirt_move_left2_src = "img/clothes/belly/mask_shirt_left2.png"; + options.shirt_mask_breasts_src = "img/clothes/belly/mask_shirt_breasts.png"; + options.shirt_move_right_src = "img/clothes/belly/mask_shirt_right.png"; + options.shirt_move_right2_src = "img/clothes/belly/mask_shirt_right2.png"; + options.shirt_move_right3_src = "img/clothes/belly/mask_shirt_right3.png"; } else { options.shirt_mask_clip_src = null; - options.shirt_mask_move_src = null; + options.shirt_move_left_src = null; + options.shirt_move_left2_src = null; + options.shirt_move_right_src = null; + options.shirt_move_right2_src = null; + options.shirt_move_right3_src = null; } } else { options.belly_mask_clip_src = null; @@ -803,11 +822,38 @@ Renderer.CanvasModels["main"] = { options.belly_mask_under_clip_src = null; } } + if (["f", "a"].includes(options.body_type) && options.breasts === "cleavage") { + if ([3, 4].includes(options.breast_size)) { + options.breasts_mask_src = `img/body/breasts/breasts-${options.body_type}-mid.png`; + } else { + options.breasts_mask_src = `img/body/breasts/breasts-${options.body_type}.png`; + } + } else { + options.breasts_mask_src = null; + } + if (["f", "a"].includes(options.body_type)) { + if (options.worn_upper_setup.formfitting || options.worn_under_upper_setup.formfitting) { + options.shirt_fitted_clip_src = `img/clothes/masks/formfitting_${options.body_type}.png`; + options.shirt_fitted_right_move_src = `img/clothes/masks/formfitting_right_move.png`; + options.shirt_fitted_left_move_src = `img/clothes/masks/formfitting_left_move.png`; + } else { + options.shirt_fitted_clip_src = null; + options.shirt_fitted_right_move_src = null; + options.shirt_fitted_left_move_src = null; + } + } + if (options.lower_tucked && !options.worn_lower_setup.notuck && !options.worn_feet_setup.notuck) { + options.feet_clip_src = "img/clothes/feet/" + options.worn_feet_setup.variable + "/mask.png"; + } else { + options.feet_clip_src = null; + } + + options.genitals_chastity = options.worn_genitals_setup.type.includes("chastity"); if (options.worn_handheld_setup.type.includes("rainproof")) { options.handheld_overhead = true; if (options.angel_halo_type === "default") { options.angel_halo_lower = true; } - } else if (["balloon", "heart balloon"].includes(options.worn_handheld_setup.name)) { + } else if (["balloon", "heart balloon", "paper fan", "torch"].includes(options.worn_handheld_setup.name)) { options.handheld_overhead = true; options.angel_halo_lower = false; } else { @@ -822,6 +868,50 @@ Renderer.CanvasModels["main"] = { } options.genitals_chastity = options.worn_genitals_setup.type.includes("chastity"); + + if (options.worn_head_setup.name === "cat hoodie hood" && options.worn_upper_setup.name === "cat hoodie") { + options.hood_damage = true; + } else { + options.hood_damage = false; + } + + if (options.worn_neck_setup.has_collar === 1 && options.worn_upper_setup.has_collar === 1 && !(options.worn_upper_setup.name === "dress shirt" && V.worn.upper.altposition === "alt")) { + options.nocollar = true; + options.serafuku = false + } else if (options.worn_neck_setup.name === "sailor ribbon" && options.worn_upper_setup.name === "serafuku") { + options.nocollar = false; + options.serafuku = true + } else { + options.nocollar = false; + options.serafuku = false + } + + if (options.worn_head_setup.mask_img === 1 && !(options.hood_down && options.worn_head_setup.hood && options.worn_head_setup.outfitSecondary !== undefined)) { + options.hood_mask = true; + } else { + options.hood_mask = null; + } + + if (options.worn_neck_setup.name === "suspenders" && options.worn_neck_setup.altposition != "alt" && + ["retro shorts", "retro trousers", "baseball shorts", "wide leg trousers"].includes(options.worn_lower_setup.name)) { + options.high_waist_suspenders = true; + } else { + options.high_waist_suspenders = null; + } + + /*clothes whose altposition does not include alternate sleeve/full states*/ + if (options.worn_upper_setup.altdisabled) { + options.worn_upper_setup.altdisabled.includes("sleeves") ? options.alt_without_sleeves = true : + options.alt_without_sleeves = null; + options.worn_upper_setup.altdisabled.includes("full") ? options.alt_without_full = true : + options.alt_without_full = null; + } + /*clothes whose sleeves cannot be rolled up*/ + if (options.worn_upper_setup.variable === "schoolcardigan" && options.worn_upper_setup.altposition === "alt") { + options.alt_sleeve_state = null; + } else { + options.alt_sleeve_state = true; + } }, layers: { // banner comments generated in http://patorjk.com/software/taag/#p=display&c=c&f=ANSI%20Regular&t=base @@ -863,10 +953,12 @@ Renderer.CanvasModels["main"] = { } else { if (options.breast_size <= 0) return ""; let fn = "breasts" + options.breast_size + (options.breasts === "cleavage" && options.breast_size >= 3 ? "_clothed" : "") + ".png"; - if (fn === "breasts5_clothed.png") fn = "breasts6_clothed.png"; return "img/body/breasts/" + fn; } }, + masksrcfn(options) { + return options.breasts_mask_src; + }, showfn(options) { return !!options.breasts; }, @@ -1164,6 +1256,9 @@ Renderer.CanvasModels["main"] = { srcfn(options) { return 'img/face/' + options.facestyle + '/makeup/mascara' + (options.eyes_half ? "_halfclosed" : "") + '.png' }, + animationfn(options) { + return options.blink_animation + }, showfn(options) { return options.show_face && !!options.mascara_colour }, @@ -1288,7 +1383,11 @@ Renderer.CanvasModels["main"] = { return !!options.show_hair && !!options.hair_fringe_type }, masksrcfn(options) { - return options.head_mask_src; + if (options.head_mask_src) { + return options.head_mask_src; + } else { + return options.fringe_mask_src; + } }, filters: ["hair_fringe"], z: ZIndices.fronthair, @@ -1296,10 +1395,12 @@ Renderer.CanvasModels["main"] = { }, "hair_extra": { // Extra layer for thighs+ long hair for certain styles srcfn(options) { - if (options.hair_sides_length === "feet" && ["default", "loose", "straight", "curl", "defined curl", "neat", "dreads", "afro pouf", "thick ponytail", "all down", "half-up"].includes(options.hair_sides_type)) { + if (options.hair_sides_length === "feet" && ["default", "loose", "straight", "curl", "defined curl", "neat", "dreads", "afro pouf", "thick ponytail", "all down", "half-up", "messy ponytail"].includes(options.hair_sides_type)) { return "img/hair/back/" + options.hair_sides_type + '/' + "feet.png" - } else if (options.hair_sides_length === "thighs" && ["default", "loose", "curl", "defined curl", "neat", "dreads", "afro pouf", "thick_ponytail", "all down", "half-up"].includes(options.hair_sides_type)) { + } else if (options.hair_sides_length === "thighs" && ["default", "loose", "curl", "defined curl", "neat", "dreads", "afro pouf", "thick_ponytail", "all down", "half-up", "messy ponytail"].includes(options.hair_sides_type)) { return "img/hair/back/" + options.hair_sides_type + '/' + "thighs.png" + } else if (options.hair_sides_length === "navel" && ["messy ponytail"].includes(options.hair_sides_type)) { + return "img/hair/back/" + options.hair_sides_type + '/' + "navel.png" } else if (["ruffled"].includes(options.hair_sides_type)) { return "img/hair/back/" + options.hair_sides_type + '/' + options.hair_sides_length + ".png" } else { @@ -1539,6 +1640,21 @@ Renderer.CanvasModels["main"] = { }, animation: "idle" }, + "angel_wings_right_front": { + srcfn(options) { + return `img/transformations/angel/rightwing/${options.angel_wings_type}.png`; + }, + showfn(options) { + return options.show_tf && isPartEnabled(options.angel_wings_type) && options.angel_wing_right === "idle" && options.angel_wings_type === "default" && options.hair_sides_position !== "front" && options.angel_wings_layer !== "back"; + }, + masksrcfn(options) { + return `img/transformations/angel/rightwing/${options.angel_wings_type}_mask.png`; + }, + zfn(options) { + return ZIndices.over_head + }, + animation: "idle" + }, "angel_wings_rightcover": { srcfn(options) { return `img/transformations/angel/rightcover/${options.angel_wings_type}.png`; @@ -1565,6 +1681,21 @@ Renderer.CanvasModels["main"] = { }, animation: "idle" }, + "angel_wings_left_front": { + srcfn(options) { + return `img/transformations/angel/leftwing/${options.angel_wings_type}.png`; + }, + showfn(options) { + return options.show_tf && isPartEnabled(options.angel_wings_type) && options.angel_wing_left === "idle" && options.angel_wings_type === "default" && options.hair_sides_position !== "front" && options.angel_wings_layer !== "back"; + }, + masksrcfn(options) { + return `img/transformations/angel/leftwing/${options.angel_wings_type}_mask.png`; + }, + zfn(options) { + return ZIndices.over_head + }, + animation: "idle" + }, "angel_wings_leftcover": { srcfn(options) { return `img/transformations/angel/leftcover/${options.angel_wings_type}.png`; @@ -1586,7 +1717,7 @@ Renderer.CanvasModels["main"] = { return options.angel_halo_lower && isPartEnabled(options.angel_halo_type) ? 20 : 0; }, zfn(options) { - return options.angel_halo_lower && isPartEnabled(options.angel_halo_type) ? ZIndices.back_lower : ZIndices.over_head_back; + return options.angel_halo_lower && isPartEnabled(options.angel_halo_type) ? ZIndices.head_back : ZIndices.over_head_back; }, animation: "idle" }, @@ -1631,6 +1762,21 @@ Renderer.CanvasModels["main"] = { }, animation: "idle" }, + "fallen_wings_right_front": { + srcfn(options) { + return `img/transformations/fallen/rightwing/${options.fallen_wings_type}.png`; + }, + showfn(options) { + return options.show_tf && isPartEnabled(options.fallen_wings_type) && options.fallen_wing_right === "idle" && ["default", "fallenplus"].includes(options.fallen_wings_type) && options.hair_sides_position !== "front" && options.fallen_wings_layer !== "back"; + }, + masksrcfn(options) { + return `img/transformations/fallen/rightwing/${options.fallen_wings_type}_mask.png`; + }, + zfn(options) { + return ZIndices.over_head; + }, + animation: "idle" + }, "fallen_wings_rightcover": { srcfn(options) { return `img/transformations/fallen/rightcover/${options.fallen_wings_type}.png`; @@ -1657,6 +1803,21 @@ Renderer.CanvasModels["main"] = { }, animation: "idle" }, + "fallen_wings_left_front": { + srcfn(options) { + return `img/transformations/fallen/leftwing/${options.fallen_wings_type}.png`; + }, + showfn(options) { + return options.show_tf && isPartEnabled(options.fallen_wings_type) && options.fallen_wing_left === "idle" && ["default", "fallenplus"].includes(options.fallen_wings_type) && options.hair_sides_position !== "front" && options.fallen_wings_layer !== "back"; + }, + masksrcfn(options) { + return `img/transformations/fallen/leftwing/${options.fallen_wings_type}_mask.png`; + }, + zfn(options) { + return ZIndices.over_head; + }, + animation: "idle" + }, "fallen_wings_leftcover": { srcfn(options) { return `img/transformations/fallen/leftcover/${options.fallen_wings_type}.png`; @@ -2547,21 +2708,216 @@ Renderer.CanvasModels["main"] = { * */ "upper_main": genlayer_clothing_main('upper', { + zfn(options) { + return options.worn_upper_setup.name === "cocoon" ? ZIndices.over_head : options.zupper; + }, + masksrcfn(options) { + if (options.belly >= 7) { + return options.shirt_mask_clip_src; + } else { + return options.shirt_fitted_clip_src; + } + } + }), + "upper_fitted_left": genlayer_clothing_fitted_left("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_fitted_left_move_src; + }, + dxfn(options) { + return -2; + }, + }), + "upper_fitted_right": genlayer_clothing_fitted_right("upper", { zfn(options) { return options.zupper }, + masksrcfn(options) { + return options.shirt_fitted_right_move_src; + }, + dxfn(options) { + return 2; + }, + }), + "upper_belly_split_shadow": genlayer_clothing_belly_split("upper", { + zfn(options) { + return options.zupper-1 + }, masksrcfn(options) { return options.shirt_mask_clip_src; + }, + dyfn(options) { + if (options.shirt_move_left_src) { + return 2; + } else { + return 0; + } + }, + dxfn(options) { + return 0; + }, + brightnessfn(options){ + if (options.shirt_move_left_src) { + return -.3; + } else { + return 0; + } + } + }), + "upper_belly_split_l": genlayer_clothing_belly_split("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_left_src; + }, + dxfn(options) { + if (options.shirt_move_left_src) { + if (options.belly >= 22) { + return 12; + } else { + return 8; + } + } else { + return 0; + } + }, + dyfn(options) { + if (options.shirt_move_left_src) { + return -2; + } else { + return 0; + } + }, + }), + "upper_belly_split_l2": genlayer_clothing_belly_split("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_left2_src; + }, + dxfn(options) { + if (options.shirt_move_left2_src) { + if (options.belly >= 22) { + return 14; + } else { + return 10; + } + } else { + return 0; + } + }, + dyfn(options) { + if (options.shirt_move_left2_src) { + return 0; + } else { + return 0; + } + }, + }), + "upper_belly_split_l_shadow": genlayer_clothing_belly_split("upper", { + zfn(options) { + return options.zupper-1 + }, + masksrcfn(options) { + return options.shirt_move_left_src; + }, + dxfn(options) { + if (options.shirt_move_left_src) { + if (options.belly >= 22) { + return 14; + } else { + return 10; + } + } else { + return 0; + } + }, + dyfn(options) { + if (options.shirt_move_left_src) { + return -2; + } else { + return 0; + } + }, + brightnessfn(options){ + if (options.shirt_move_left_src) { + return -.3; + } else { + return 0; + } + } + }), + "upper_belly_split_l2_shadow": genlayer_clothing_belly_split("upper", { + zfn(options) { + return options.zupper-1 + }, + masksrcfn(options) { + return options.shirt_move_left2_src; + }, + dxfn(options) { + if (options.shirt_move_left2_src) { + if (options.belly >= 22) { + return 16; + } else { + return 12; + } + } else { + return 0; + } + }, + dyfn(options) { + if (options.shirt_move_left2_src) { + return 0; + } else { + return 0; + } + }, + brightnessfn(options){ + if (options.shirt_move_left_src) { + return -.3; + } else { + return 0; + } } }), - "upper_belly_3": genlayer_clothing_belly_split("upper", { + "upper_belly_split_r": genlayer_clothing_belly_split("upper", { zfn(options) { return options.zupper }, masksrcfn(options) { - return options.shirt_mask_move_src; + return options.shirt_move_right_src; } }), + "upper_belly_split_r2": genlayer_clothing_belly_split("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_right2_src; + }, + dxfn(options) { + if (options.shirt_move_right2_src) { + return -4; + } + }, + }), + "upper_belly_split_r3": genlayer_clothing_belly_split("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_right3_src; + }, + dxfn(options) { + if (options.shirt_move_right3_src) { + return -6; + } + }, + }), "upper_belly_2": genlayer_clothing_belly_2("upper", { zfn(options) { return options.zupper @@ -2596,42 +2952,209 @@ Renderer.CanvasModels["main"] = { return options.belly_mask_src; } }), - "upper_breasts": genlayer_clothing_breasts("upper", { + "upper_belly_split_acc_shadow": genlayer_clothing_belly_split_acc("upper", { zfn(options) { - if (options.acc_layer_under) { - return ZIndices.upper + 1; - }else { - return options.zupper + return options.zupper-1 + }, + masksrcfn(options) { + return options.shirt_mask_clip_src; + }, + dyfn(options) { + if (options.shirt_move_left_src) { + return 2; + } else { + return 0; } - } - }), - "upper_acc": genlayer_clothing_accessory("upper", { - zfn(options) { - if (options.arm_right === "hold" && (options.worn_upper_setup.name === "winter jacket" || options.worn_over_upper_setup.name === "winter jacket" || options.worn_upper_setup.name === "skimpy lolita dress")) { - return ZIndices.lower_high; + }, + dxfn(options) { + return 0; + }, + brightnessfn(options){ + if (options.shirt_move_left_src) { + return -.3; } else { - return options.zupper; + return 0; } } }), - "upper_breasts_acc": genlayer_clothing_breasts_acc("upper", { + "upper_belly_split_acc_l": genlayer_clothing_belly_split_acc("upper", { zfn(options) { return options.zupper - } + }, + masksrcfn(options) { + return options.shirt_move_left_src; + }, + dxfn(options) { + if (options.shirt_move_left_src) { + if (options.belly >= 22) { + return 12; + } else { + return 10; + } + } else { + return 0; + } + }, + dyfn(options) { + if (options.shirt_move_left_src) { + return -4; + } else { + return 0; + } + }, }), - "upper_rightarm": genlayer_clothing_arm("right", "upper", { + "upper_belly_split_acc_l2": genlayer_clothing_belly_split_acc("upper", { zfn(options) { - return options.zupperright - } + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_left2_src; + }, + dxfn(options) { + if (options.shirt_move_left2_src) { + if (options.belly >= 22) { + return 14; + } else { + return 12; + } + } else { + return 0; + } + }, + dyfn(options) { + if (options.shirt_move_left2_src) { + return -2; + } else { + return 0; + } + }, }), - "upper_leftarm": genlayer_clothing_arm("left", "upper", { + "upper_belly_split_acc_r": genlayer_clothing_belly_split_acc("upper", { zfn(options) { - return options.zupperleft + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_right_src; + } + }), + "upper_belly_split_acc_r2": genlayer_clothing_belly_split_acc("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_right2_src; + }, + dxfn(options) { + if (options.shirt_move_right2_src) { + return -4; + } + }, + }), + "upper_belly_split_acc_r3": genlayer_clothing_belly_split_acc("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_move_right3_src; + }, + dxfn(options) { + if (options.shirt_move_right3_src) { + return -6; + } + }, + }), + "upper_acc": genlayer_clothing_accessory("upper", { + zfn(options) { + if (options.arm_right === "hold" && options.sleeve_over_hold) { + return ZIndices.lower_high; + } else if (options.worn_upper_setup.breasts_under_acc === 1) { + return options.zupper + 1; + } else { + return options.zupper; + } + }, + masksrcfn(options) { + if (options.belly >= 19 && options.worn_upper_setup.pregType == "split") { + return options.shirt_mask_clip_src; + } else { + return options.shirt_fitted_clip_src; + } + } + }), + "upper_fitted_left_acc": genlayer_clothing_fitted_left_acc("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_fitted_left_move_src; + }, + dxfn(options) { + return -2; + }, + }), + "upper_fitted_right_acc": genlayer_clothing_fitted_right_acc("upper", { + zfn(options) { + return options.zupper + }, + masksrcfn(options) { + return options.shirt_fitted_right_move_src; + }, + dxfn(options) { + return 2; + }, + }), + "upper_breasts": genlayer_clothing_breasts("upper", { + zfn(options) { + if (options.acc_layer_under) { + return ZIndices.upper + 1; + } else { + return options.zupper + } + }, + masksrcfn(options) { + return options.shirt_mask_breasts_src; + } + }), + "upper_breasts_acc": genlayer_clothing_breasts_acc("upper", { + zfn(options) { + return options.zupper + } + }), + "upper_rightarm": genlayer_clothing_arm("right", "upper", { + zfn(options) { + return options.zupperright + } + }), + "upper_leftarm": genlayer_clothing_arm("left", "upper", { + zfn(options) { + return options.zupperleft }, masksrcfn(options) { return options.belly_hides_lower ? options.belly_mask_clip_src : null; } }), + "upper_leftarm_fitted": genlayer_clothing_arm_fitted("left", "upper", { + zfn(options) { + return options.zupperleft-1 + }, + masksrcfn(options) { + return options.shirt_fitted_left_move_src; + }, + dxfn(options) { + return -2 + } + }), + "upper_leftarm_fitted_acc": genlayer_clothing_arm_acc_fitted("left", "upper", { + zfn(options) { + return options.zupperleft-1 + }, + masksrcfn(options) { + return options.shirt_fitted_left_move_src; + }, + dxfn(options) { + return-2 + } + }), "upper_rightarm_acc": genlayer_clothing_arm_acc("right", "upper", { zfn(options) { return options.zupperright @@ -2727,7 +3250,11 @@ Renderer.CanvasModels["main"] = { return options.worn_lower_setup.high_img ? ZIndices.lower_high : options.worn_lower_setup.covers_top ? ZIndices.lower_cover : ZIndices.lower; }, masksrcfn(options) { - return options.belly_mask_clip_src; + if (between(options.belly, 15, 24)) { + return options.belly_mask_clip_src; + } else { + return options.feet_clip_src; + } }, }), "lower_belly_2": genlayer_clothing_belly_2("lower", { @@ -2767,18 +3294,18 @@ Renderer.CanvasModels["main"] = { let path = 'img/clothes/lower/' + options.worn_lower_setup.variable + '/' + (options.worn_lower_setup.accessory_integrity_img ? 'acc_' + options.worn_lower_integrity : options.worn_upper_setup.name === "school blouse" && ["school pinafore", "plaid school pinafore"].includes(options.worn_lower_setup.name) ? 'acc_under' : 'acc') + '.png'; - if (["school pinafore", "plaid school pinafore"].includes(options.worn_lower_setup.name)) { - return gray_suffix(path, options.filters['worn_lower_acc']) - }else { - return gray_suffix(path, options.filters['worn_lower_acc']) - } + return gray_suffix(path, options.filters['worn_lower_acc']) }, zfn(options) { return options.worn_lower_setup.high_img ? ZIndices.lower_high : options.worn_lower_setup.covers_top ? ZIndices.lower_cover : ZIndices.lower; }, masksrcfn(options) { - return options.belly_mask_clip_src; - }, + if (between(options.belly, 15, 24)) { + return options.belly_mask_clip_src; + } else { + return options.feet_clip_src; + } + }, }), "lower_penis": { srcfn(options) { @@ -2954,9 +3481,31 @@ Renderer.CanvasModels["main"] = { */ "under_upper": genlayer_clothing_main('under_upper', { masksrcfn(options) { - return options.shirt_mask_clip_src; + if (options.belly >= 19 && options.worn_upper_setup.pregType == "split") { + return options.worn_under_upper_setup.pregType === "split" && + options.shirt_mask_clip_src; + } else { + return options.worn_under_upper_setup.formfitting && + options.shirt_fitted_clip_src; + } } }), + "under_upper_fitted_left": genlayer_clothing_fitted_left("under_upper", { + masksrcfn(options) { + return options.shirt_fitted_left_move_src; + }, + dxfn(options) { + return -2; + }, + }), + "under_upper_fitted_right": genlayer_clothing_fitted_right("under_upper", { + masksrcfn(options) { + return options.shirt_fitted_right_move_src; + }, + dxfn(options) { + return 2; + }, + }), "under_upper_belly_2": genlayer_clothing_belly_2("under_upper", { masksrcfn(options) { return options.belly_mask_src; @@ -3005,6 +3554,28 @@ Renderer.CanvasModels["main"] = { return options.arm_left === "cover" ? ZIndices.under_upper_arms_cover : ZIndices.under_upper_arms; } }), + "under_upper_leftarm_fitted": genlayer_clothing_arm_fitted("left", "under_upper", { + zfn(options) { + return ZIndices.under_upper_arms-.1; + }, + masksrcfn(options) { + return options.shirt_fitted_left_move_src; + }, + dxfn(options) { + return -2 + } + }), + "under_upper_leftarm_fitted_acc": genlayer_clothing_arm_acc_fitted("left", "under_upper", { + zfn(options) { + return ZIndices.under_upper_arms-.1; + }, + masksrcfn(options) { + return options.shirt_fitted_left_move_src; + }, + dxfn(options) { + return-2 + } + }), /*** * ██ ██ █████ ███ ██ ██████ ███████ * ██ ██ ██ ██ ████ ██ ██ ██ ██ @@ -3104,18 +3675,26 @@ Renderer.CanvasModels["main"] = { */ "handheld": genlayer_clothing_main('handheld', { srcfn(options) { + let torchLevels = [100, 80, 60, 40, 20, 1, 0]; + let fileFormat = options.worn_handheld_setup.name === "torch" && V.catacombs_torch >= 0 ? `${torchLevels.findIndex(x => V.catacombs_torch >= x) + 1}.png` : ".png"; let path = 'img/clothes/handheld/' + options.worn_handheld_setup.variable + '/' + - (options.arm_right === "cover" ? "right_cover" : "right") + '.png'; + (options.arm_right === "cover" ? "right_cover" : "right") + fileFormat; return gray_suffix(path, options.filters['worn_handheld']); }, showfn(options) { - return options.show_clothes && + if (options.arm_right === "cover") { + return options.show_clothes && options.worn_handheld > 0 && - options.arm_right !== "none" + options.worn_handheld_setup.coverImage !== 0; + } else { + return options.show_clothes && + options.worn_handheld > 0 && + options.arm_right !== "none"; + } }, zfn(options) { - return options.handheld_overhead ? ZIndices.over_upper : ZIndices.handheld; + return options.handheld_overhead || options.worn_handheld_setup.type.includes("prop") ? ZIndices.over_upper : ZIndices.handheld; }, }), "handheld_acc": genlayer_clothing_accessory('handheld', { @@ -3126,13 +3705,20 @@ Renderer.CanvasModels["main"] = { return gray_suffix(path, options.filters['worn_handheld_acc']); }, showfn(options) { - return options.show_clothes && + if (options.arm_right === "cover") { + return options.show_clothes && options.worn_handheld > 0 && options.worn_handheld_setup.accessory === 1 && - options.arm_right !== "none" + options.worn_handheld_setup.coverImage !== 0; + } else { + return options.show_clothes && + options.worn_handheld > 0 && + options.worn_handheld_setup.accessory === 1 && + options.arm_right !== "none"; + } }, zfn(options) { - return options.handheld_overhead ? ZIndices.over_upper : ZIndices.handheld; + return options.handheld_overhead || options.worn_handheld_setup.type.includes("prop") ? ZIndices.over_upper : ZIndices.handheld; }, }), "handheld_left": { @@ -3146,13 +3732,13 @@ Renderer.CanvasModels["main"] = { return options.show_clothes && options.worn_handheld > 0 && options.worn_handheld_setup.leftImage === 1 && - options.arm_left !== "none" + options.arm_left !== "none"; }, zfn(options) { return options.arm_left === "cover" ? ZIndices.hands : (options.zarms + 0.2); }, filtersfn(options) { - return ["worn_handheld"] + return ["worn_handheld"]; }, }, "handheld_left_acc": { @@ -3167,7 +3753,7 @@ Renderer.CanvasModels["main"] = { options.worn_handheld > 0 && options.worn_handheld_setup.leftImage === 1 && options.worn_handheld_setup.accessory === 1 && - options.arm_left !== "none" + options.arm_left !== "none"; }, zfn(options) { return options.arm_left === "cover" ? ZIndices.hands : (options.zarms + 0.2); @@ -3177,10 +3763,10 @@ Renderer.CanvasModels["main"] = { }, }, "handheld_back_acc": genlayer_clothing_back_img_acc('handheld', { - z: ZIndices.back + z: ZIndices.over_head_back }), "handheld_back": genlayer_clothing_back_img('handheld',{ - z: ZIndices.back + z: ZIndices.over_head_back }), /*** * ██ ██ ███████ █████ ██████ @@ -3195,15 +3781,22 @@ Renderer.CanvasModels["main"] = { srcfn(options) { let path = 'img/clothes/head/' + options.worn_head_setup.variable + '/' + - (options.worn_head_setup.name === "cat hoodie hood" && options.worn_upper_setup.name === "cat hoodie" ? options.worn_upper_integrity : options.worn_head_integrity) + '.png'; + (options.hood_damage ? options.worn_upper_integrity : options.worn_head_integrity) + '.png'; return gray_suffix(path, options.filters['worn_head']); }, + masksrcfn(options) { + if (options.worn_upper_setup.name === "cocoon") { + return options.head_mask_src; + } else { + return ""; + } + }, }), "head_acc": genlayer_clothing_accessory('head', { srcfn(options) { let path = 'img/clothes/head/' + options.worn_head_setup.variable + '/' + - (options.worn_head_setup.name === "cat hoodie hood" && options.worn_upper_setup.name === "cat hoodie" ? 'acc_' + options.worn_upper_integrity : 'acc') + '.png'; + (options.hood_damage ? 'acc_' + options.worn_upper_integrity : 'acc') + '.png'; return gray_suffix(path, options.filters['worn_head_acc']); }, }), @@ -3233,19 +3826,45 @@ Renderer.CanvasModels["main"] = { */ "face": genlayer_clothing_main('face', { + srcfn(options) { + let isAltPosition = options.alt_position_face && + options.worn_face_setup.altposition !== undefined; + let path = 'img/clothes/face/' + + options.worn_face_setup.variable + '/' + + options.worn_face_integrity + (isAltPosition ? '_alt' : '') + '.png'; + return gray_suffix(path, options.filters['worn_face']); + }, zfn(options) { - if (options.facewear_layer === "front") { + let isAltPosition = options.alt_position_face && + options.worn_face_setup.altposition !== undefined; + if (isAltPosition && options.worn_face_setup.type.includes("cool") || options.worn_face_setup.type.includes("glasses")) { + return ZIndices.over_head; + } else if (options.facewear_layer === "front") { return ZIndices.face - 12.5; - }else { + } else { return ZIndices.face; } }, }), "face_acc": genlayer_clothing_accessory('face', { + srcfn(options) { + let isAltPosition = options.alt_position_face && + options.worn_face_setup.altposition !== undefined; + let path = 'img/clothes/face/' + + options.worn_face_setup.variable + '/' + + 'acc' + + (setup.accessory_integrity_img ? '_' + options.worn_face_integrity : '') + + (isAltPosition ? '_alt' : '') + '.png'; + return gray_suffix(path, options.filters['worn_face_acc']); + }, zfn(options) { - if (options.facewear_layer === "front") { + let isAltPosition = options.alt_position_face && + options.worn_face_setup.altposition !== undefined; + if (isAltPosition && options.worn_face_setup.type.includes("cool") || options.worn_face_setup.type.includes("glasses")) { + return ZIndices.over_head; + } else if (options.facewear_layer === "front") { return ZIndices.face - 12.5; - }else { + } else { return ZIndices.face; } }, @@ -3268,13 +3887,14 @@ Renderer.CanvasModels["main"] = { options.worn_neck_setup.altposition !== undefined; let path = 'img/clothes/neck/' + options.worn_neck_setup.variable + '/' + - options.worn_neck_integrity + (options.worn_neck_setup.has_collar === 1 && options.worn_upper_setup.has_collar === 1 ? '_nocollar' : options.worn_neck_setup.name === "sailor ribbon" && options.worn_upper_setup.name === "serafuku" ? '_serafuku' :'') + (isAltPosition ? '_alt' : '') + '.png'; + options.worn_neck_integrity + (options.nocollar ? '_nocollar' : options.serafuku ? '_serafuku' :'') + (isAltPosition ? '_alt' : '') + '.png'; return gray_suffix(path, options.filters['worn_neck']); }, + masksrcfn(options) { + return options.high_waist_suspenders ? "img/clothes/neck/suspenders/mask.png" : null; + }, zfn(options) { - return (options.worn_head_setup.mask_img === 1 && - !(options.hood_down && options.worn_head_setup.hood && options.worn_head_setup.outfitSecondary !== undefined)) - ? ZIndices.collar : ZIndices.neck; + return options.hood_mask ? ZIndices.collar : ZIndices.neck; }, }), "neck_acc": genlayer_clothing_accessory('neck', { @@ -3284,15 +3904,18 @@ Renderer.CanvasModels["main"] = { let path = 'img/clothes/neck/' + options.worn_neck_setup.variable + '/' + 'acc' + - (setup.accessory_integrity_img ? '_' + options["worn_" + slot + "_integrity"] : '') + + (setup.accessory_integrity_img ? '_' + options.worn_neck_integrity : '') + (isAltPosition ? '_alt' : '') + '.png'; - return gray_suffix(path, options.filters['worn_neck']); + return gray_suffix(path, options.filters['worn_neck_acc']); }, zfn(options) { return (options.worn_head_setup.mask_img === 1 && !(options.hood_down && options.worn_head_setup.hood && options.worn_head_setup.outfitSecondary !== undefined)) ? ZIndices.collar : ZIndices.neck; }, + dyfn(options) { + return options.high_waist_suspenders ? -8 : 0; + } }), /*** * ██ ███████ ██████ ███████ @@ -3305,10 +3928,38 @@ Renderer.CanvasModels["main"] = { */ "legs": genlayer_clothing_main('legs', { masksrcfn(options) { - return options.belly_mask_clip_src; + if (between(options.belly, 15, 24)) { + return options.belly_mask_clip_src; + } else { + return options.feet_clip_src; + } + }, + zfn(options) { + if (options.worn_under_lower_setup.set === options.worn_under_upper_setup.set || + options.worn_under_lower_setup.high_img === 1) { + return ZIndices.legs; + } else { + return ZIndices.legs_high; + } + } + }), + "legs_acc": genlayer_clothing_accessory('legs', { + masksrcfn(options) { + if (between(options.belly, 15, 24)) { + return options.belly_mask_clip_src; + } else { + return options.feet_clip_src; + } }, + zfn(options) { + if (options.worn_under_lower_setup.set === options.worn_under_upper_setup.set || + options.worn_under_lower_setup.high_img === 1) { + return ZIndices.legs; + } else { + return ZIndices.legs_high; + } + } }), - "legs_acc": genlayer_clothing_accessory('legs'), "legs_back_acc": genlayer_clothing_back_img_acc('legs'), "legs_back": genlayer_clothing_back_img('legs'), /*** @@ -3320,8 +3971,24 @@ Renderer.CanvasModels["main"] = { * * */ - "feet": genlayer_clothing_main('feet'), - "feet_acc": genlayer_clothing_accessory('feet'), + "feet": genlayer_clothing_main('feet', { + zfn(options) { + if (options.lower_tucked && !options.worn_lower_setup.notuck && !options.worn_feet_setup.notuck) { + return ZIndices.lower_tucked_feet; + } else { + return ZIndices.feet; + } + } + }), + "feet_acc": genlayer_clothing_accessory('feet', { + zfn(options) { + if (options.lower_tucked) { + return ZIndices.lower_tucked_feet; + } else { + return ZIndices.feet; + } + } + }), "feet_back_acc": genlayer_clothing_back_img_acc('feet'), "feet_back": genlayer_clothing_back_img('feet'), @@ -3385,7 +4052,8 @@ function genlayer_clothing_main(slot, overrideOptions) { options["worn_" + slot + "_setup"].hoodposition !== undefined && options["worn_" + slot + "_setup"].outfitPrimary.head !== undefined; let isAltPosition = options.alt_position && - options["worn_" + slot + "_setup"].altposition !== undefined; + options["worn_" + slot + "_setup"].altposition !== undefined && + !options.alt_without_full; let path = 'img/clothes/' + slot + '/' + options["worn_" + slot + "_setup"].variable + '/' + @@ -3406,6 +4074,134 @@ function genlayer_clothing_main(slot, overrideOptions) { animation: "idle" }, overrideOptions) } +function genlayer_clothing_fitted_left(slot, overrideOptions) { + return Object.assign({ + srcfn(options) { + let isHoodDown = options.hood_down && + options["worn_" + slot + "_setup"].hoodposition !== undefined && + options["worn_" + slot + "_setup"].outfitPrimary.head !== undefined; + let isAltPosition = options.alt_position && + options["worn_" + slot + "_setup"].altposition !== undefined && + !options.alt_without_full; + let path = 'img/clothes/' + + slot + '/' + + options["worn_" + slot + "_setup"].variable + '/' + + (options["worn_" + slot + "_integrity"]) + + (isHoodDown ? '_down' : isAltPosition ? '_alt' : '') + '.png'; + return gray_suffix(path, options.filters['worn_' + slot]); + }, + showfn(options) { + return options.show_clothes && + options["worn_" + slot] > 0 && + options["worn_" + slot + "_setup"].mainImage !== 0 && + options["worn_" + slot + "_setup"].formfitting === 1 && + ["f", "a"].includes(options.body_type) + }, + alphafn(options) { + return options["worn_" + slot + "_alpha"] + }, + z: ZIndices[slot], + filters: ["worn_" + slot], + animation: "idle" + }, overrideOptions) +} +function genlayer_clothing_fitted_right(slot, overrideOptions) { + return Object.assign({ + srcfn(options) { + let isHoodDown = options.hood_down && + options["worn_" + slot + "_setup"].hoodposition !== undefined && + options["worn_" + slot + "_setup"].outfitPrimary.head !== undefined; + let isAltPosition = options.alt_position && + options["worn_" + slot + "_setup"].altposition !== undefined && + !options.alt_without_full; + let path = 'img/clothes/' + + slot + '/' + + options["worn_" + slot + "_setup"].variable + '/' + + (options["worn_" + slot + "_integrity"]) + + (isHoodDown ? '_down' : isAltPosition ? '_alt' : '') + '.png'; + return gray_suffix(path, options.filters['worn_' + slot]); + }, + showfn(options) { + return options.show_clothes && + options["worn_" + slot] > 0 && + options["worn_" + slot + "_setup"].mainImage !== 0 && + options["worn_" + slot + "_setup"].formfitting === 1 && + options.body_type == "f" + }, + alphafn(options) { + return options["worn_" + slot + "_alpha"] + }, + z: ZIndices[slot], + filters: ["worn_" + slot], + animation: "idle" + }, overrideOptions) +} +function genlayer_clothing_fitted_left_acc(slot, overrideOptions) { + return Object.assign({ + srcfn(options) { + let setup = options["worn_" + slot + "_setup"]; + let isHoodDown = options.hood_down && + setup.hoodposition !== undefined && + setup.outfitPrimary.head !== undefined; + let isAltPosition = options.alt_position && + setup.altposition !== undefined; + let path = 'img/clothes/' + + slot + '/' + + setup.variable + '/' + + 'acc' + + (setup.accessory_integrity_img ? '_' + options["worn_" + slot + "_integrity"] : '') + + (isHoodDown ? '_down' : isAltPosition ? '_alt' : '') + '.png'; + return gray_suffix(path, options.filters['worn_' + slot + '_acc']); + }, + showfn(options) { + return options.show_clothes && + options["worn_" + slot] > 0 && + options["worn_" + slot + "_setup"].accImage !== 0 && + options["worn_" + slot + "_setup"].accessory === 1 && + options["worn_" + slot + "_setup"].formfitting === 1 && + ["f", "a"].includes(options.body_type) + }, + alphafn(options) { + return options["worn_" + slot + "_alpha"] + }, + z: ZIndices[slot], + filters: ["worn_" + slot], + animation: "idle" + }, overrideOptions) +} +function genlayer_clothing_fitted_right_acc(slot, overrideOptions) { + return Object.assign({ + srcfn(options) { + let setup = options["worn_" + slot + "_setup"]; + let isHoodDown = options.hood_down && + setup.hoodposition !== undefined && + setup.outfitPrimary.head !== undefined; + let isAltPosition = options.alt_position && + setup.altposition !== undefined; + let path = 'img/clothes/' + + slot + '/' + + setup.variable + '/' + + 'acc' + + (setup.accessory_integrity_img ? '_' + options["worn_" + slot + "_integrity"] : '') + + (isHoodDown ? '_down' : isAltPosition ? '_alt' : '') + '.png'; + return gray_suffix(path, options.filters['worn_' + slot + '_acc']); + }, + showfn(options) { + return options.show_clothes && + options["worn_" + slot] > 0 && + options["worn_" + slot + "_setup"].accImage !== 0 && + options["worn_" + slot + "_setup"].accessory === 1 && + options["worn_" + slot + "_setup"].formfitting === 1 && + options.body_type == "f" + }, + alphafn(options) { + return options["worn_" + slot + "_alpha"] + }, + z: ZIndices[slot], + filters: ["worn_" + slot], + animation: "idle" + }, overrideOptions) +} function genlayer_clothing_accessory(slot, overrideOptions) { return Object.assign({ srcfn(options) { @@ -3426,6 +4222,7 @@ function genlayer_clothing_accessory(slot, overrideOptions) { showfn(options) { return options.show_clothes && options["worn_" + slot] > 0 && + options["worn_" + slot + "_setup"].accImage !== 0 && options["worn_" + slot + "_setup"].accessory === 1 }, alphafn(options) { @@ -3439,10 +4236,13 @@ function genlayer_clothing_accessory(slot, overrideOptions) { function genlayer_clothing_breasts(slot, overrideOptions) { return Object.assign({ srcfn(options) { + let isAltPosition = options.alt_position && + (options["worn_" + slot + "_setup"].altposition !== undefined && + ["dress shirt"].includes(options["worn_" + slot + "_setup"].name)); let path = 'img/clothes/' + slot + '/' + options["worn_" + slot + "_setup"].variable + '/' + - (Math.min(options.breast_size, 5)) + '.png'; + (Math.min(options.breast_size, 6)) + (isAltPosition ? '_alt' : '') + '.png'; return gray_suffix(path, options.filters['worn_' + slot]); }, z: ZIndices[slot], @@ -3581,7 +4381,7 @@ function genlayer_clothing_belly_split(slot, overrideOptions) { return options["worn_" + slot + "_alpha"] }, dxfn(options) { - if (options.shirt_mask_move_src) { + if (options.shirt_move_right_src) { return -2; } }, @@ -3590,6 +4390,43 @@ function genlayer_clothing_belly_split(slot, overrideOptions) { animation: "idle" }, overrideOptions) } +function genlayer_clothing_belly_split_acc(slot, overrideOptions) { + return Object.assign({ + srcfn(options) { + let setup = options["worn_" + slot + "_setup"]; + let isHoodDown = options.hood_down && + setup.hoodposition !== undefined && + setup.outfitPrimary.head !== undefined; + let isAltPosition = options.alt_position && + setup.altposition !== undefined; + let path = 'img/clothes/' + + slot + '/' + + setup.variable + '/' + + 'acc' + + (setup.accessory_integrity_img ? '_' + options["worn_" + slot + "_integrity"] : '') + + (isHoodDown ? '_down' : isAltPosition ? '_alt' : '') + '.png'; + return gray_suffix(path, options.filters['worn_' + slot + '_acc']); + }, + showfn(options) { + return options.belly > 7 + && options.show_clothes + && options["worn_" + slot] > 0 + && options["worn_" + slot + "_setup"].accessory === 1 + && options["worn_" + slot + "_setup"].mainImage !== 0 + }, + alphafn(options) { + return options["worn_" + slot + "_alpha"] + }, + dxfn(options) { + if (options.shirt_move_right_src) { + return -2; + } + }, + z: ZIndices.bellyClothes, + filters: ["worn_" + slot + "_acc"], + animation: "idle" + }, overrideOptions) +} function genlayer_clothing_belly_shadow(slot, overrideOptions) { return Object.assign({ srcfn(options) { @@ -3695,6 +4532,7 @@ function genlayer_clothing_belly_acc(slot, overrideOptions) { return options.belly > 7 && options.show_clothes && options.worn_upper_setup.pregType != "min" + && !options.shirt_mask_clip_src && options["worn_" + slot] > 0 && options["worn_" + slot + "_setup"].accessory === 1 } else { @@ -3733,7 +4571,7 @@ function genlayer_clothing_breasts_acc(slot, overrideOptions) { let path = 'img/clothes/' + slot + '/' + options["worn_" + slot + "_setup"].variable + '/' + - (Math.min(options.breast_size, 5)) + '_acc.png'; + (Math.min(options.breast_size, 6)) + '_acc.png'; return gray_suffix(path, options.filters['worn_' + slot + '_acc']); }, z: ZIndices[slot], @@ -3754,14 +4592,16 @@ function genlayer_clothing_back_img(slot, overrideOptions) { srcfn(options) { let isAltPosition = options.alt_position && options["worn_" + slot + "_setup"].altposition !== undefined; + let setup = options["worn_" + slot + "_setup"]; let path = 'img/clothes/' + slot + '/' + options["worn_" + slot + "_setup"].variable + '/' + - (isAltPosition ? 'back_alt' : 'back') + '.png'; + (isAltPosition ? 'back_alt' : 'back') + + (setup.back_integrity_img ? '_' + options["worn_" + slot + "_integrity"] : '') + '.png'; return gray_suffix(path, options.filters[this.filtersfn(options)[0]]); }, showfn(options) { - if (!options.show_clothes || (slot === "handheld" && options.arm_right !== "hold")) return false; + if (!options.show_clothes || (slot === "handheld" && options.arm_right === "cover" && options.worn_handheld_setup.coverBackImage === 0)) return false; let isHoodDown = options.hood_down && options["worn_" + slot + "_setup"].hood && options["worn_" + slot + "_setup"].outfitSecondary !== undefined; @@ -3835,8 +4675,10 @@ function genlayer_clothing_arm(arm, slot, overrideOptions) { return Object.assign({ srcfn(options) { let isAltPosition = (options.alt_position && - options["worn_" + slot + "_setup"].altposition !== undefined); + options["worn_" + slot + "_setup"].altposition !== undefined && + !options.alt_without_sleeves); let isAltSleeve = options.alt_sleeve && + options.alt_sleeve_state && options["worn_" + slot + "_setup"].altsleeve !== undefined; let path = 'img/clothes/' + slot + '/' + @@ -3913,3 +4755,100 @@ function genlayer_clothing_arm_acc(arm, slot, overrideOptions) { animation: "idle" }, overrideOptions) } +/** + * Does not setup z-index, it should be in overrideOptions + * + * @param {"left"|"right"} arm + * @param {string} slot + * @param {object?} overrideOptions + */ +function genlayer_clothing_arm_fitted(arm, slot, overrideOptions) { + return Object.assign({ + srcfn(options) { + let isAltPosition = (options.alt_position && + options["worn_" + slot + "_setup"].altposition !== undefined && + !options.alt_without_sleeves); + let isAltSleeve = options.alt_sleeve && + options.alt_sleeve_state && + options["worn_" + slot + "_setup"].altsleeve !== undefined; + let path = 'img/clothes/' + + slot + '/' + + options["worn_" + slot + "_setup"].variable + '/' + + (options["arm_" + arm] === "cover" ? (arm + '_cover') : options.handheld_position && arm === "right" ? "hold" : (arm)) + + (isAltPosition ? "_alt" : "") + + (isAltSleeve ? "_rolled.png" : ".png"); + return gray_suffix(path, options.filters[this.filtersfn(options)[0]]); + }, + showfn(options) { + return options.show_clothes && + options["worn_" + slot] > 0 && + options["worn_" + slot + "_setup"].sleeve_img === 1 && + ["f", "a"].includes(options.body_type) && + options.arm_left === "idle" && + !(options.belly > 7) && + options["arm_" + arm] !== "none" + }, + alphafn(options) { + return options["worn_" + slot + "_alpha"] + }, + filtersfn(options) { + switch (options["worn_" + slot + "_setup"].sleeve_colour) { + case undefined: + case "": + case "primary": + return ["worn_" + slot]; + case "secondary": + return ["worn_" + slot + "_acc"]; + case "no": + default: + return []; + } + }, + animation: "idle" + }, overrideOptions) +} +/** + * Does not setup z-index, it should be in overrideOptions + * + * @param {"left"|"right"} arm + * @param {string} slot + * @param {object?} overrideOptions + */ +function genlayer_clothing_arm_acc_fitted(arm, slot, overrideOptions) { + return Object.assign({ + srcfn(options) { + let path = 'img/clothes/' + + slot + '/' + + options["worn_" + slot + "_setup"].variable + '/' + + (options["arm_" + arm] === "cover" ? (arm + '_cover_acc.png') : options.handheld_position && arm === "right" ? "hold_acc.png" :(arm + "_acc.png")); + return gray_suffix(path, options.filters[this.filtersfn(options)[0]]); + }, + showfn(options) { + return options.show_clothes && + options["worn_" + slot] > 0 && + options["worn_" + slot + "_setup"].sleeve_img === 1 && + options["worn_" + slot + "_setup"].sleeve_acc_img === 1 && + ["f", "a"].includes(options.body_type) && + options.arm_left === "idle" && + !(options.belly > 7) && + options["arm_" + arm] !== "none" + }, + alphafn(options) { + return options["worn_" + slot + "_alpha"] + }, + filtersfn(options) { + switch (options["worn_" + slot + "_setup"].accessory_colour_sidebar) { + case undefined: + case "": + case "primary": + return ["worn_" + slot]; + case "secondary": + return ["worn_" + slot + "_acc"]; + case "no": + default: + return []; + } + }, + animation: "idle" + }, overrideOptions) +} diff --git a/game/04-Variables/colours.js b/game/04-Variables/colours.js index e343bdcfd..a77533432 100644 --- a/game/04-Variables/colours.js +++ b/game/04-Variables/colours.js @@ -910,6 +910,13 @@ setup.colours.clothes = [ csstext: "light-blue", canvasfilter: { blend: "#559BC0" }, }, + { + variable: "neon blue", + name: "neon blue", + name_cap: "Neon Blue", + csstext: "neon-blue", + canvasfilter: { blend: "#00d5ff" }, + }, { variable: "white", name: "white", @@ -931,6 +938,13 @@ setup.colours.clothes = [ csstext: "red", canvasfilter: { blend: "#ff0000" }, }, + { + variable: "jewel red", + name: "jewel red", + name_cap: "Jewel red", + csstext: "jewel-red", + canvasfilter: { blend: "#d4273b" }, + }, { variable: "green", name: "green", @@ -973,6 +987,13 @@ setup.colours.clothes = [ csstext: "purple", canvasfilter: { blend: "#8f09f3" }, }, + { + variable: "lilac", + name: "lilac", + name_cap: "Lilac", + csstext: "lilac", + canvasfilter: { blend: "#d692fc" }, + }, { variable: "tangerine", name: "tangerine", @@ -1015,6 +1036,13 @@ setup.colours.clothes = [ csstext: "brown", canvasfilter: { blend: "#703000" }, }, + { + variable: "soft brown", + name: "soft brown", + name_cap: "Soft brown", + csstext: "brownish", + canvasfilter: { blend: "#6a4225" }, + }, { variable: "tan", name: "tan", @@ -1064,6 +1092,13 @@ setup.colours.clothes = [ csstext: "navy blue", canvasfilter: { blend: "#16168d" }, }, + { + variable: "denim", + name: "denim", + name_cap: "Denim", + csstext: "denim", + canvasfilter: { blend: "#4b6e85" }, + }, { variable: "olive", name: "olive", @@ -1078,6 +1113,13 @@ setup.colours.clothes = [ csstext: "wine", canvasfilter: { blend: "#65252d" }, }, + { + variable: "russet", + name: "russet", + name_cap: "Russet", + csstext: "russet", + canvasfilter: { blend: "#9f4033" }, + }, { variable: "apocalypse", name: "apocalypse", @@ -1120,6 +1162,13 @@ setup.colours.clothes = [ csstext: "silver", canvasfilter: { blend: "#C0C0C0" }, }, + { + variable: "sterling silver", + name: "sterling silver", + name_cap: "Sterling silver", + csstext: "sterling-silver", + canvasfilter: { blend: "#8b9fc4" }, + }, ]; /** * Makeup colour records: diff --git a/game/04-Variables/feats.js b/game/04-Variables/feats.js index f1ad23987..ea8306a00 100644 --- a/game/04-Variables/feats.js +++ b/game/04-Variables/feats.js @@ -40,7 +40,7 @@ setup.feats = { }, "It Belongs in a Museum": { title: "이건 박물관으로 가야해!", - desc: "모든 유물을 찾았다", + desc: "Find all the artefacts", difficulty: 3, series: "", filter: ["All", "General"], @@ -742,7 +742,7 @@ setup.feats = { difficulty: 2, series: "", filter: ["All", "Pregnancy"], - hint: "Hint: 마법이 불가능한 곳에서 생성되는 것을 돕는다.", + hint: "Hint: The Magic that helps create that what should not be.", }, "Diversity of Life": { title: "생명의 다양성", @@ -1480,7 +1480,7 @@ setup.feats = { }, "Field Work": { title: "현장 연구", - desc: "호수에 고고학 연구 사무소를 만들었다.", + desc: "Built an archaeological field office at the lake.", difficulty: 1, series: "", filter: ["All", "Discoveries"], @@ -1861,3 +1861,199 @@ function featsMerge() { } } DefineMacro("featsMerge", featsMerge); + +// eslint-disable-next-line no-unused-vars +function earnHourlyFeats() { + if (V.feats.locked || V.cheatdisable === "f" || V.debug || V.gamemode === "soft" || V.alluremod < 1 || V.replayScene) return false; + + const fragment = document.createDocumentFragment(); + + const earnFeat = featName => { + if (!V.feats.currentSave[featName]) fragment.append(wikifier("earnFeat", `"${featName}"`)); + }; + + // Feats that can only be earned after 50 days + if (Time.days >= 50) { + switch (V.player.gender) { + case "m": + earnFeat("Being a Boy"); + break; + case "f": + earnFeat("Being a Girl"); + break; + case "h": + earnFeat("Being a Hermaphrodite"); + break; + } + + if (Time.days >= 150) earnFeat("Being an Orphan"); + + if (!V.passoutstat) { + earnFeat("Stressful Challenge"); + if (Time.days >= 150) earnFeat("Long Stressful Challenge"); + } + } + + if (V.awareness >= 500) earnFeat("Most Aware"); + if (V.awareness <= -199) earnFeat("Most Innocent"); + if (V.promiscuity >= 100 && V.deviancy >= 100 && V.exhibitionism >= 100) earnFeat("No More Control"); + + if ( + (!V.player.vaginaExist || V.vaginalskill >= 1000) && + (!V.player.penisExist || V.penileskill >= 1000) && + V.oralskill >= 1000 && + (V.analskill >= 1000 || V.analdisable === "t") && + V.handskill >= 1000 && + V.feetskill >= 1000 && + V.bottomskill >= 1000 && + V.thighskill >= 1000 && + V.chestskill >= 1000 + ) { + earnFeat("Sex Specialist"); + } + + if (V.submissive >= 2000) earnFeat("Perfect Sub"); + if (V.submissive <= 0) earnFeat("Defying the Odds"); + if (V.museumAntiques.museumCount === V.museumAntiques.maxCount) earnFeat("It Belongs in a Museum"); + + // LI Romance + const loveCount = (V.robinromance ? 1 : 0) + (V.whitneyromance ? 1 : 0) + (V.kylarenglish ? 1 : 0) + (V.sydneyromance ? 1 : 0); + if (loveCount >= 3) earnFeat("Love Triangles"); + if (loveCount >= 4) earnFeat("Love Trapezoids"); + + if (V.cat >= 6) earnFeat("Neko"); + if (V.wolfgirl >= 6) earnFeat("Wolf"); + if (V.angel >= 6) earnFeat("Angel"); + if (V.fallenangel >= 2) earnFeat("Fallen Angel"); + if (V.demon >= 6) earnFeat("Demon"); + if (V.cow >= 6) earnFeat("Cattle"); + if (V.harpy >= 6) earnFeat("Harpy"); + if (V.fox >= 6) earnFeat("Fox"); + + const specialTraits = + (V.orgasmtrait >= 1 ? 1 : 0) + + (V.ejactrait >= 1 ? 1 : 0) + + (V.molesttrait >= 1 ? 1 : 0) + + (V.rapetrait >= 1 ? 1 : 0) + + (V.bestialitytrait >= 1 ? 1 : 0) + + (V.tentacletrait >= 1 ? 1 : 0) + + (V.voretrait >= 1 ? 1 : 0) + + (V.milkdranktrait >= 1 ? 1 : 0) + + (V.choketrait >= 1 ? 1 : 0); + if (specialTraits >= 1) earnFeat("A Special Trait"); + if (specialTraits >= 9) earnFeat("A Special Trait Collector"); + + if (V.sexStats.anus.pregnancy.motherStatus >= 2 || V.sexStats.vagina.pregnancy.motherStatus >= 2) earnFeat("Broodmother Host"); + if ( + V.pregnancyStats.parasiteTypesSeen && + V.pregnancyStats.parasiteTypesSeen.length >= 14 && + V.pregnancyStats.parasiteVariantsSeen.length >= 2 && + V.pregnancyStats.parasiteBook === 3 + ) { + // typesSeen: fish, snake, slime, spider, maggot, worm, eel, wasp, bee, lurker, squid, slug, tentacle, vine + // variantsSeen: pale, metal + earnFeat("Broodmother Zoologist"); + } + + if (V.spraymax >= 7) earnFeat("Max Those Shots"); + if ((V.semen_volume >= 2000 && V.semen_amount >= V.semen_volume) || (V.milk_volume >= 2000 && V.milk_amount >= V.milk_volume)) earnFeat("Feeling Full"); + if (V.cool >= 400) earnFeat("Social Butterfly"); + if (V.cool <= 2 && !V.backgroundTraits.includes("nerd")) earnFeat("Anti-Social Moth"); + if (V.delinquency <= 0) earnFeat("Teachers Pet"); + if (V.delinquency >= 1000) earnFeat("Teachers Nightmare"); + + if ( + V.skin.forehead.writing && + V.skin.left_cheek.writing && + V.skin.right_cheek.writing && + V.skin.left_shoulder.writing && + V.skin.right_shoulder.writing && + V.skin.breasts.writing && + V.skin.back.writing && + V.skin.pubic.writing && + V.skin.left_thigh.writing && + V.skin.right_thigh.writing + ) { + earnFeat("A Living Canvas"); + } + + if (V.produce_sold >= 100) earnFeat("Hawker"); + if (V.produce_sold >= 1000) earnFeat("Vendor"); + if (V.produce_sold >= 5000) earnFeat("Merchant"); + if (V.plants_known.length >= 17) earnFeat("Seedy"); + if (V.daily.ex.road === 1 && V.daily.ex.cream === 1 && V.daily.ex.flyover === 1) earnFeat("A Lewd Adventure"); + if (V.athletics >= 1000) earnFeat("Swift"); + + if (V.farm_stage && V.farm.beasts.horses >= 20 && V.farm.beasts.cattle >= 20 && V.farm.beasts.dogs >= 20 && V.farm.beasts.pigs >= 20) { + earnFeat("Animal Tender"); + } + + if (V.masochism_level >= 4 && V.sadism_level >= 4) earnFeat("Sadomasochist"); + if (V.masochism_level >= 4) earnFeat("Twisted Desire"); + if (V.sadism_level >= 4) earnFeat("Served Hot"); + + // V.fame.pimp has been excluded, should be added back in if enabled + if ( + V.fame.sex <= 29 && + V.fame.prostitution <= 29 && + V.fame.rape <= 29 && + V.fame.bestiality <= 29 && + V.fame.exhibitionism <= 29 && + V.fame.pregnancy <= 29 && + V.fame.impreg <= 29 && + V.fame.scrap >= 1000 && + V.fame.good >= 1000 && + V.fame.business >= 1000 && + V.fame.social >= 1000 && + V.fame.model >= 1000 + ) { + earnFeat("Shining Reputation"); + } + + if ( + Object.values(V.children).reduce((prev, curr) => { + if (curr.mother === "pc") prev.pushUnique(curr.type); + return prev; + }, []).length >= Object.keys(pregnancyGenerator).filter(type => setup.pregnancy.typesEnabled.includes(type)).length + ) { + earnFeat("Diversity of Life"); + } + + if (V.money >= 100000) earnFeat("Pocket Change"); + if (V.money >= 1000000) earnFeat("Money Maker"); + if (V.money >= 10000000) earnFeat("Tycoon"); + if (V.money >= 100000000) earnFeat("Millionaire"); + + if ( + V.liquidoutsidecount >= 100 && + (V.analdisable === "t" || setup.bodyliquid.combined("anus") >= 5) && + (!V.player.vaginaExist || setup.bodyliquid.combined("vagina") >= 5) && + setup.bodyliquid.combined("mouth") >= 5 + ) { + earnFeat("Fully Covered"); + } + + if (V.skulduggery >= 1000) earnFeat("Thief"); + if (V.danceskill >= 1000) earnFeat("May I have this Dance?"); + if (V.swimmingskill >= 1000) earnFeat("Aquanaut"); + if (V.seductionskill >= 1000) earnFeat("Seductress"); + if (V.tending >= 1000) earnFeat("Green Fingered"); + if (V.housekeeping >= 1000) earnFeat("Majordomo"); + if (V.baseAllure >= 7000 && V.outside === 1 && V.moonstate === 0) earnFeat("Alluring"); + if (V.science >= 1000 && V.maths >= 1000 && V.english >= 1000 && V.history >= 1000) earnFeat("Perfect Record"); + if (V.earSlime.corruption >= 100) earnFeat("Ear Slime Lover"); + if (V.earSlime.corruption >= 100 && V.earSlime.growth >= 200) earnFeat("Ear Slime Amalgam"); + + // To earn the feat "Curious Attire" + fragment.append(wikifier("specialClothesUpdate")); + + // Should be last + let currentMax = 0; + for (let i = 0; i < Object.keys(setup.feats).length; i++) { + currentMax += setup.feats[Object.keys(setup.feats)[i]].difficulty; + } + if (V.feats.allSaves.points >= Math.floor(currentMax * 0.5)) earnFeat("My Collection of Feats"); + if (V.feats.allSaves.points >= Math.floor(currentMax * 0.95)) earnFeat("My Timeless Collection of Feats"); + + return fragment; +} diff --git a/game/04-Variables/hair-defs.js b/game/04-Variables/hair-defs.js index 7f946a410..00cdbaa73 100644 --- a/game/04-Variables/hair-defs.js +++ b/game/04-Variables/hair-defs.js @@ -1,51 +1,79 @@ -/*Add new hairstyles here to indicate how styles will be ruffled*/ +/* Add new hairstyles here to indicate how styles will be ruffled */ setup.hair = { hairtype: [ { name: "default", list: [ "default", - "loose", - "straight", - "swept left", + "all down", + "bedhead", "curl", - "defined curl", - "neat", "curly side up", + "defined curl", + "drill ringlets", + "half-up", "heart braid", + "loose", + "messy bun", + "neat", "ruffled", "sidecut", + "sleek", "space buns", - "messy bun", - "all down", - "half-up" + "straight", + "swept left", ], - devolve: ["ruffled"], + devolve: ["ruffled", "bedhead"], }, { name: "single tail", - list: ["flat ponytail", "ponytail", "side tail left", "side tail right", "fluffy ponytail", "side thicktail", "thick ponytail"], - devolve: ["ponytail"], + list: [ + "flat ponytail", + "fluffy ponytail", + "messy bun", + "ponytail", + "ribbon tail", + "side tail left", + "side tail right", + "thick ponytail", + "thick sidetail", + ], + devolve: ["messy ponytail"], }, { name: "double tail", - list: ["pigtails", "twintails", "curly pigtails", "sailor buns", "loop braid", "thick twintails", "drill ringlets", "low tails", "thick pigtails", "scorpion tails"], + list: [ + "curly pigtails", + "drill ringlets", + "loop braid", + "low tails", + "pigtails", + "sailor buns", + "scorpion tails", + "thick pigtails", + "thick twintails", + "twintails",], devolve: ["twintails"], }, { name: "single braid", - list: ["braid left", "braid right", "left fishtail", "right fishtail"], + list: ["braid left", "braid right", "left fishtail", "right fishtail", "sidetail left", "sidetail right"], devolve: ["braid left"], }, + { + name: "double bun", + list: ["sailor buns", "space buns"], + devolve: ["sailor buns"], + }, { name: "double braid", - list: ["twin braids"], + list: [ "bubble tails", "twin braids", "twin fishtails"], devolve: ["twin braids"], }, { name: "short", - list: ["messy", "short", "short spiky", "layered bob", "french bob"], - devolve: ["messy", "short spiky"], + list: ["french bob", "layered bob", "messy", "short", "short spiky"], + devolve: ["messy", "bedhead"], }, { name: "fro", @@ -55,7 +83,7 @@ setup.hair = { { /* immune to being ruined (because devolve list is empty) */ name: "special", - list: ["dreads", "bubble tails"], + list: ["dreads","shaved"], devolve: [], }, ], @@ -64,40 +92,43 @@ setup.hair = { name: "default", list: [ "default", - "thin flaps", - "wide flaps", + "blunt locks", + "bowl cut", + "buzzcut", + "curtain", + "drill ringlets", + "emo", + "emo left", + "emo right", + "flat", + "framed", + "front braids", "hime", "loose", "messy", + "mohawk", "overgrown", - "ringlets", - "split", - "straight", - "side braid", - "swept left", - "back", "parted", - "flat", "quiff", - "straight curl", - "ringlet curl", - "curtain", - "trident", - "framed", - "sidecut", - "drill ringlets", - "front braids", - "blunt sidelocks", - "short air vents", - "side-pinned", + "ringlet curls", "ruffled", "sectioned", - "bowl", - "emo", - "emo left", - "emo right", + "sidecut", + "side braid", + "side-pinned", + "short air vents", + "sleek", + "split", + "straight", + "straight curls", + "swept back", + "swept left", + "thin flaps", + "tied back", + "trident", + "wide flaps" ], - devolve: ["messy", "trident", "thin flaps", "ruffled"], + devolve: ["messy", "trident", "thin flaps", "ruffled", "bedhead"], }, { /* immune to being ruined (because devolve list is empty) */ diff --git a/game/04-Variables/hair-styles.twee b/game/04-Variables/hair-styles.twee index 91a8d0b4b..8cc8091b7 100644 --- a/game/04-Variables/hair-styles.twee +++ b/game/04-Variables/hair-styles.twee @@ -1,5 +1,7 @@ :: Widgets Hair [widget] -/*Add new hairstyles here. Please put in alphabetical order! */ +/*Add new hairstyles here. Please put in alphabetical order! +alt: Alternate hairstyle to use for certain styles of headwear +alt_head_type: Type of headwear that alt style is used for*/ /*Side Styles*/ <> @@ -33,6 +35,12 @@ variable: "all down", type: ["loose"], shop: ["mirrorhair"], + }, + {name: "bedhead", + name_cap: "Bedhead", + variable: "bedhead", + type: ["loose"], + shop: ["mirrorhair"], }, {name: "braid left", name_cap: "Braid Left", @@ -62,6 +70,8 @@ name_cap: "Curly Pigtails", variable: "curly pigtails", type: ["pigtails", "curly"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "curly side up", @@ -75,6 +85,12 @@ variable: "defined curl", type: ["loose", "curly"], shop: ["mirrorhair"], + }, + {name: "draggletail", + name_cap: "Draggletail", + variable: "messy ponytail", + type: ["ponytail"], + shop: ["mirrorhair"], }, {name: "dreads", name_cap: "Dreads", @@ -98,6 +114,8 @@ name_cap: "Fluffy Ponytail", variable: "fluffy ponytail", type: ["ponytail", "wavy"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "french bob", @@ -110,6 +128,8 @@ name_cap: "Half-up", variable: "half-up", type: ["loose"], + alt: "all down", + alt_head_type: ["hat", "veil"], shop: ["mirrorhair"], }, {name: "heart braids", @@ -134,6 +154,8 @@ name_cap: "Loop Braids", variable: "loop braid", type: ["braids", "pigtails"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "loose", @@ -182,6 +204,8 @@ name_cap: "Ribbon Tail", variable: "ribbon tail", type: ["loose"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "right fishtail", @@ -200,12 +224,16 @@ name_cap: "Sailor Buns", variable: "sailor buns", type: ["buns", "loose"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "scorpion tails", name_cap: "Scorpion Tails", variable: "scorpion tails", type: ["pigtails"], + alt: "bubble tails", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "shaved", @@ -243,6 +271,12 @@ variable: "sidetail right", type: ["ponytail"], shop: ["mirrorhair"], + }, + {name: "sleek", + name_cap: "Sleek", + variable: "sleek", + type: ["loose", "sleek"], + shop: ["mirrorhair"], }, {name: "space buns", name_cap: "Space Buns", @@ -266,24 +300,32 @@ name_cap: "Thick Pigtails", variable: "thick pigtails", type: ["pigtails"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "thick ponytail", name_cap: "Thick Ponytail", variable: "thick ponytail", type: ["ponytail"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "thick sidetail", name_cap: "Thick Sidetail", variable: "thick sidetail", type: ["ponytail"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "thick twintails", name_cap: "Thick Twintails", variable: "thick twintails", type: ["pigtails"], + alt: "shaved", + alt_head_type: ["veil"], shop: ["mirrorhair"], }, {name: "twin braids", @@ -327,6 +369,12 @@ variable: "bowl", type: ["loose", "sleek"], shop: ["mirrorhair"], + }, + {name: "bedhead", + name_cap: "Bedhead", + variable: "bedhead", + type: ["short"], + shop: ["mirrorhair"], }, {name: "buzzcut", name_cap: "Buzzcut", @@ -489,6 +537,12 @@ variable: "short air vents", type: ["loose", "sleek"], shop: ["mirrorhair"], + }, + {name: "sleek", + name_cap: "Sleek", + variable: "sleek", + type: ["loose", "sleek"], + shop: ["mirrorhair"], }, {name: "split", name_cap: "Split", diff --git a/game/04-Variables/npcNamed.twee b/game/04-Variables/npcNamed.twee index 416ee8611..93e023ccf 100644 --- a/game/04-Variables/npcNamed.twee +++ b/game/04-Variables/npcNamed.twee @@ -63,32 +63,32 @@ <> /* .init and .pronoun can't be set within an easy loop */ <> - <> + <> <> <> <> - <> + <> /* do not unset old gender, it is still needed within a loop */ <> /* set new npc attribute vars and remove old ones in a loop */ <> <> - <> + <> <> <> <> <> <> - <> + <> <> <> <> - <> + <> <> <> <> - <> + <> <> <> <> diff --git a/game/04-Variables/presets.twee b/game/04-Variables/presets.twee index 63ec8c53f..812dc4623 100644 --- a/game/04-Variables/presets.twee +++ b/game/04-Variables/presets.twee @@ -62,13 +62,13 @@ <> <> <> - <> + <> <> <> <> <> <> - <> + <> <> <> <> @@ -191,10 +191,14 @@ <>
<> -
- <>: - <> -
+ <> + <> + <> +
+ <>: + <> +
+ <
> <
> <>
@@ -372,7 +376,7 @@ <> <> <> <> - <> + <> <> <> <> diff --git a/game/04-Variables/shop.js b/game/04-Variables/shop.js new file mode 100644 index 000000000..4add67d50 --- /dev/null +++ b/game/04-Variables/shop.js @@ -0,0 +1,288 @@ +setup.shopDetails = { + normal: { + name: "Normal", + desc: "Suitable for everyday use.", + details: "none", + }, + formal: { + name: "Formal", + desc: "Suitable for important occasions, and for good etiquette among certain company.", + details: "Required attire for specific events and fancy dates.", + }, + school: { + name: "School", + desc: "Sanctioned uniform of the local school.", + details: "Upperwear and lowerwear is required attire for attending class. Wearing additional school-trait clothes will slightly reduce delinquency.", + }, + glasses: { + name: "Glasses", + desc: "Helps you focus.", + details: "Increases the rate at which school skills are improved.", + }, + cool: { + name: "Cool", + desc: "Makes you more popular, but some teachers may dislike it.", + details: "Increases the rate at which school status is improved. May cause negative events from teachers who disapprove.", + }, + swim: { + name: "Swim", + desc: "Fares well underwater.", + details: "Helps you succeed at swimming skill checks. Prevents moisture from seeping through your clothes.", + }, + diving: { + name: "Diving", + desc: "Helps keep you warm underwater.", + details: "Prevents stress from rising when swimming in cold water.", + }, + dance: { + name: "Dance", + desc: "Flexible enough to withstand the rigours of the dance floor.", + details: "Won't tear while dancing, while non-dance clothes may be damaged.", + }, + costume: { + name: "Costume", + desc: "People may look at this askance outside the right situations.", + details: "Triggers unique dialogue or actions in certain events.", + }, + serving: { + name: "Serving", + desc: "Encourages tips when working as a bartender, waiter or waitress if visible.", + details: "Increases the amount of money gained from each tip by 20% per item with the trait.", + }, + fetish: { + name: "Fetish", + desc: "Intrinsically lewd.", + details: "Increases the minimum arousal threshold.", + }, + sleep: { + name: "Sleep", + desc: "Soft and cosy. May improve the quality of your sleep.", + details: "Reduces additional fatigue while sleeping. Sleeping in clothes other than sleepwear, underwear, and eerie items will negate this effect.", + }, + mask: { + name: "Mask", + desc: "Conceals your identity. Won't fool the police or people who know you.", + details: "Stops fame from increasing, both positive and negative.", + }, + holy: { + name: "Holy", + desc: "Sacred to the local faith.", + details: "Increases purity gains and losses.", + }, + dark: { + name: "Dark", + desc: "Considered obscene by the local faith.", + details: "Increases awareness gains and losses.", + }, + binding: { + name: "Binding", + desc: "Keeps your arms firmly secure and helpless.", + details: "Prevents you from using your arms.", + }, + stealthy: { + name: "Stealthy", + desc: "Makes your crimes harder to trace.", + details: "Decreases the amount of crime gained from thievery.", + }, + sticky_fingers: { + name: "Sticky fingers", + desc: "More likely to get your way, and keep that which isn't yours.", + details: "Helps you succeed at skulduggery skill checks.", + }, + rainproof: { + name: "Rainproof", + desc: "Protects you from the rain.", + details: "Prevents your clothes from becoming soaked by rain and snow.", + }, + tanLines: { + name: "Tan lines", + desc: "Shields your skin from the sun while tanning.", + details: `Will leave behind tan lines. "Visual representation of the player's and NPCs' skin colour" and "Tanning changes due to sun exposure" must be enabled in the Options menu.`, + }, + bimbo: { + name: "Special", + desc: "Something seems special about this set of clothing.", + details: `Feminises your body type, increases breast and butt size, and reduces penis size. Increases progress towards the "Lustful" trait.`, + }, + himbo: { + name: "Special", + desc: "Protects you from the rain.", + details: `Masculinises your body type, reduces breast and butt size, and increases penis size. Increases progress towards the "Lustful" trait.`, + }, + heels: { + name: "High heeled", + desc: "Difficult to get around in, unless you have skilled feet.", + details: + "Improves kicking power during combat. Reduces travel speed. May cause fatigue and failed athletics checks in the farmlands, moor, and forest.", + }, + rugged: { + name: "Rugged", + desc: "Helps you keep your footing in tough environments.", + details: "Helps you succeed at athletics checks in the farmlands, moor, and forest.", + }, + chest_bind: { + name: "Chest binding", + desc: "Fits tight around your chest, concealing your breasts.", + details: `Reduces your apparent breast size. Breast sizes above "modest" won't appear perfectly flat, and will instead appear to be five sizes smaller.`, + }, + eerie: { + name: "Eerie", + get desc() { + return V.transformdisable === "f" + ? "Protects a specific transformation. Transformations progress and decay at midnight." + : "There's something peculiar about this object."; + }, + get details() { + return V.transformdisable === "f" ? "Prevents its associated transformation from decaying." : "Enable transformations to make use of this trait."; + }, + }, + shade: { + name: "Shade", + desc: "Blocks the sun.", + details: "Protects your skin from passive tanning.", + }, + asylum: { + name: "Asylum", + desc: "Clothes from the local asylum.", + details: "none", + }, + prison: { + name: "Prison", + desc: "Clothes from the local prison.", + details: "none", + }, + sticky: { + name: "Sticky", + desc: "Stuck to your skin.", + details: "Can't be easily removed during combat.", + }, + "strap-on": { + name: "Strap-on", + desc: "Fits around your waist.", + details: "Can be used to penetrate others.", + }, + covered: { + name: "Covered", + desc: "Protects various body parts.", + details: + "Concealed skin cannot be written on. Facewear with this trait protects your mouth against kissing, penetration, and oral activities. Underwear with this trait will not be considered lewd when exposed. Legwear with this trait will cover your undergarments. Lowerwear with this trait will cover your upper half, allowing you to wear them with nothing on top.", + }, + naked: { + name: "Naked", + desc: "So revealing, you may as well be wearing nothing at all.", + details: "Exposes everything.", + }, + athletic: { + name: "Athletic", + desc: "Sporty.", + details: "Increases the rate at which the athletics skill is improved.", + }, + riding: { + name: "Riding", + desc: "Suitable for equestrians.", + details: "Boosts thigh skill gained from horseback riding lessons.", + }, + maid: { + name: "Maid", + desc: "Makes you look the part of a housekeeper.", + details: "Helps you succeed at housekeeping skill checks.", + }, + chastity: { + name: "Chastity", + desc: "Protects your innocence, whether you want it or not.", + details: "Locks away your genitals, preventing use.", + }, + cage: { + name: "Cage", + desc: "Protects your innocence, whether you want it or not.", + details: "Locks away your genitals, preventing use.", + }, + hidden: { + name: "Hidden", + desc: "Keeps your privates private.", + details: "No one can see what kind of genitals you have.", + }, + gag: { + name: "Gag", + desc: "Protects your mouth, but also limits its use.", + details: "Prevents kissing, penetration, and other oral activities. Makes it impossible to speak.", + }, + leash: { + name: "Leash", + desc: "A convenient handle to yank you around with.", + details: "Lets people manhandle you in certain events.", + }, + pushup: { + name: "Push up", + desc: "Makes your breasts appear bigger.", + details: "Makes your breasts appear to be two sizes larger.", + }, + bellyHide: { + name: "Belly hiding", + desc: "Hides pregnant bellies, up to a point.", + details: "none", + }, + bellyShow: { + name: "Belly showing", + desc: "Exposes your belly. Makes pregnancy more obvious.", + details: "none", + }, + constricting: { + name: "Constricting", + desc: "Unable to be worn after a certain point during pregnancy.", + details: "none", + }, + bookbag: { + name: "Bookbag", + desc: "For carrying school textbooks.", + details: "Allows you to study before class. Helps avoid negative school events related to textbooks.", + }, + waterproof: { + name: "Waterproof", + desc: "Impermeable.", + details: "Prevents moisture from seeping through your clothes.", + }, + esoteric: { + name: "Esoteric", + desc: "Reveals what cannot be.", + details: "Enables hallucinations regardless of awareness level, trauma, or hallucinogens.", + }, + unstealthy: { + name: "Unstealthy", + desc: "Makes it more difficult to hide.", + details: "Increases the amount of crime gained during thievery.", + }, +}; + +setup.hairDetails = { + loose: { + desc: "Hair that falls loosely around your face. Grows to be long.", + }, + short: { + desc: "Hair that looks short when styled, even when your hair has grown long.", + }, + curly: { + desc: "Curly hair.", + }, + sleek: { + desc: "Straight hair.", + }, + textured: { + desc: "Afro-textured hair.", + }, + wavy: { + desc: "Not quite curly, not quite straight.", + }, + pigtails: { + desc: "Two bunches of hair that hang on either side of the head.", + }, + buns: { + desc: "Hair that has been coiled around itself and secured in place.", + }, + braids: { + desc: "Interwoven strands of hair. Includes styles that aren't technically braids, such as dreadlocks.", + }, + ponytail: { + desc: "Hair gathered at the back of the head.", + }, +}; diff --git a/game/04-Variables/variables-passageFooter.twee b/game/04-Variables/variables-passageFooter.twee index e95c39fe0..974b8dbf9 100644 --- a/game/04-Variables/variables-passageFooter.twee +++ b/game/04-Variables/variables-passageFooter.twee @@ -57,8 +57,8 @@ <>
이 문제를 제보해 주시고, 이전 세이브를 다시 로드하거나 이 페이지의 아래에 있는 긴급 탈출 링크로 당신이 있었던 마지막 안전 위치로 돌아가세요. - <> - 혹은, 이력 뒤로가기 기능을 사용하여 이전 경로(passage)로 돌아가세요. + <> + Alternatively, return to the previous passage using the history depth function<> that can be enabled in the options<>. <>
<> @@ -76,8 +76,8 @@ 당신이 치트 기능을 사용해 여기에 도달한 것이 아니라면, <
> 이 문제를 제보해 주시고, 이전 세이브를 다시 로드하거나 이 페이지의 아래에 있는 긴급 탈출 링크로 당신이 있었던 마지막 안전 위치로 돌아가세요. - <> - 혹은, 이력 뒤로가기 기능을 사용하여 이전 경로(passage)로 돌아가세요. + <> + Alternatively, return to the previous passage using the history depth function<> that can be enabled in the options<>. <>


<> diff --git a/game/04-Variables/variables-start.twee b/game/04-Variables/variables-start.twee index aae18a73e..4b85b7c06 100644 --- a/game/04-Variables/variables-start.twee +++ b/game/04-Variables/variables-start.twee @@ -43,7 +43,9 @@ lightFlat: 0, lightCombat: 0.2, lightTFColor: 0.2, - maxStates: 1, + maxStates: 5, + maxSessionStates: 1, + historyControls: false, numpad: false, newWardrobeStyle: true, useNarrowMarket: false, @@ -202,6 +204,10 @@ <> <> <> + <> + /* Dupicate stats are intentional to use as a potential gameplay mode */ + <> + <> <> <> <> @@ -293,9 +299,6 @@ <> <> <> - <> - <> - <> <> <> @@ -446,6 +449,8 @@ <> <> <> + <> + <> <> <> <> @@ -470,6 +475,7 @@ <> <> <> + <> <> <> <> @@ -637,7 +643,12 @@ social: 0, model: 0, pregnancy: 0, - impreg: 0 + impreg: 0, + }>> + + <> <> diff --git a/game/04-Variables/variables-start2.twee b/game/04-Variables/variables-start2.twee index 4561dadfe..310fb082e 100644 --- a/game/04-Variables/variables-start2.twee +++ b/game/04-Variables/variables-start2.twee @@ -3,6 +3,8 @@ /*Variables required at the start of passage 'Start2', please remove if not required"*/ /*This widget should be used to initialise most or all variables that are required when you begin a new game. also check <> for other initialisations. */ + <> + <> <> <> @@ -362,9 +364,8 @@ <> <> - - <> - <> + <> + <> <> <> diff --git a/game/04-Variables/variables-static.twee b/game/04-Variables/variables-static.twee index ba3ee4d88..07a748a9a 100644 --- a/game/04-Variables/variables-static.twee +++ b/game/04-Variables/variables-static.twee @@ -107,7 +107,7 @@ "breasts4":"img/bodyRed/breasts/breasts4.png", "breasts4_clothed":"img/bodyRed/breasts/breasts4_clothed.png", "breasts5":"img/bodyRed/breasts/breasts5.png", - "breasts5_clothed":"img/bodyRed/breasts/breasts6_clothed.png", + "breasts5_clothed":"img/bodyRed/breasts/breasts5_clothed.png", "breasts6":"img/bodyRed/breasts/breasts6.png", "breasts6_clothed":"img/bodyRed/breasts/breasts6_clothed.png", "penis-2":"img/bodyRed/penis/penis-2.png", @@ -871,4 +871,19 @@ To be added to canBePregnant - "Great Hawk" To be added to canImpregnatePlayer - "Great Hawk" */ + + <> + <> <> diff --git a/game/04-Variables/variables-versionUpdate.twee b/game/04-Variables/variables-versionUpdate.twee index 98071e1b5..e3f551dd7 100644 --- a/game/04-Variables/variables-versionUpdate.twee +++ b/game/04-Variables/variables-versionUpdate.twee @@ -264,9 +264,9 @@ <> <> - <> - <> - <> + <> + <> + <> <> <> @@ -534,11 +534,11 @@ <> <> - <> + <> <> <> - <> + <> <> <> @@ -923,8 +923,8 @@ <> <> - <> - <> + <> + <> <> <> @@ -2025,8 +2025,8 @@ <> <> - <> - <> + <> + <> <> <> @@ -2701,8 +2701,8 @@ <> - <> - <> + <> + <> <> <> <> @@ -2893,8 +2893,8 @@ <> - <> - <> + <> + <> <> @@ -3032,7 +3032,7 @@ <> <> - <> + <> <> <> <> @@ -3056,7 +3056,7 @@ <> - <> + <> <> <> @@ -3167,13 +3167,13 @@ <> - <> + <> <> <> <> - <> + <> <> <> <> @@ -3211,7 +3211,7 @@ <> - <> + <> <> <> @@ -3300,7 +3300,9 @@ lightFlat: $lightFlat !== undefined ? $lightFlat : 0, lightCombat: $lightCombat !== undefined ? $lightCombat : 0.2, lightTFColor: $lightTFColor !== undefined ? $lightTFColor : 0.2, - maxStates: $maxStates !== undefined ? $maxStates : 1, + maxStates: $maxStates !== undefined ? $maxStates : 5, + maxSessionStates: 1, + historyControls: false, numpad: $numpad !== undefined ? $numpad : false, newWardrobeStyle: $newWardrobeStyle !== undefined ? $newWardrobeStyle : true, useNarrowMarket: $useNarrowMarket !== undefined ? $useNarrowMarket : false, @@ -3317,7 +3319,7 @@ - <> + <> <> <> <> @@ -3571,7 +3573,7 @@ /* Code that should not be moved into a check like above */ <> - <> + <> <> <> @@ -3582,20 +3584,20 @@ <> <> - <> + <> <> - <> + <> <> <> - <> + <> <> - <> + <> <> - <> + <> <> <> @@ -3630,19 +3632,19 @@ <> <> - <> - <> + <> + <> <> <> - <> + <> <> <> <> - <> - <> + <> + <> <> <> - <> + <> <> <> <> @@ -4274,15 +4276,15 @@ const month = Time.monthNames.map(month => month.toLowerCase()).indexOf(V.month) + 1; const date = new DateTime(V.year, month, V.monthday, Math.min(V.hour, 23), Math.min(V.minute, 59), V.seconds); const startDate = new DateTime(date); - T.passMinutes = (V.hour > 23 ? (V.hour - 23) * 60 : 0) + (V.minute > 59 ? V.minute - 59 : 0); - if (V.real_season) V.real_timeStamp = new DateTime(V.real_year, V.real_month, V.real_monthday, V.real_hour, V.real_minute).timeStamp; Time.startDate = startDate.addDays(-V.days); + const passMinutes = (V.hour > 23 ? (V.hour - 23) * 60 : 0) + (V.minute > 59 ? V.minute - 59 : 0); + if (V.real_season) V.real_timeStamp = new DateTime(V.real_year, Time.monthNames.indexOf(V.real_month.toUpperFirst()) + 1, V.real_monthday, V.real_hour, V.real_minute).timeStamp - V.startDate; Time.setDate(date); V.weekDayOffset = (V.weekday - Time.weekDay + 6) % 7; /* stay positive */ if (V.weekDayOffset === 6) delete V.weekDayOffset; /* and clean the rubbish */ ["time", "year", "yeardays", "season", "month", "monthday", "weekday", "schoolday", "days", "daystate", "dayname", "hour", "minute", "seconds", "pass", "real_year", "real_season", "real_month", "real_monthday", "real_hour", "real_minute", "real_time"].forEach(v => delete V[v]); + Time.set(V.timeStamp + passMinutes * 60); <> - <> <> @@ -4432,6 +4434,9 @@ <> <> + <> + <> + <> <> <> <> @@ -4707,7 +4712,7 @@ <> - <> + <> <> <> @@ -4817,4 +4822,252 @@ <><><> /*fix weeksEmpty increasing even when the player hasn't started the vending machine questline. reset vending machine questline if vending machines were sold despite never having started the questline*/ <> + + /* To fix cases where the value could be deleted after starting the game */ + <> + <> + <> + + /*0.4.6.0 Adding arrays for photos and videos taken of the PC that they'll be able to find online*/ + <> + <> + <> + + + <><><> + + <> + <> + <> + + <> + <> + <> + + <> + <> + /* Dupicate stats are intentional to use as a potential gameplay mode */ + <> + <> + <> + + + <> + + + <> + <> + <> + <> + + /*Added npc generation and pronoun widgets to the livestock horse passages*/ + <> + <> + <> + + /*Adds Gift Variable for Office Manager for old saves*/ + <> + <> + <> + + /* Stat tracking for unsafe sex */ + <> + <> + <> + + + <> + <> + <> <><> diff --git a/game/base-clothing/canvasmodel-img.twee b/game/base-clothing/canvasmodel-img.twee index 7d159bf98..47892b038 100644 --- a/game/base-clothing/canvasmodel-img.twee +++ b/game/base-clothing/canvasmodel-img.twee @@ -145,7 +145,12 @@ Requires prior <> --> <> - <> + < hs.variable is $hairtype)>> + <> + <> + <> + <> + <> <> <> <> @@ -161,9 +166,17 @@ Requires prior <> <> <> + <> - <> - <> + + <> + <> + <> + <> + <> + <> + <> + <> <> <> @@ -175,8 +188,15 @@ Requires prior <> <> <> - <> - <> + + <> + <> + <> + <> + <> + <> + <> + <> <> <> @@ -573,12 +593,18 @@ Set model options & filters for player clothes <> <> - <> - <> + <> + <> + <> <> <> + <> + <> + <> + <> + <> <> let slots = [ diff --git a/game/base-clothing/captiontext.twee b/game/base-clothing/captiontext.twee index 17abf6eed..4deeda53b 100644 --- a/game/base-clothing/captiontext.twee +++ b/game/base-clothing/captiontext.twee @@ -321,6 +321,8 @@ <> 당신의 <> 과시하고 있다.`>> <> <> 당신의 <> 가리기에는 너무 크다.`>> + <> + <> is too large for your $worn.lower.name to hide.`>> <> <> 잘 보인다.`>> <> @@ -328,6 +330,8 @@ <> <> <> 당신의 <> 가리고 있다.`>> + <> + <>.`>> <> <> 당신의 <> 가리고 있다.`>> <> diff --git a/game/base-clothing/clothing-face.js b/game/base-clothing/clothing-face.js index c9a5dba55..67cc78e4e 100644 --- a/game/base-clothing/clothing-face.js +++ b/game/base-clothing/clothing-face.js @@ -50,9 +50,10 @@ function initFace() { cost: 5000, description: "Makes studying easier, but you might be picked on at school.", shop: ["clothing", "school"], - accessory: 0, + accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "glasses.png", @@ -82,6 +83,7 @@ function initFace() { accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "cool_shades.png", @@ -247,9 +249,10 @@ function initFace() { cost: 5000, description: "Makes studying easier, but you might be picked on at school.", shop: ["clothing", "school"], - accessory: 0, + accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "low_frame_glasses.png", @@ -275,9 +278,10 @@ function initFace() { cost: 5000, description: "Makes studying easier, but you might be picked on at school.", shop: ["clothing", "school"], - accessory: 0, + accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "half_moon_glasses.png", @@ -303,9 +307,10 @@ function initFace() { cost: 5000, description: "Makes studying easier, but you might be picked on at school.", shop: ["clothing", "school"], - accessory: 0, + accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "deep_frame_glasses.png", @@ -334,7 +339,9 @@ function initFace() { shop: ["clothing"], accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "yellow", "custom"], + accessory_colour_sidebar: 1, + altposition: "none", cursed: 0, location: 0, iconFile: "square_shades.png", @@ -364,6 +371,7 @@ function initFace() { accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "round_shades.png", @@ -390,9 +398,11 @@ function initFace() { cost: 7000, description: "Makes status rise faster at school.", shop: ["clothing"], - accessory: 0, + accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "yellow", "custom"], + accessory_colour_sidebar: 1, + altposition: "none", cursed: 0, location: 0, iconFile: "shield_shades.png", @@ -420,7 +430,9 @@ function initFace() { shop: ["clothing", "adult"], accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "yellow", "custom"], + accessory_colour_sidebar: 1, + altposition: "none", cursed: 0, location: 0, iconFile: "cat_eye_shades.png", @@ -449,7 +461,9 @@ function initFace() { shop: ["clothing", "adult"], accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["original", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "yellow", "custom"], + accessory_colour_sidebar: 1, + altposition: "none", cursed: 0, location: 0, iconFile: "aviators.png", @@ -478,7 +492,9 @@ function initFace() { shop: ["clothing", "adult"], accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "yellow", "custom"], + accessory_colour_sidebar: 1, + altposition: "none", cursed: 0, location: 0, iconFile: "punk_shades.png", @@ -560,9 +576,11 @@ function initFace() { cost: 1500, description: "For keeping your vision clear down below.", shop: ["clothing"], - accessory: 0, + accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_sidebar: 1, + altposition: "none", cursed: 0, location: 0, iconFile: "swimming_goggles.png", @@ -869,7 +887,7 @@ function initFace() { colour: 0, colour_options: [], colour_combat: 0, - type: ["costume", "event", "mask"], + type: ["costume", "mask"], gender: "n", warmth: 0, cost: 2500, @@ -939,7 +957,7 @@ function initFace() { cursed: 0, location: 0, iconFile: "gas_mask.png", - accIcon:"gas_mask_acc.png", + accIcon: "gas_mask_acc.png", }, { index: 33, @@ -965,6 +983,7 @@ function initFace() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "eyepatch.png", @@ -994,6 +1013,7 @@ function initFace() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "medical_eyepatch.png", @@ -1073,7 +1093,7 @@ function initFace() { colour_options: ["black", "grey", "steel", "blue steel", "bronze", "gold", "silver"], colour_sidebar: 1, colour_combat: 0, - type: ["glasses"], + type: ["glasses", "formal"], gender: "n", warmth: 0, cost: 10000, @@ -1082,6 +1102,7 @@ function initFace() { accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "monocle.png", @@ -1136,14 +1157,93 @@ function initFace() { cost: 5000, description: "Makes studying easier, but you might be picked on at school.", shop: ["clothing", "school"], - accessory: 0, + accessory: 1, accessory_colour: 0, accessory_colour_options: [], + altposition: "none", cursed: 0, location: 0, iconFile: "reading_glasses.png", accIcon: 0, }, + { + index: 40, + name: "bandanna", + name_cap: "Bandanna", + variable: "bandanna", + integrity: 20, + integrity_max: 20, + fabric_strength: 20, + reveal: 1, + word: "a", + plural: 1, + colour: 0, + colour_options: ["black", "light blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "yellow", "silver", "custom"], + colour_sidebar: 1, + colour_combat: 0, + type: ["mask", "covered"], + gender: "n", + warmth: 0, + cost: 500, + description: "Hides your identity.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + cursed: 0, + location: 0, + iconFile: "bandanna.png", + accIcon: 0, + }, + { + index: 41, + name: "heart sunglasses", + name_cap: "Heart sunglasses", + variable: "heartsunglasses", + integrity: 30, + integrity_max: 30, + fabric_strength: 20, + reveal: 1, + word: "n", + plural: 1, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + type: ["cool"], + gender: "n", + warmth: 0, + cost: 7000, + description: "Makes status rise faster at school.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: [ + "light pink", + "lilac", + "olive", + "gold", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "neon blue", + "custom", + ], + accessory_colour_sidebar: 1, + altposition: "none", + cursed: 0, + location: 0, + iconFile: "heart_sunglasses.png", + accIcon: "heart_sunglasses_acc.png", + }, ]; /* diff --git a/game/base-clothing/clothing-feet.js b/game/base-clothing/clothing-feet.js index 3587cca41..5113ccd41 100644 --- a/game/base-clothing/clothing-feet.js +++ b/game/base-clothing/clothing-feet.js @@ -24,6 +24,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: 0, accIcon: 0, @@ -47,12 +48,13 @@ function initFeet() { gender: "n", warmth: 10, cost: 2500, - description: "Smart and suitable for school.", + description: "Smart and suitable for school, but not required.", shop: ["clothing", "school"], accessory: 0, accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "school_shoes.png", accIcon: 0, @@ -83,6 +85,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "tuxedo_shoes.png", accIcon: 0, @@ -112,6 +115,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 1, + notuck: 1, location: 0, iconFile: 0, accIcon: 0, @@ -142,6 +146,7 @@ function initFeet() { accessory_colour_options: [], accessory_colour_combat: "brown", cursed: 0, + notuck: 1, location: 0, iconFile: "sandals.png", accIcon: 0, @@ -172,6 +177,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "dress_sandals.png", accIcon: 0, @@ -191,7 +197,7 @@ function initFeet() { colour: 0, colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, - type: ["normal"], + type: ["athletic"], gender: "n", warmth: 10, cost: 3000, @@ -202,6 +208,7 @@ function initFeet() { accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, cursed: 0, + notuck: 1, location: 0, iconFile: "trainers.png", accIcon: "trainers_acc.png", @@ -227,12 +234,13 @@ function initFeet() { femininity: 200, warmth: 20, cost: 4000, - description: "Waterproof.", + description: "Cute and spooky.", shop: ["forest"], accessory: 0, accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "witch_shoes.png", accIcon: "witch_shoes_acc.png", @@ -262,6 +270,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "wellies.png", accIcon: 0, @@ -292,6 +301,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "platform_heels.png", accIcon: 0, @@ -322,6 +332,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "kitten_heels.png", accIcon: 0, @@ -353,6 +364,7 @@ function initFeet() { accessory_colour_options: [], accessory_colour_combat: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "wedge_sandals.png", accIcon: 0, @@ -383,6 +395,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "court_heels.png", accIcon: 0, @@ -413,6 +426,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "heeled_boots.png", accIcon: 0, @@ -444,6 +458,7 @@ function initFeet() { accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, cursed: 0, + notuck: 1, location: 0, iconFile: "stripper_heels.png", accIcon: 0, @@ -474,6 +489,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "horsebit_loafers.png", accIcon: 0, @@ -504,6 +520,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "cordovan_loafers.png", accIcon: 0, @@ -523,7 +540,7 @@ function initFeet() { colour: 0, colour_options: [], colour_combat: "white", - type: ["normal"], + type: ["sleep"], gender: "f", femininity: 200, warmth: 40, @@ -534,6 +551,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "bunny_slippers.png", accIcon: 0, @@ -564,6 +582,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "combat_boots.png", accIcon: 0, @@ -594,6 +613,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "field_boots.png", accIcon: 0, @@ -624,6 +644,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "paddock_boots.png", accIcon: 0, @@ -654,6 +675,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "work_boots.png", accIcon: 0, @@ -684,6 +706,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "flippers.png", accIcon: 0, @@ -714,6 +737,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "ice_skates.png", accIcon: 0, @@ -744,6 +768,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "long_boots.png", accIcon: 0, @@ -773,6 +798,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "light-up_trainers.png", accIcon: "light-up_trainers_acc.png", @@ -802,6 +828,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 1, + notuck: 1, location: 0, iconFile: 0, accIcon: 0, @@ -832,6 +859,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "cowboy_boots.png", accIcon: 0, @@ -861,6 +889,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "trainers.png", accIcon: "trainers_acc.png", @@ -891,6 +920,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 1, location: 0, iconFile: "belly_dancers_shoes.png", accIcon: 0, @@ -922,6 +952,7 @@ function initFeet() { accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, cursed: 0, + notuck: 1, location: 0, iconFile: "canvas_loafers.png", accIcon: "canvas_loafers_acc.png", @@ -951,6 +982,7 @@ function initFeet() { accessory_colour: 0, accessory_colour_options: [], cursed: 0, + notuck: 0, location: 0, iconFile: "thighhigh_heels.png", accIcon: 0, @@ -981,10 +1013,73 @@ function initFeet() { accessory_colour_options: ["white", "brown", "black", "tan", "custom"], accessory_colour_sidebar: 1, cursed: 0, + notuck: 0, location: 0, iconFile: "fur_boots.png", accIcon: "fur_boots_acc.png", }, + { + index: 33, + name: "mary janes", + name_cap: "Mary janes", + variable: "maryjanes", + integrity: 300, + integrity_max: 300, + fabric_strength: 20, + reveal: 200, + word: "n", + plural: 1, + colour: 0, + colour_options: ["black", "brown", "tan", "white", "light pink", "light blue", "lilac", "custom"], + colour_sidebar: 1, + type: ["school"], + gender: "f", + femininity: 200, + warmth: 0, + cost: 2500, + description: "Smart and suitable for school, but not required.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["steel", "blue steel", "bronze", "gold", "silver", "black", "white"], + accessory_colour_sidebar: 1, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "mary_janes.png", + accIcon: "mary_janes_acc.png", + }, + { + index: 34, + name: "platform mary janes", + name_cap: "Platform mary janes", + variable: "platformmaryjanes", + integrity: 300, + integrity_max: 300, + fabric_strength: 20, + reveal: 200, + word: "n", + plural: 1, + colour: 0, + colour_options: ["black", "brown", "tan", "white", "light pink", "light blue", "lilac", "custom"], + colour_sidebar: 1, + type: ["school"], + gender: "f", + femininity: 200, + warmth: 0, + cost: 4500, + description: "Suitable for school, but not required.", + shop: ["clothing", "adult"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["steel", "blue steel", "bronze", "gold", "silver", "black", "white"], + accessory_colour_sidebar: 1, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "platform_mary_janes.png", + accIcon: "platform_mary_janes_acc.png", + }, ]; /* diff --git a/game/base-clothing/clothing-genitals.js b/game/base-clothing/clothing-genitals.js index a2740b2e7..427bdb7b7 100644 --- a/game/base-clothing/clothing-genitals.js +++ b/game/base-clothing/clothing-genitals.js @@ -141,6 +141,8 @@ function initGenitals() { "classic lace panties", "classic briefs", "classic school swimsuit bottom", + "denim panties", + "denim thong", ], altDamage: "metal", iconFile: 0, diff --git a/game/base-clothing/clothing-handheld.js b/game/base-clothing/clothing-handheld.js index 47959147a..1cfbb9fd0 100644 --- a/game/base-clothing/clothing-handheld.js +++ b/game/base-clothing/clothing-handheld.js @@ -44,7 +44,7 @@ function initHandheld() { plural: 0, mask_img: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, type: ["rainproof", "shade"], gender: "n", @@ -55,16 +55,30 @@ function initHandheld() { shop: ["clothing"], accessory: 1, accessory_colour: 0, - accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "neon blue", + "custom", + ], accessory_colour_sidebar: 1, back_img: 1, back_img_acc: 1, back_img_acc_colour: "secondary", - mask_img: 1, cursed: 0, location: 0, iconFile: "umbrella.png", accIcon: "umbrella_acc.png", + coverBackImage: 0, }, { index: 2, @@ -79,7 +93,23 @@ function initHandheld() { plural: 0, mask_img: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "light pink", + "lilac", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "neon blue", + "custom", + ], colour_sidebar: 1, type: ["rainproof", "shade"], gender: "f", @@ -96,11 +126,11 @@ function initHandheld() { back_img: 1, back_img_acc: 1, back_img_acc_colour: "secondary", - mask_img: 1, cursed: 0, location: 0, iconFile: "parasol.png", accIcon: "parasol_acc.png", + coverBackImage: 0, }, { index: 3, @@ -114,7 +144,22 @@ function initHandheld() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "light pink", + "lilac", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, type: ["rainproof", "shade"], gender: "f", @@ -136,6 +181,7 @@ function initHandheld() { location: 0, iconFile: "parasol.png", accIcon: "sweetparasol_acc.png", + coverBackImage: 0, }, { index: 4, @@ -171,6 +217,7 @@ function initHandheld() { location: 0, iconFile: "paperparasol.png", accIcon: "paperparasol_acc.png", + coverBackImage: 0, }, { index: 5, @@ -184,9 +231,25 @@ function initHandheld() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "light pink", + "lilac", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "neon blue", + "custom", + ], colour_sidebar: 1, - type: ["normal", "bookbag"], + type: ["school", "bookbag"], gender: "f", femininity: 100, warmth: 0, @@ -214,9 +277,25 @@ function initHandheld() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "light pink", + "lilac", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "neon blue", + "custom", + ], colour_sidebar: 1, - type: ["normal", "bookbag"], + type: ["school", "bookbag"], gender: "f", femininity: 100, warmth: 0, @@ -244,9 +323,9 @@ function initHandheld() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, - type: ["normal", "bookbag"], + type: ["school", "bookbag"], gender: "m", femininity: -100, warmth: 0, @@ -274,9 +353,9 @@ function initHandheld() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, - type: ["normal", "bookbag"], + type: ["school", "bookbag"], gender: "n", femininity: 0, warmth: 0, @@ -307,7 +386,7 @@ function initHandheld() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, type: ["normal"], gender: "n", @@ -334,7 +413,7 @@ function initHandheld() { plural: 0, colour: 0, colour_options: [], - colour_sidebar: 1, + colour_sidebar: 0, type: ["prop"], shop: [], gender: "n", @@ -353,7 +432,7 @@ function initHandheld() { plural: 0, colour: 0, colour_options: [], - colour_sidebar: 1, + colour_sidebar: 0, type: ["prop"], shop: [], gender: "n", @@ -407,7 +486,7 @@ function initHandheld() { colour: 0, colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "pale tangerine", "teal", "pale white", "pale yellow", "custom"], colour_sidebar: 1, - type: ["costume", "athletic"], + type: ["costume"], gender: "f", femininity: 200, warmth: 15, @@ -436,7 +515,7 @@ function initHandheld() { word: "a", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, type: ["costume"], gender: "n", @@ -464,7 +543,7 @@ function initHandheld() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, type: ["costume"], gender: "n", @@ -497,6 +576,7 @@ function initHandheld() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], + coverImage: 0, }, { index: 17, @@ -515,6 +595,7 @@ function initHandheld() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], + coverImage: 0, }, { index: 18, @@ -551,6 +632,7 @@ function initHandheld() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], + coverImage: 0, }, { index: 20, @@ -569,6 +651,303 @@ function initHandheld() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], + coverImage: 0, + }, + { + index: 21, + name: "beer bottle", + name_cap: "Beer bottle", + variable: "beerbottle", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Alcoholic.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + coverImage: 0, + }, + { + index: 22, + name: "mug of beer", + name_cap: "Mug of beer", + variable: "beermug", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Alcoholic.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + coverImage: 0, + }, + { + index: 23, + name: "shot glass", + name_cap: "Shot glass", + variable: "shotglass", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Alcoholic.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + coverImage: 0, + }, + { + index: 24, + name: "wine glass", + name_cap: "Wine glass", + variable: "wine", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Alcoholic.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + coverImage: 0, + }, + { + index: 25, + name: "torch", + name_cap: "Torch", + variable: "torch", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Burn, baby, burn.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + coverImage: 0, + }, + { + index: 26, + name: "gym bag", + name_cap: "Gym bag", + variable: "gymbag", + integrity: 120, + integrity_max: 120, + fabric_strength: 10, + reveal: 200, + word: "a", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], + colour_sidebar: 1, + type: ["school", "bookbag"], + gender: "m", + femininity: -100, + warmth: 0, + cost: 4000, + description: "Useful for carrying school textbooks, if you don't mind them smelling like gym socks.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "neon blue", + "custom", + ], + accessory_colour_sidebar: 1, + cursed: 0, + location: 0, + iconFile: "gym_bag.png", + accIcon: "gym_bag_acc.png", + }, + { + index: 27, + name: "cup of tea", + name_cap: "Cup of tea", + variable: "tea", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Hot, because iced tea is for heathens.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + }, + { + index: 28, + name: "cup of coffee", + name_cap: "Cup of coffee", + variable: "coffee", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Hot.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + }, + { + index: 29, + name: "forkful of salad", + name_cap: "Forkful of salad", + variable: "salad", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Rabbit food.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + }, + { + index: 30, + name: "forkful of pancake", + name_cap: "Forkful of pancake", + variable: "pancake", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Smothered in syrup.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + }, + { + index: 31, + name: "forkful of pasta", + name_cap: "Forkful of pasta", + variable: "pasta", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "For when your mom comes home and makes the spaghet. If you had one.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + }, + { + index: 32, + name: "fork", + name_cap: "Fork", + variable: "fork", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Cutlery.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + }, + { + index: 33, + name: "spoon", + name_cap: "Spoon", + variable: "spoon", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Cutlery.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + }, + { + index: 34, + name: "cream bun", + name_cap: "Cream bun", + variable: "creambun", + word: "a", + plural: 0, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["prop"], + shop: [], + gender: "n", + description: "Unsullied by lewd fluids. Probably.", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, }, ]; diff --git a/game/base-clothing/clothing-hands.js b/game/base-clothing/clothing-hands.js index 8a77b56e4..99242024e 100644 --- a/game/base-clothing/clothing-hands.js +++ b/game/base-clothing/clothing-hands.js @@ -147,6 +147,7 @@ function initHands() { colour_sidebar: 1, type: ["formal"], gender: "f", + femininity: 200, warmth: 5, cost: 2500, description: "Fashionable.", @@ -176,8 +177,26 @@ function initHands() { word: "n", plural: 1, colour: 0, - colour_options: [], - colour_sidebar: 0, + colour_options: [ + "black", + "wine", + "navy blue", + "silver", + "gold", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "brown", + "custom", + ], + colour_sidebar: 1, + colour_combat: 0, type: ["normal", "sticky_fingers", "stealthy"], gender: "n", warmth: 10, @@ -211,7 +230,7 @@ function initHands() { colour: 0, colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "pale tangerine", "teal", "pale white", "pale yellow", "custom"], colour_sidebar: 1, - type: ["costume", "athletic"], + type: ["costume"], gender: "f", femininity: 200, warmth: 15, @@ -328,6 +347,70 @@ function initHands() { leftImage: 1, rightImage: 1, }, + { + index: 10, + name: "sexy nun's gloves", + name_cap: "Sexy nun's gloves", + variable: "nunlewd", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 100, + word: "a", + plural: 1, + colour: 0, + colour_options: [], + colour_sidebar: 0, + type: ["holy", "costume", "fetish"], + gender: "f", + warmth: 10, + cost: 3000, + description: "Great for rubbing out sin.", + shop: ["forest"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + back_img: 0, + cursed: 0, + location: 0, + iconFile: "sexy_nuns_gloves.png", + accIcon: 0, + mainImage: 0, + leftImage: 1, + rightImage: 1, + }, + { + index: 11, + name: "wrist cuffs", + name_cap: "Wrist cuffs", + variable: "wristcuffs", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 500, + word: "n", + plural: 1, + colour: 0, + colour_options: [], + type: ["costume", "serving"], + gender: "n", + femininity: 0, + warmth: 0, + cost: 800, + description: "Sexy enough for a playboy bunny, classy enough for a butler.", + shop: ["adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + back_img: 0, + cursed: 0, + location: 0, + iconFile: "wrist_cuffs.png", + accIcon: 0, + mainImage: 0, + leftImage: 1, + rightImage: 1, + }, ]; /* diff --git a/game/base-clothing/clothing-head.js b/game/base-clothing/clothing-head.js index 5f925d0e4..ff8e609ec 100644 --- a/game/base-clothing/clothing-head.js +++ b/game/base-clothing/clothing-head.js @@ -1,4 +1,5 @@ /* For any item that has a colour_combat tag, set it to 0 if that item ever gets its own combat sprites. */ +/* head_type should only be added to headwear that doesn't play well with certain hairstyles. Hairstyles whose alt_head_type matches the head_type of the worn head item will use an alternate hairstyle instead. */ function initHead() { setup.clothes.head = [ { @@ -150,6 +151,7 @@ function initHead() { accessory_colour_options: [], back_img: 1, back_img_colour: "primary", + head_type: "veil", cursed: 0, location: 0, iconFile: "nuns_veil.png", @@ -168,7 +170,7 @@ function initHead() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "pale yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "lilac", "red", "tangerine", "teal", "pale white", "pale yellow", "custom"], colour_sidebar: 1, type: ["normal"], gender: "f", @@ -328,7 +330,8 @@ function initHead() { plural: 0, mask_img: 1, colour: 0, - colour_options: [], + colour_options: ["tan", "sand", "brown", "white", "black", "light pink", "light blue", "russet", "grey", "olive", "wine", "custom"], + colour_sidebar: 1, colour_combat: 0, type: ["costume", "shade"], gender: "n", @@ -336,15 +339,16 @@ function initHead() { cost: 8000, description: "Protects you from the sun.", shop: ["clothing", "adult"], - accessory: 0, + accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["tan", "sand", "brown", "white", "black", "light pink", "light blue", "russet", "grey", "olive", "wine", "custom"], + accessory_colour_sidebar: 1, back_img: 1, back_img_colour: "primary", cursed: 0, location: 0, iconFile: "cowboy_hat.png", - accIcon: 0, + accIcon: "cowboy_hat_acc.png", }, { @@ -495,6 +499,7 @@ function initHead() { accessory_colour: 0, accessory_colour_options: [], back_img: 0, + head_type: "hat", cursed: 0, location: 0, iconFile: "backwards_cap.png", @@ -554,12 +559,14 @@ function initHead() { cost: 700, description: "Keeps the sun off.", shop: ["clothing"], + shopGroup: "straw", accessory: 1, accessory_colour: 0, - accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "lilac", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, back_img: 1, back_img_colour: "primary", + head_type: "hat", cursed: 0, location: 0, iconFile: "straw_hat.png", @@ -587,13 +594,15 @@ function initHead() { warmth: 0, cost: 1500, description: "Keeps the sun off.", - shop: [], + shop: ["clothing"], + shopGroup: "straw", accessory: 1, accessory_colour: 0, - accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "lilac", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, back_img: 1, back_img_colour: "primary", + head_type: "hat", cursed: 0, location: 0, iconFile: 0, @@ -770,7 +779,6 @@ function initHead() { reveal: 1, word: "a", plural: 0, - mask_img: 1, colour: 0, colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, @@ -794,8 +802,8 @@ function initHead() { { index: 25, - name: "football helmet", - name_cap: "Football helmet", + name: "foreign football helmet", + name_cap: "Foreign football helmet", variable: "football", integrity: 200, integrity_max: 200, @@ -808,7 +816,7 @@ function initHead() { colour: 0, colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, - type: ["costume", "athletic"], + type: ["costume"], gender: "m", femininity: -200, warmth: 30, @@ -873,7 +881,7 @@ function initHead() { colour: 0, colour_options: [], colour_combat: 0, - type: ["costume", "athletic", "riding"], + type: ["costume", "riding"], gender: "n", femininity: 0, warmth: 30, @@ -974,7 +982,7 @@ function initHead() { colour: 0, colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, - type: ["costume", "athletic", "riding"], + type: ["costume", "riding"], gender: "n", femininity: 0, warmth: 20, @@ -1149,6 +1157,7 @@ function initHead() { cost: 3000, description: "Whiskers not included.", shop: ["clothing"], + shopGroup: "cathat", accessory: 1, accessory_colour: 0, accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], @@ -1157,7 +1166,7 @@ function initHead() { cursed: 0, location: 0, iconFile: "cat_hat.png", - accIcon: 0, + accIcon: "cat_hat_acc.png", }, { @@ -1308,7 +1317,7 @@ function initHead() { colour: 0, colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, - type: ["athletic", "shade"], + type: ["shade"], gender: "n", warmth: 0, cost: 6000, @@ -1439,6 +1448,7 @@ function initHead() { cost: 1200, description: "Worn by nurses at the hospital.", shop: ["clothing"], + shopGroup: "nursehat", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -1470,6 +1480,7 @@ function initHead() { cost: 1800, description: "Easy to clean.", shop: ["clothing"], + shopGroup: "nursehat", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -1614,6 +1625,8 @@ function initHead() { reveal: 1, word: "a", plural: 0, + mask_img: 1, + mask_img_ponytail: 1, colour: 0, colour_combat: 0, colour_options: ["black", "brown", "grey", "tan", "sand", "custom"], @@ -1630,6 +1643,7 @@ function initHead() { accessory_colour_options: ["black", "brown", "grey", "tan", "sand", "custom"], accessory_colour_sidebar: 1, back_img: 0, + head_type: "hat", cursed: 0, location: 0, iconFile: "furcapm.png", @@ -1647,9 +1661,12 @@ function initHead() { reveal: 1, word: "a", plural: 0, + mask_img: 1, + mask_img_ponytail: 1, colour: 0, colour_combat: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "yellow", "custom"], + // eslint-disable-next-line prettier/prettier + colour_options: ["black", "navy blue", "wine", "blue", "brown", "green", "pink", "light pink", "purple", "lilac", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, type: ["normal"], gender: "f", @@ -1660,9 +1677,11 @@ function initHead() { shop: ["clothing"], accessory: 1, accessory_colour: 0, - accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "yellow", "custom"], + // eslint-disable-next-line prettier/prettier + accessory_colour_options: ["black", "navy blue", "wine", "blue", "brown", "green", "pink", "light pink", "purple", "lilac", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, back_img: 0, + head_type: "hat", cursed: 0, location: 0, iconFile: "furcapf.png", @@ -1715,6 +1734,7 @@ function initHead() { hood: 1, mask_img: 1, colour: 0, + // eslint-disable-next-line prettier/prettier colour_options: ["black", "blue steel", "grey", "white", "light pink", "light blue", "light green", "sand", "red", "pink", "purple", "tangerine", "teal", "custom"], colour_sidebar: "primary", type: ["normal"], @@ -1761,7 +1781,10 @@ function initHead() { accessory_colour: 0, accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, - back_img: 0, + back_img: 1, + back_img_acc: 1, + back_img_acc_colour: "secondary", + head_type: "veil", cursed: 0, location: 0, iconFile: "bat_beanie.png", @@ -1812,7 +1835,7 @@ function initHead() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "lilac", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, colour_combat: 0, type: ["normal"], @@ -1866,6 +1889,286 @@ function initHead() { iconFile: "star_hairpin.png", accIcon: "star_hairpin_acc.png", }, + { + index: 57, + name: "durag", + name_cap: "Durag", + variable: "durag", + integrity: 50, + integrity_max: 50, + fabric_strength: 20, + reveal: 1, + word: "a", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + colour_combat: 0, + type: ["normal"], + gender: "m", + femininity: -100, + warmth: 10, + cost: 500, + description: "Protects your hair.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + mask_img: 1, + mask_img_ponytail: 0, + back_img: 1, + back_img_colour: "primary", + head_type: "hat", + cursed: 0, + location: 0, + iconFile: "bandanna.png", + accIcon: 0, + }, + { + index: 58, + name: "kittycat hat", + name_cap: "kittycat hat", + variable: "cattail", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 100, + word: "a", + plural: 0, + mask_img: 1, + mask_img_ponytail: 1, + hood: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + type: ["costume", "shade"], + gender: "n", + warmth: 25, + cost: 3000, + description: "Whiskers included.", + shop: ["clothing"], + shopGroup: "cathat", + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_sidebar: 1, + back_img: 0, + cursed: 0, + location: 0, + iconFile: "cat_hat.png", + accIcon: "cat_hat_acc.png", + }, + { + index: 59, + name: "sexy nun's veil", + name_cap: "Sexy nun's veil", + variable: "nunlewd", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 100, + word: "a", + plural: 0, + mask_img: 1, + colour: 0, + colour_options: [], + type: ["holy", "costume", "fetish"], + gender: "f", + femininity: 200, + warmth: 10, + cost: 5000, + description: "Won't keep your face clean while you're down and dirty.", + shop: ["forest"], + shopGroup: "nunlewd", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + back_img: 1, + back_img_colour: "primary", + head_type: "veil", + cursed: 0, + location: 0, + iconFile: "sexy_nuns_veil.png", + accIcon: 0, + }, + { + index: 60, + name: "sexy nun's ornate veil", + name_cap: "Sexy nun's ornate veil", + variable: "nunlewdornate", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 100, + word: "a", + plural: 0, + mask_img: 1, + colour: 0, + colour_options: [], + type: ["holy", "costume", "fetish"], + gender: "f", + femininity: 200, + warmth: 10, + cost: 5000, + description: "Won't keep your face clean while you're down and dirty.", + shop: ["forest"], + shopGroup: "nunlewd", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + back_img: 1, + back_img_colour: "primary", + head_type: "veil", + cursed: 0, + location: 0, + iconFile: "sexy_nuns_veil.png", + accIcon: 0, + }, + { + index: 61, + name: "newsboy cap", + name_cap: "Newsboy cap", + variable: "newsboy", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 1, + word: "a", + plural: 0, + mask_img: 1, + colour: 0, + colour_options: [ + "russet", + "sand", + "grey", + "wine", + "olive", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + colour_combat: 0, + colour_sidebar: 1, + type: ["normal"], + gender: "m", + femininity: -100, + warmth: 10, + cost: 500, + description: "For poor little street urchins.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + back_img: 0, + head_type: "hat", + cursed: 0, + location: 0, + iconFile: "newsboy_cap.png", + }, + { + index: 62, + name: "visor", + name_cap: "Visor", + variable: "visor", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 1, + word: "a", + plural: 0, + mask_img: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_combat: 0, + colour_sidebar: 1, + type: ["normal", "shade"], + gender: "n", + femininity: 0, + warmth: 0, + cost: 700, + description: "Keeps the sun out of your eyes.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_sidebar: 1, + back_img: 0, + cursed: 0, + location: 0, + iconFile: "visor.png", + accIcon: "visor_acc.png", + }, + { + index: 63, + name: "lolita headband", + name_cap: "Lolita headband", + variable: "lolita", + integrity: 50, + integrity_max: 50, + fabric_strength: 20, + reveal: 1, + word: "a", + plural: 0, + colour: 0, + colour_options: [ + "light pink", + "lilac", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + colour_sidebar: 1, + type: ["costume"], + gender: "f", + femininity: 200, + warmth: 0, + cost: 3100, + description: "Holds your hair back.", + shop: ["clothing", "adult"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: [ + "light pink", + "lilac", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + accessory_colour_sidebar: 1, + back_img: 0, + cursed: 0, + location: 0, + iconFile: "lolita_headband.png", + accIcon: "lolita_headband_acc.png", + }, ]; /* diff --git a/game/base-clothing/clothing-legs.js b/game/base-clothing/clothing-legs.js index 737b5d7a5..1e460a5bb 100644 --- a/game/base-clothing/clothing-legs.js +++ b/game/base-clothing/clothing-legs.js @@ -80,12 +80,12 @@ function initLegs() { colour: 0, colour_options: [], colour_combat: 0, - type: ["school"], + type: ["school", "athletic"], gender: "m", femininity: -100, warmth: 15, cost: 500, - description: "Cushioned and breathable.", + description: "Cushioned and breathable. Suitable for school, but not required.", shop: ["clothing", "school", "adult"], accessory: 0, accessory_colour: 0, @@ -112,12 +112,12 @@ function initLegs() { colour: 0, colour_options: [], colour_combat: 0, - type: ["school"], + type: ["school", "athletic"], gender: "f", femininity: 100, warmth: 15, cost: 500, - description: "Cushioned and breathable.", + description: "Cushioned and breathable. Suitable for school, but not required.", shop: ["clothing", "school", "adult"], accessory: 0, accessory_colour: 0, @@ -238,7 +238,26 @@ function initLegs() { state_base: "thighs", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "pale white", "pale yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "brown", + "wine", + "grey", + "light pink", + "lilac", + "soft brown", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "pale white", + "pale yellow", + "custom", + ], colour_sidebar: 1, colour_combat: 0, type: ["normal"], @@ -271,7 +290,7 @@ function initLegs() { state_base: "thighs", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, colour_combat: 0, type: ["normal"], @@ -627,7 +646,7 @@ function initLegs() { state_base: "knees", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, type: ["normal"], gender: "m", @@ -638,7 +657,21 @@ function initLegs() { shop: ["clothing"], accessory: 1, accessory_colour: 0, - accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "neon blue", + "custom", + ], accessory_colour_sidebar: 1, cursed: 0, location: 0, @@ -747,7 +780,7 @@ function initLegs() { { index: 23, - name: "Rib-knit ankle socks", + name: "rib-knit ankle socks", name_cap: "Rib-knit ankle socks", variable: "striped socks short", integrity: 100, @@ -780,7 +813,7 @@ function initLegs() { { index: 24, - name: "Striped kneehighs", + name: "striped kneehighs", name_cap: "Striped kneehighs", variable: "striped kneehighs", integrity: 100, @@ -797,7 +830,7 @@ function initLegs() { type: ["normal"], gender: "n", femininity: 0, - warmth: 20, + warmth: 15, cost: 2000, description: "Brimming with personality.", shop: ["clothing"], @@ -825,7 +858,26 @@ function initLegs() { state_base: "waist", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "brown", + "wine", + "grey", + "light pink", + "lilac", + "soft brown", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "pale white", + "pale yellow", + "custom", + ], colour_sidebar: 1, type: ["normal", "dance", "athletic"], gender: "f", @@ -856,7 +908,7 @@ function initLegs() { state_base: "ankles", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, type: ["normal"], gender: "n", @@ -888,7 +940,8 @@ function initLegs() { state_base: "ankles", plural: 1, colour: 0, - colour_options: [], + colour_options: ["white", "black", "grey", "lilac", "light pink", "light blue", "custom"], + colour_sidebar: 1, type: ["school"], gender: "f", femininity: 100, @@ -904,6 +957,203 @@ function initLegs() { iconFile: "loose_socks.png", accIcon: 0, }, + { + index: 28, + name: "sexy nun's stockings", + name_cap: "Sexy nun's stockings", + variable: "nunlewd", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 500, + word: "a", + state: "knees", + state_base: "knees", + plural: 1, + colour: 0, + colour_options: ["white", "black", "bronze"], + colour_sidebar: 1, + type: ["holy", "costume", "fetish"], + gender: "f", + femininity: 200, + warmth: 10, + cost: 2000, + description: "For looking your Sunday best while on your knees.", + shop: ["forest"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["white", "black", "bronze"], + accessory_colour_sidebar: 1, + cursed: 0, + location: 0, + iconFile: "sexy_nuns_stockings.png", + accIcon: "sexy_nuns_stockings_acc.png", + }, + { + index: 29, + name: "leather leggings", + name_cap: "Leather leggings", + variable: "leatherleggings", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 600, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 0, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: ["black", "brown", "silver", "gold", "blue", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + colour_combat: "black", + type: ["normal", "covered"], + gender: "n", + warmth: 25, + cost: 3500, + description: "Tight.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + cursed: 0, + location: 0, + iconFile: "leather_leggings.png", + accIcon: 0, + }, + { + index: 30, + name: "men's garter socks", + name_cap: "Men's garter socks", + variable: "mensgarters", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 100, + word: "n", + state: "knees", + state_base: "knees", + plural: 1, + colour: 0, + colour_options: ["black", "brown", "tan", "white", "navy blue", "grey", "olive", "custom"], + colour_sidebar: 1, + type: ["normal"], + gender: "m", + femininity: -200, + warmth: 20, + cost: 4000, + description: "Sexy and functional.", + shop: ["clothing", "adult"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["steel", "blue steel", "bronze", "gold", "silver", "black", "white"], + accessory_colour_sidebar: 1, + cursed: 0, + location: 0, + iconFile: "mens_garters.png", + accIcon: "mens_garters_acc.png", + }, + { + index: 31, + name: "ruffled socks", + name_cap: "Ruffled socks", + variable: "ruffled socks", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 200, + word: "n", + state: "ankles", + state_base: "ankles", + plural: 1, + colour: 0, + colour_options: ["black", "wine", "brown", "tan", "white", "light pink", "light blue", "lilac", "custom"], + colour_sidebar: 1, + type: ["normal"], + gender: "f", + femininity: 200, + warmth: 10, + cost: 800, + description: "Frilly.", + shop: ["clothing", "adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + cursed: 0, + location: 0, + iconFile: "ruffled_socks.png", + accIcon: 0, + }, + { + index: 32, + name: "ruffled kneehighs", + name_cap: "Ruffled kneehighs", + variable: "ruffled kneehighs", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 200, + word: "n", + state: "knees", + state_base: "knees", + plural: 1, + colour: 0, + colour_options: ["black", "wine", "brown", "tan", "white", "light pink", "light blue", "lilac", "custom"], + colour_sidebar: 1, + type: ["normal"], + gender: "f", + femininity: 200, + warmth: 15, + cost: 1200, + description: "Frilly.", + shop: ["clothing", "adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + cursed: 0, + location: 0, + iconFile: "ruffled_kneehighs.png", + accIcon: 0, + }, + { + index: 33, + name: "ruffled thighhighs", + name_cap: "Ruffled thighhighs", + variable: "ruffled thighhighs", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 200, + word: "n", + state: "thighs", + state_base: "thighs", + plural: 1, + colour: 0, + colour_options: ["black", "wine", "brown", "tan", "white", "light pink", "light blue", "lilac", "custom"], + colour_sidebar: 1, + type: ["normal"], + gender: "f", + femininity: 200, + warmth: 20, + cost: 2000, + description: "Frilly.", + shop: ["clothing", "adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + cursed: 0, + location: 0, + iconFile: "ruffled_thighhighs.png", + accIcon: 0, + }, ]; /* diff --git a/game/base-clothing/clothing-lower.js b/game/base-clothing/clothing-lower.js index 6a0a140a3..f22d07c5d 100644 --- a/game/base-clothing/clothing-lower.js +++ b/game/base-clothing/clothing-lower.js @@ -40,6 +40,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: 0, accIcon: 0, @@ -87,6 +88,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "sundress.png", accIcon: 0, @@ -106,7 +108,7 @@ function initLower() { word: "n", one_piece: 0, skirt: 0, - skirt_down: 1, + skirt_down: 0, short: 0, state: "waist", state_base: "waist", @@ -133,6 +135,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "pyjama_bottoms.png", accIcon: 0, @@ -178,6 +181,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "towel.png", accIcon: 0, @@ -223,6 +227,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "waist_apron.png", accIcon: 0, @@ -241,7 +246,7 @@ function initLower() { word: "n", one_piece: 0, skirt: 0, - skirt_down: 1, + skirt_down: 0, short: 1, state: "waist", state_base: "waist", @@ -268,6 +273,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "shorts.png", accIcon: 0, @@ -286,7 +292,7 @@ function initLower() { word: "n", one_piece: 0, skirt: 0, - skirt_down: 1, + skirt_down: 0, short: 1, state: "waist", state_base: "waist", @@ -315,6 +321,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "school_shorts.png", accIcon: 0, @@ -362,6 +369,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "school_skirt.png", accIcon: 0, @@ -407,6 +415,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: 0, accIcon: 0, @@ -431,7 +440,24 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -453,6 +479,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "evening_gown.png", accIcon: 0, @@ -478,7 +505,24 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -497,9 +541,10 @@ function initLower() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], - high_img: 1, + high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "ballgown.png", accIcon: 0, @@ -547,6 +592,7 @@ function initLower() { back_img: 1, back_img_colour: "primary", cursed: 0, + notuck: 1, location: 0, iconFile: "kimono.png", accIcon: 0, @@ -594,6 +640,7 @@ function initLower() { back_img: 1, back_img_colour: "primary", cursed: 0, + notuck: 1, location: 0, iconFile: "mini_kimono.png", accIcon: 0, @@ -641,6 +688,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "maid_dress.png", accIcon: 0, @@ -689,6 +737,7 @@ function initLower() { back_img: 1, back_img_colour: "primary", cursed: 0, + notuck: 1, location: 0, iconFile: "nuns_habit.png", accIcon: 0, @@ -735,6 +784,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "towel.png", accIcon: 0, @@ -782,6 +832,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "tuxedo_trousers.png", accIcon: 0, @@ -827,6 +878,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "board_shorts.png", accIcon: 0, @@ -873,6 +925,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "breeches.png", accIcon: 0, @@ -919,6 +972,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "long_cut_skirt.png", accIcon: 0, @@ -965,6 +1019,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "short_cut_skirt.png", accIcon: 0, @@ -1010,6 +1065,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "cycle_shorts.png", accIcon: 0, @@ -1034,8 +1090,9 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: [], + colour_options: ["original", "denim", "light blue", "black", "blue steel", "grey", "custom"], colour_combat: "blue", + colour_sidebar: 1, exposed: 0, exposed_base: 0, vagina_exposed: 0, @@ -1055,6 +1112,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "jeans.png", accIcon: 0, @@ -1100,6 +1158,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "trousers.png", accIcon: 0, @@ -1146,6 +1205,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "long_skirt.png", accIcon: 0, @@ -1192,6 +1252,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "miniskirt.png", accIcon: 0, @@ -1239,6 +1300,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "long_school_skirt.png", accIcon: 0, @@ -1286,6 +1348,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "short_school_skirt.png", accIcon: 0, @@ -1333,6 +1396,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "school_trousers.png", accIcon: 0, @@ -1378,6 +1442,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "oversized_sweater.png", accIcon: 0, @@ -1411,7 +1476,7 @@ function initLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "witch", gender: "f", femininity: 200, @@ -1426,6 +1491,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "witch_dress.png", accIcon: 0, @@ -1451,7 +1517,9 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: [], + colour_options: ["original", "denim", "light blue", "black", "blue steel", "grey", "custom"], + colour_combat: "blue", + colour_sidebar: 1, exposed: 0, exposed_base: 0, vagina_exposed: 0, @@ -1471,6 +1539,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "denim_shorts.png", accIcon: 0, @@ -1517,6 +1586,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "girls_oversized_sweater.png", accIcon: 0, @@ -1563,6 +1633,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "christmas_trousers.png", }, @@ -1608,6 +1679,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, outfitSecondary: ["upper", "christmas dress"], }, @@ -1654,6 +1726,7 @@ function initLower() { back_img: 1, back_img_colour: "primary", cursed: 0, + notuck: 1, location: 0, iconFile: "monks_habit.png", accIcon: 0, @@ -1701,6 +1774,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "cowboy_chaps.png", accIcon: 0, @@ -1728,8 +1802,8 @@ function initLower() { colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_combat: "red", colour_sidebar: 1, - exposed: 1, - exposed_base: 1, + exposed: 0, + exposed_base: 0, vagina_exposed: 0, vagina_exposed_base: 0, anus_exposed: 0, @@ -1748,6 +1822,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "belly_dancers_bottoms.png", accIcon: "belly_dancers_bottoms_acc.png", @@ -1793,6 +1868,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 1, + notuck: 1, location: 0, iconFile: 0, accIcon: 0, @@ -1839,6 +1915,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "khakis.png", accIcon: 0, @@ -1863,7 +1940,22 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -1883,9 +1975,11 @@ function initLower() { accessory_colour: 0, accessory_colour_options: [], accessory_colour_combat: "white", + accessory_integrity_img: 1, high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "gingham_dress.png", accIcon: "gingham_dress_acc.png", @@ -1911,8 +2005,9 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: [], + colour_options: ["original", "denim", "light blue", "black", "blue steel", "grey", "custom"], colour_combat: "blue", + colour_sidebar: 1, exposed: 0, exposed_base: 0, vagina_exposed: 0, @@ -1932,6 +2027,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "overalls.png", accIcon: 0, @@ -1980,6 +2076,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "cheongsam.png", accIcon: "cheongsam_acc.png", @@ -2028,6 +2125,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "short_cheongsam.png", accIcon: "short_cheongsam_acc.png", @@ -2075,6 +2173,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "micro_pleated_skirt.png", accIcon: 0, @@ -2120,6 +2219,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "chinos.png", accIcon: 0, @@ -2165,9 +2265,10 @@ function initLower() { accessory_colour_options: [], accessory_colour_sidebar: 0, accessory_integrity_img: 1, - high_img: 1, + high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "gothic_gown.png", accIcon: 0, @@ -2215,6 +2316,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "gothic_trousers.png", accIcon: 0, @@ -2261,6 +2363,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "waiters_trousers.png", accIcon: 0, @@ -2307,6 +2410,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "lederhosen.png", accIcon: 0, @@ -2354,6 +2458,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "karate_trousers.png", accIcon: 0, @@ -2400,6 +2505,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "sailor_trousers.png", accIcon: 0, @@ -2446,6 +2552,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "sailor_shorts.png", accIcon: 0, @@ -2453,8 +2560,8 @@ function initLower() { { index: 53, - name: "football shorts", - name_cap: "Football shorts", + name: "foreign football shorts", + name_cap: "Foreign football shorts", variable: "football", integrity: 200, integrity_max: 200, @@ -2470,7 +2577,7 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -2492,6 +2599,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "football_shorts.png", accIcon: 0, @@ -2538,6 +2646,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "gym_bloomers.png", accIcon: 0, @@ -2585,6 +2694,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "chapette_breeches.png", accIcon: "chapette_breeches_acc.png", @@ -2632,6 +2742,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "cheerleading_skirt.png", accIcon: 0, @@ -2678,6 +2789,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "mummy_skirt.png", accIcon: 0, @@ -2724,6 +2836,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "diving_suit.png", accIcon: "diving_suit_acc.png", @@ -2771,6 +2884,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "classic_sundress.png", accIcon: 0, @@ -2790,7 +2904,7 @@ function initLower() { word: "n", one_piece: 0, skirt: 0, - skirt_down: 1, + skirt_down: 0, short: 1, state: "waist", state_base: "waist", @@ -2819,6 +2933,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "classic_school_shorts.png", accIcon: 0, @@ -2866,6 +2981,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "classic_school_skirt.png", accIcon: 0, @@ -2909,9 +3025,10 @@ function initLower() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], - high_img: 1, + high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "classic_gothic_gown.png", accIcon: 0, @@ -2937,7 +3054,7 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "neon blue", "custom"], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -2945,7 +3062,7 @@ function initLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["normal", "athletic"], + type: ["normal"], set: "lower", gender: "m", femininity: -200, @@ -2960,6 +3077,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "scout_shorts.png", accIcon: 0, @@ -2967,8 +3085,8 @@ function initLower() { { index: 64, - name: "soccer shorts", - name_cap: "Soccer shorts", + name: "football shorts", + name_cap: "Football shorts", variable: "soccer", integrity: 120, integrity_max: 120, @@ -3006,6 +3124,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "soccer_shorts.png", accIcon: 0, @@ -3052,6 +3171,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "star_pyjama_shorts.png", accIcon: "star_pyjama_shorts_acc.png", @@ -3098,6 +3218,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "moon_pyjama_bottoms.png", accIcon: "moon_pyjama_bottoms_acc.png", @@ -3158,6 +3279,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "sweatpants.png", accIcon: 0, @@ -3203,6 +3325,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "unitard.png", accIcon: 0, @@ -3248,6 +3371,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "kilt.png", accIcon: 0, @@ -3286,7 +3410,7 @@ function initLower() { gender: "n", warmth: 40, cost: 0, - description: "Soft and cozy.", + description: "Soft and cosy.", shop: ["clothing"], accessory: 0, accessory_colour: 0, @@ -3294,6 +3418,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "bathrobe.png", accIcon: 0, @@ -3327,7 +3452,7 @@ function initLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["rag", "costume"], + type: ["costume"], set: "lower", gender: "n", warmth: 15, @@ -3340,6 +3465,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "rag_bottom.png", accIcon: 0, @@ -3386,6 +3512,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "retro_trousers.png", accIcon: "retro_trousers_acc.png", @@ -3432,6 +3559,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "retro_shorts.png", accIcon: "retro_shorts_acc.png", @@ -3477,6 +3605,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "monster_hoodie.png", outfitSecondary: ["upper", "monster hoodie"], @@ -3524,6 +3653,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "keyhole.png", outfitSecondary: ["upper", "keyhole dress"], @@ -3549,8 +3679,9 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: [], + colour_options: ["original", "denim", "light blue", "black", "blue steel", "grey", "custom"], colour_combat: 0, + colour_sidebar: 1, exposed: 0, exposed_base: 0, vagina_exposed: 0, @@ -3572,6 +3703,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "jorts.png", accIcon: 0, @@ -3618,6 +3750,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: 0, accIcon: 0, @@ -3663,6 +3796,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: 0, accIcon: 0, @@ -3710,6 +3844,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "patient_gown.png", accIcon: 0, @@ -3756,6 +3891,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "cow_onesie.png", outfitSecondary: ["upper", "cow onesie"], @@ -3803,6 +3939,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "cow_print_chaps.png", accIcon: 0, @@ -3821,7 +3958,7 @@ function initLower() { word: "n", one_piece: 0, skirt: 0, - skirt_down: 1, + skirt_down: 0, short: 1, state: "waist", state_base: "waist", @@ -3849,6 +3986,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "baseball_shorts.png", accIcon: "baseball_shorts_acc.png", @@ -3882,7 +4020,7 @@ function initLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "scarecrow", gender: "n", femininity: 0, @@ -3897,6 +4035,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "scarecrow_shirt.png", accIcon: 0, @@ -3958,6 +4097,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "waitress_uniform.png", accIcon: "waitress_uniform_acc.png", @@ -4019,6 +4159,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "split_dress.png", accIcon: "split_dress_acc.png", @@ -4044,7 +4185,24 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -4066,6 +4224,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "skimpy_lolita_dress.png", accIcon: "skimpy_lolita_dress_acc.png", @@ -4091,7 +4250,24 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -4110,9 +4286,10 @@ function initLower() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], - high_img: 1, + high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "short_ballgown.png", accIcon: 0, @@ -4160,6 +4337,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "slacks.png", accIcon: 0, @@ -4207,6 +4385,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "pink_nurse_dress.png", accIcon: 0, @@ -4255,6 +4434,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "plastic_nurse_dress.png", accIcon: 0, @@ -4303,6 +4483,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "transparent_nurse_dress.png", accIcon: 0, @@ -4364,6 +4545,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "hanfu.png", accIcon: 0, @@ -4411,6 +4593,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "open_shoulder_sweater.png", accIcon: 0, @@ -4457,7 +4640,9 @@ function initLower() { accessory_colour_options: [], high_img: 0, back_img: 0, + mask_img: 1, cursed: 0, + notuck: 1, location: 0, iconFile: 0, accIcon: 0, @@ -4491,7 +4676,7 @@ function initLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "skele", gender: "n", femininity: 0, @@ -4505,6 +4690,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "skeleton_outfit.png", accIcon: 0, @@ -4538,7 +4724,7 @@ function initLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "futuresuit", gender: "n", femininity: 0, @@ -4552,6 +4738,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "futuresuit.png", accIcon: 0, @@ -4599,6 +4786,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "pencil_skirt.png", accIcon: 0, @@ -4645,6 +4833,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "lace_nightgown.png", accIcon: 0, @@ -4693,6 +4882,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "ao_dai_trousers.png", accIcon: 0, @@ -4739,6 +4929,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "maid_dress_traditional.png", accIcon: 0, @@ -4786,6 +4977,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "maid_dress_victorian.png", accIcon: 0, @@ -4833,6 +5025,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "shrine_maiden.png", accIcon: 0, @@ -4881,6 +5074,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "school_skirt_plaid.png", accIcon: 0, @@ -4928,6 +5122,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "school_trousers_plaid.png", accIcon: 0, @@ -4946,7 +5141,7 @@ function initLower() { word: "n", one_piece: 0, skirt: 0, - skirt_down: 1, + skirt_down: 0, short: 1, state: "waist", state_base: "waist", @@ -4975,6 +5170,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "school_shorts_plaid.png", accIcon: 0, @@ -5024,6 +5220,7 @@ function initLower() { back_img: 0, covers_top: 1, cursed: 0, + notuck: 1, location: 0, iconFile: "pinafore.png", accIcon: 0, @@ -5074,6 +5271,7 @@ function initLower() { back_img: 0, covers_top: 1, cursed: 0, + notuck: 1, location: 0, iconFile: "pinafore_plaid.png", accIcon: 0, @@ -5122,6 +5320,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "wide_leg_trousers.png", accIcon: 0, @@ -5165,10 +5364,10 @@ function initLower() { accessory: 0, accessory_colour: 0, accessory_colour_options: [], - accessory_colour: 0, high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "straight_trousers.png", accIcon: 0, @@ -5210,11 +5409,27 @@ function initLower() { shop: ["clothing"], accessory: 1, accessory_colour: 0, - accessory_colour_options: ["black", "blue steel", "grey", "white", "light pink", "light blue", "light green", "sand", "red", "pink", "purple", "tangerine", "teal", "custom"], + accessory_colour_options: [ + "black", + "blue steel", + "grey", + "white", + "light pink", + "light blue", + "light green", + "sand", + "red", + "pink", + "purple", + "tangerine", + "teal", + "custom", + ], accessory_colour_sidebar: 1, high_img: 0, back_img: 0, cursed: 0, + notuck: 0, location: 0, iconFile: "yoga_pants.png", accIcon: "yoga_pants_acc.png", @@ -5261,6 +5476,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "virgin_killer_dress.png", accIcon: 0, @@ -5286,7 +5502,24 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -5308,6 +5541,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "halter_sundress.png", accIcon: 0, @@ -5333,8 +5567,26 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: [], - colour_sidebar: 0, + colour_options: [ + "black", + "wine", + "blue steel", + "light pink", + "silver", + "gold", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "brown", + "custom", + ], + colour_sidebar: 1, exposed: 0, exposed_base: 0, vagina_exposed: 0, @@ -5355,6 +5607,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "leather_dress.png", accIcon: 0, @@ -5369,7 +5622,7 @@ function initLower() { integrity: 160, integrity_max: 160, fabric_strength: 30, - reveal: 500, + reveal: 800, rearresize: 0, word: "a", one_piece: 0, @@ -5380,7 +5633,9 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: [], + colour_options: ["original", "denim", "light blue", "black", "blue steel", "grey", "custom"], + colour_combat: "blue", + colour_sidebar: 1, exposed: 0, exposed_base: 0, vagina_exposed: 1, @@ -5401,6 +5656,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "jean_mini_skirt.png", accIcon: 0, @@ -5450,6 +5706,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "dolphin_shorts.png", accIcon: "dolphin_shorts_acc.png", @@ -5497,6 +5754,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "school_skirt.png", accIcon: 0, @@ -5544,6 +5802,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "long_school_skirt.png", accIcon: 0, @@ -5591,6 +5850,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "short_school_skirt.png", accIcon: 0, @@ -5615,7 +5875,8 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: [], + colour_options: ["original", "denim", "light blue", "black", "blue steel", "grey", "custom"], + colour_sidebar: 1, colour_combat: 0, exposed: 0, exposed_base: 0, @@ -5638,6 +5899,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "jorts.png", accIcon: 0, @@ -5661,7 +5923,22 @@ function initLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "light pink", + "lilac", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -5685,6 +5962,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "open_shoulder_lolita_dress.png", accIcon: "open_shoulder_lolita_dress_acc.png", @@ -5731,6 +6009,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "gym_shorts.png", accIcon: 0, @@ -5754,7 +6033,24 @@ function initLower() { state_base: "waist", plural: 1, colour: 0, - colour_options: ["blue steel", "light blue", "tan", "sand","black", "white", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "yellow", "custom"], + colour_options: [ + "blue steel", + "light blue", + "tan", + "sand", + "black", + "white", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -5762,10 +6058,10 @@ function initLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["normal"], + type: ["normal", "bellyHide"], set: "lower", gender: "n", - femininity: 200, + femininity: 0, warmth: 20, cost: 2500, description: "Shirt sold separately.", @@ -5778,6 +6074,7 @@ function initLower() { back_img: 0, covers_top: 1, cursed: 0, + notuck: 1, location: 0, iconFile: "shortalls.png", accIcon: "shortalls_acc.png", @@ -5825,6 +6122,7 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "jinglebell_dress.png", outfitSecondary: ["upper", "jingle-bell dress"], @@ -5871,11 +6169,849 @@ function initLower() { high_img: 0, back_img: 0, cursed: 0, + notuck: 1, location: 0, iconFile: "jinglebell_dress_sleeveless.png", outfitSecondary: ["upper", "sleeveless jingle-bell dress"], }, - + { + index: 125, + name: "leather miniskirt", + name_cap: "Leather miniskirt", + variable: "leatherminiskirt", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 800, + rearresize: 0, + word: "a", + one_piece: 1, + skirt: 1, + skirt_down: 1, + short: 0, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: ["black", "brown", "silver", "gold", "blue", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 1, + vagina_exposed_base: 1, + anus_exposed: 1, + anus_exposed_base: 1, + type: ["normal", "cool"], + set: "lower", + gender: "f", + femininity: 200, + warmth: 15, + cost: 4000, + description: "Close to being a wardrobe malfunction.", + shop: ["clothing", "adult"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["silver", "gold", "steel", "blue steel"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "leather_mini_skirt.png", + accIcon: "leather_mini_skirt_acc.png", + }, + { + index: 126, + name: "leather pants", + name_cap: "Leather pants", + variable: "leatherpants", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 600, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 0, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: ["black", "brown", "silver", "gold", "blue", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + colour_combat: "black", + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal", "constricting"], + set: "lower", + gender: "n", + warmth: 25, + cost: 4500, + description: "Tight.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["silver", "gold", "steel", "blue steel"], + accessory_colour_sidebar: 1, + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 0, + location: 0, + iconFile: "leather_pants.png", + accIcon: "leather_pants_acc.png", + }, + { + index: 127, + name: "leather shorts", + name_cap: "Leather shorts", + variable: "leathershorts", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 600, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 1, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: ["black", "brown", "silver", "gold", "blue", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + colour_combat: "black", + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal", "constricting"], + set: "lower", + gender: "n", + warmth: 20, + cost: 4000, + description: "Tight.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["silver", "gold", "steel", "blue steel"], + accessory_colour_sidebar: 1, + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "leather_shorts.png", + accIcon: "leather_shorts_acc.png", + }, + { + index: 128, + name: "sexy nun's habit skirt", + name_cap: "Sexy nun's habit skirt", + variable: "nunlewd", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 500, + rearresize: 0, + word: "a", + one_piece: 1, + skirt: 1, + skirt_down: 1, + short: 0, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: [], + colour_combat: "black", + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["holy", "costume", "fetish"], + set: "nunlewd", + gender: "f", + femininity: 200, + warmth: 30, + cost: 0, + description: "A habit better suited to the bedroom.", + shop: ["forest"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + back_img_colour: "primary", + cursed: 0, + location: 0, + iconFile: "sexy_nuns_habit.png", + accIcon: 0, + outfitSecondary: ["upper", "sexy nun's habit"], + }, + { + index: 129, + name: "sexy priest's shorts", + name_cap: "Sexy priest's shorts", + variable: "monklewd", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 500, + rearresize: 0, + word: "a", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 1, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: [], + colour_combat: "black", + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["holy", "costume", "fetish"], + set: "monklewd", + gender: "m", + femininity: -200, + warmth: 15, + cost: 0, + description: "Shows off what the good lord gave you.", + shop: ["forest"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + back_img_colour: "primary", + cursed: 0, + location: 0, + iconFile: "sexy_priests_vestments.png", + accIcon: 0, + outfitSecondary: ["upper", "sexy priest's vestments"], + }, + { + index: 130, + name: "oversized button-down bottom", + name_cap: "Oversized button-down bottom", + variable: "oversizedbuttondown", + integrity: 60, + integrity_max: 60, + fabric_strength: 30, + reveal: 500, + rearresize: 0, + word: "an", + one_piece: 1, + skirt: 1, + skirt_down: 1, + short: 1, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + vagina_exposed: 1, + vagina_exposed_base: 1, + anus_exposed: 1, + anus_exposed_base: 1, + type: ["normal", "sleep"], + set: "oversizedbuttondown", + gender: "n", + warmth: 5, + cost: 0, + description: "Snug, comfy and long enough to cover your unmentionables. Just.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "oversized_button_down.png", + accIcon: 0, + outfitSecondary: ["upper", "oversized button-down"], + }, + { + index: 131, + name: "suit trousers", + name_cap: "Suit trousers", + variable: "3piecesuit", + integrity: 160, + integrity_max: 160, + fabric_strength: 30, + reveal: 600, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 0, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: ["black", "grey", "white", "wine", "navy blue", "olive", "brown", "tan", "sand", "off-white", "custom"], + colour_combat: 0, + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["formal"], + set: "lower", + gender: "m", + femininity: -200, + warmth: 40, + cost: 20000, + description: "Smart and sophisticated.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 0, + location: 0, + iconFile: "3piece_trousers.png", + accIcon: 0, + }, + { + index: 132, + name: "puffy shorts", + name_cap: "Puffy shorts", + variable: "puffy", + integrity: 160, + integrity_max: 160, + fabric_strength: 30, + reveal: 300, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 1, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: [ + "russet", + "sand", + "grey", + "wine", + "olive", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal"], + set: "lower", + gender: "m", + femininity: -200, + warmth: 20, + cost: 3000, + description: "Boyish and old-fashioned.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["steel", "blue steel", "bronze", "gold", "silver"], + accessory_colour_sidebar: 1, + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "puffy_shorts_m.png", + accIcon: "puffy_shorts_m_acc.png", + }, + { + index: 133, + name: "bloomers", + name_cap: "Bloomers", + variable: "puffybloomers", + integrity: 160, + integrity_max: 160, + fabric_strength: 30, + reveal: 300, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 1, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: [ + "russet", + "sand", + "grey", + "wine", + "olive", + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal"], + set: "lower", + gender: "n", + femininity: 0, + warmth: 20, + cost: 3000, + description: "Old-fashioned and androgynous.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "puffy_shorts.png", + accIcon: 0, + }, + { + index: 134, + name: "harem pants", + name_cap: "Harem pants", + variable: "harempants", + integrity: 150, + integrity_max: 150, + fabric_strength: 30, + reveal: 400, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 0, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_combat: "red", + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["dance"], + set: "lower", + gender: "m", + femininity: -200, + warmth: 10, + cost: 12000, + description: "Exotic and comfortable.", + shop: ["forest"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 0, + location: 0, + iconFile: "harem_pants.png", + accIcon: 0, + }, + { + index: 135, + name: "sexy butler shorts", + name_cap: "Sexy butler shorts", + variable: "slutler", + integrity: 130, + integrity_max: 130, + fabric_strength: 30, + reveal: 850, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 1, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: [], + colour_combat: 0, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["maid", "costume", "serving"], + set: "lower", + gender: "m", + femininity: -200, + warmth: 10, + cost: 1500, + description: "Shows off the whole package.", + shop: ["adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "sexy_butler_shorts.png", + accIcon: 0, + }, + { + index: 136, + name: "sarong", + name_cap: "Sarong", + variable: "sarong", + integrity: 10, + integrity_max: 10, + fabric_strength: 20, + reveal: 800, + rearresize: 0, + word: "a", + one_piece: 0, + skirt: 1, + skirt_down: 1, + short: 0, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 1, + exposed_base: 1, + vagina_exposed: 1, + vagina_exposed_base: 1, + anus_exposed: 1, + anus_exposed_base: 1, + type: ["normal"], + set: "lower", + gender: "n", + warmth: 10, + cost: 800, + description: "Flirty beachwear.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 1, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "sarong.png", + accIcon: 0, + }, + { + index: 137, + name: "cargo pants", + name_cap: "Cargo pants", + variable: "cargo", + integrity: 160, + integrity_max: 160, + fabric_strength: 30, + reveal: 300, + rearresize: 0, + word: "n", + one_piece: 0, + skirt: 0, + skirt_down: 0, + short: 0, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: ["black", "grey", "white", "wine", "navy", "olive", "brown", "tan", "sand", "off-white", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal"], + set: "lower", + gender: "m", + femininity: -200, + warmth: 25, + cost: 4000, + description: "Sturdy, with lots of pockets.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 0, + location: 0, + iconFile: "cargo_pants.png", + accIcon: 0, + }, + { + index: 138, + name: "high-waisted skirt", + name_cap: "High-waisted skirt", + variable: "highwaistedskirt", + integrity: 160, + integrity_max: 160, + fabric_strength: 20, + reveal: 400, + rearresize: 0, + word: "a", + one_piece: 0, + skirt: 1, + skirt_down: 1, + short: 0, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "light pink", + "lilac", + "custom", + ], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal"], + set: "lower", + gender: "f", + femininity: 200, + warmth: 20, + cost: 4000, + description: "Easy to move in.", + shop: ["clothing", "adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "highwaisted_skirt.png", + accIcon: 0, + }, + + { + index: 139, + name: "classic lolita skirt", + name_cap: "Classic lolita skirt", + variable: "openshoulderlolitaclassic", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 400, + rearresize: 0, + word: "a", + one_piece: 1, + skirt: 1, + skirt_down: 1, + short: 0, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: [ + "black", + "light pink", + "lilac", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal"], + set: "classicopenshoulderlolita", + gender: "f", + femininity: 200, + warmth: 15, + cost: 0, + description: "A blend of fashions.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 1, + location: 0, + iconFile: "classic_open_shoulder_lolita_dress.png", + accIcon: "classic_open_shoulder_lolita_dress_acc.png", + outfitSecondary: ["upper", "classic open shoulder lolita dress"], + }, + { + index: 140, + name: "jumpsuit trousers", + name_cap: "Jumpsuit trousers", + variable: "jumpsuitstylish", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 300, + rearresize: 0, + word: "n", + one_piece: 1, + skirt: 0, + skirt_down: 0, + short: 0, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "light pink", + "light blue", + "lilac", + "olive", + "tan", + "russet", + "neon blue", + "custom", + ], + colour_combat: 0, + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal"], + set: "jumpsuit", + gender: "n", + warmth: 40, + cost: 0, + description: "Trendy and retro.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + high_img: 0, + back_img: 0, + cursed: 0, + notuck: 0, + location: 0, + iconFile: 0, + accIcon: 0, + outfitSecondary: ["upper", "prison jumpsuit"], + }, ]; /* diff --git a/game/base-clothing/clothing-neck.js b/game/base-clothing/clothing-neck.js index 46d5ba5fb..675a019c2 100644 --- a/game/base-clothing/clothing-neck.js +++ b/game/base-clothing/clothing-neck.js @@ -456,6 +456,7 @@ function initNeck() { accessory_colour: 0, accessory_colour_options: ["steel", "blue steel", "bronze", "gold", "silver"], accessory_colour_sidebar: 1, + mask_img: 1, cursed: 0, location: 0, iconFile: "suspenders.png", @@ -506,7 +507,25 @@ function initNeck() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "blue", + "neon blue", + "wine", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, type: ["normal"], gender: "n", @@ -813,7 +832,7 @@ function initNeck() { colour: 0, colour_options: [], colour_combat: "black", - type: ["fetish", "costume", "eerie"], + type: ["fetish", "costume", "eerie", "leash"], gender: "n", femininity: 0, warmth: 0, @@ -874,7 +893,24 @@ function initNeck() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, type: ["normal"], gender: "n", @@ -935,8 +971,9 @@ function initNeck() { word: "a", plural: 0, colour: 0, - colour_options: [], - colour_combat: "red", + colour_options: ["bronze", "gold", "silver"], + colour_sidebar: 1, + colour_combat: "gold", type: ["normal"], gender: "n", femininity: 0, @@ -997,7 +1034,7 @@ function initNeck() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "navy blue", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, type: ["formal"], gender: "m", @@ -1029,7 +1066,22 @@ function initNeck() { word: "a", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, type: ["formal"], gender: "f", @@ -1049,6 +1101,35 @@ function initNeck() { iconFile: "ribbon_tie.png", accIcon: 0, }, + { + index: 34, + name: "whistle", + name_cap: "Whistle", + variable: "whistle", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 100, + word: "a", + plural: 0, + colour: 0, + colour_options: [], + type: ["normal"], + gender: "n", + femininity: 0, + warmth: 0, + cost: 300, + description: "Not effective as a rape whistle.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + cursed: 0, + location: 0, + iconFile: "whistle.png", + accIcon: 0, + }, ]; /* diff --git a/game/base-clothing/clothing-over_head.js b/game/base-clothing/clothing-over-head.js similarity index 100% rename from game/base-clothing/clothing-over_head.js rename to game/base-clothing/clothing-over-head.js diff --git a/game/base-clothing/clothing-over_lower.js b/game/base-clothing/clothing-over-lower.js similarity index 100% rename from game/base-clothing/clothing-over_lower.js rename to game/base-clothing/clothing-over-lower.js diff --git a/game/base-clothing/clothing-over_upper.js b/game/base-clothing/clothing-over-upper.js similarity index 100% rename from game/base-clothing/clothing-over_upper.js rename to game/base-clothing/clothing-over-upper.js diff --git a/game/base-clothing/clothing-sets.twee b/game/base-clothing/clothing-sets.twee index 839113ec7..b0debd1dd 100644 --- a/game/base-clothing/clothing-sets.twee +++ b/game/base-clothing/clothing-sets.twee @@ -101,11 +101,17 @@ <> <> < outfitSet.type.includes("sleep"))>> + <> + <> + <> + <> + <> + <> <> - <>당신 머리의 슬라임은 조용하다. + 머릿속의 슬라임은 조용하다.
<> - <>슬라임이 당신이 옷을 입고 자는 것을 허락하지 않는다. + The slime is not allowing you to sleep with anything on.
<
> <
> @@ -277,7 +283,7 @@ <> <> <> - <> + <> <> <> /* Check for saved colors in clothing set */ @@ -530,7 +536,7 @@ <> <> <> - <>< { + < { switch (notEquippedItem.reason) { case "not found": if (_resultStrings.notFound === undefined) { @@ -641,6 +647,7 @@ _resultStrings.cursedUnequip.names.push(notEquippedItem.name); break; case "not found piece": + if (V.worn[slot].name === notEquippedItem.name) break; if (_resultStrings.notFoundPiece === undefined) { _resultStrings.notFoundPiece = { "start": "예기치 못한 문제로 (대부분 세이브파일 에러로) 당신은 ", diff --git a/game/base-clothing/clothing-under.js b/game/base-clothing/clothing-under-lower.js similarity index 93% rename from game/base-clothing/clothing-under.js rename to game/base-clothing/clothing-under-lower.js index 056c7b802..dd6b55388 100644 --- a/game/base-clothing/clothing-under.js +++ b/game/base-clothing/clothing-under-lower.js @@ -395,7 +395,7 @@ function initUnderLower() { anus_exposed: 0, anus_exposed_base: 0, no_aside: 1, - type: ["dance", "athletic"], + type: ["dance", "covered"], anal_shield: 0, set: "leotard", gender: "n", @@ -443,7 +443,7 @@ function initUnderLower() { anus_exposed: 0, anus_exposed_base: 0, no_aside: 1, - type: ["dance", "athletic"], + type: ["dance", "covered"], anal_shield: 0, set: "unitard", gender: "n", @@ -489,7 +489,7 @@ function initUnderLower() { vagina_exposed_base: 0, anus_exposed: 0, anus_exposed_base: 0, - type: ["dance", "athletic"], + type: ["dance"], anal_shield: 0, set: "skimpy leotard", gender: "n", @@ -618,7 +618,7 @@ function initUnderLower() { state_base: "waist", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "white", "yellow", "tan", "fleshy"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "tan", "fleshy"], colour_sidebar: 1, colour_combat: 0, exposed: 0, @@ -927,7 +927,7 @@ function initUnderLower() { integrity: 100, integrity_max: 100, fabric_strength: 15, - reveal: 400, + reveal: 500, rearresize: 0, word: "n", one_piece: 0, @@ -1335,7 +1335,7 @@ function initUnderLower() { index: 29, name: "boxers", name_cap: "Boxers", - name_simple: "undies", + name_simple: "boxers", variable: "boxers", integrity: 100, integrity_max: 100, @@ -1403,7 +1403,7 @@ function initUnderLower() { anus_exposed: 0, anus_exposed_base: 0, no_aside: 1, - type: ["normal"], + type: ["normal", "covered"], anal_shield: 0, set: "under_lower", warmth: 30, @@ -1416,7 +1416,7 @@ function initUnderLower() { accessory_colour: 0, accessory_colour_options: [], penis_img: 0, - high_img: 0, + high_img: 1, cursed: 0, location: 0, iconFile: "long_johns.png", @@ -1862,7 +1862,7 @@ function initUnderLower() { anus_exposed: 0, anus_exposed_base: 0, no_aside: 1, - type: ["dance", "athletic"], + type: ["dance"], anal_shield: 0, set: "turtleneck leotard", gender: "n", @@ -1972,6 +1972,142 @@ function initUnderLower() { iconFile: "lace_panties.png", accIcon: 0, }, + { + index: 43, + name: "denim panties", + name_cap: "Denim panties", + name_simple: "panties", + variable: "janties", + integrity: 60, + integrity_max: 60, + fabric_strength: 15, + reveal: 600, + rearresize: 0, + word: "n", + one_piece: 0, + state: "waist", + state_base: "waist", + plural: 1, + colour: 0, + colour_options: ["denim", "light blue", "black", "blue steel", "grey", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + type: ["normal"], + anal_shield: 0, + set: "under_lower", + gender: "f", + femininity: 300, + warmth: 5, + cost: 1500, + description: "An atrocity.", + shop: ["clothing", "adult"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["silver", "gold"], + accessory_colour_sidebar: 1, + penis_img: 0, + high_img: 0, + cursed: 0, + location: 0, + iconFile: "janties.png", + accIcon: 0, + }, + { + index: 44, + name: "denim thong", + name_cap: "Denim thong", + name_simple: "thong", + variable: "jhong", + integrity: 40, + integrity_max: 40, + fabric_strength: 20, + reveal: 700, + rearresize: 0, + word: "a", + one_piece: 0, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: ["denim", "light blue", "black", "blue steel", "grey", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 1, + anus_exposed_base: 1, + type: ["normal"], + anal_shield: 0, + set: "under_lower", + gender: "n", + femininity: 0, + warmth: 3, + cost: 1500, + description: "Barely covers the essentials. Somewhat uncomfortable.", + shop: ["clothing", "adult"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["silver", "gold"], + accessory_colour_sidebar: 1, + penis_img: 0, + high_img: 0, + cursed: 0, + location: 0, + iconFile: "jhong.png", + accIcon: 0, + }, + { + index: 45, + name: "latex leotard bottom", + name_cap: "Latex leotard bottom", + name_simple: "leotard", + variable: "latexleotard", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 600, + rearresize: 0, + word: "a", + one_piece: 1, + state: "waist", + state_base: "waist", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + vagina_exposed: 0, + vagina_exposed_base: 0, + anus_exposed: 0, + anus_exposed_base: 0, + no_aside: 1, + type: ["dance", "fetish", "waterproof"], + anal_shield: 0, + set: "latexleotard", + gender: "n", + warmth: 20, + cost: 0, + description: "Form fitting and waterproof.", + shop: ["clothing", "adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + penis_img: 0, + high_img: 0, + cursed: 0, + location: 0, + iconFile: "bunny_leotard.png", + accIcon: 0, + outfitSecondary: ["under_upper", "latex leotard"], + }, ]; /* diff --git a/game/base-clothing/clothing-under-upper.js b/game/base-clothing/clothing-under-upper.js index 4ae3ea3b6..bec89aa59 100644 --- a/game/base-clothing/clothing-under-upper.js +++ b/game/base-clothing/clothing-under-upper.js @@ -81,6 +81,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "bikini_top.png", @@ -115,6 +116,7 @@ function initUnderUpper() { set: "school swimsuit", gender: "f", femininity: 300, + formfitting: 1, warmth: 20, cost: 2500, description: "Proper school swimwear.", @@ -157,9 +159,10 @@ function initUnderUpper() { colour_combat: 0, exposed: 0, exposed_base: 0, - type: ["dance", "athletic"], + type: ["dance", "covered"], set: "leotard", gender: "n", + formfitting: 1, warmth: 20, cost: 3000, description: "Form fitting.", @@ -202,9 +205,10 @@ function initUnderUpper() { colour_combat: 0, exposed: 0, exposed_base: 0, - type: ["dance", "athletic"], + type: ["dance", "covered"], set: "unitard", gender: "n", + formfitting: 1, warmth: 25, cost: 3500, description: "Form fitting. Covers your legs.", @@ -246,9 +250,10 @@ function initUnderUpper() { colour_combat: 0, exposed: 0, exposed_base: 0, - type: ["dance", "athletic"], + type: ["dance", "covered"], set: "skimpy leotard", gender: "n", + formfitting: 1, warmth: 15, cost: 2500, description: "Form fitting. Shows off your thighs.", @@ -293,6 +298,7 @@ function initUnderUpper() { set: "foreign school swimsuit", gender: "f", femininity: 300, + formfitting: 1, warmth: 20, cost: 2500, description: "Official uniform swimsuit for the local school, but in a style popular in a foreign country.", @@ -338,6 +344,7 @@ function initUnderUpper() { set: "swimsuit", gender: "f", femininity: 300, + formfitting: 1, warmth: 20, cost: 7500, description: "Sexy.", @@ -374,7 +381,7 @@ function initUnderUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "tan", "fleshy"], colour_sidebar: 1, colour_combat: 0, exposed: 0, @@ -383,6 +390,7 @@ function initUnderUpper() { set: "leotardbunny", gender: "f", femininity: 300, + formfitting: 1, warmth: 25, cost: 5000, description: "Waitress attire. Tougher than it looks. Part of a bunny outfit, and has a fluffy white tail on the back to prove it.", @@ -480,6 +488,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "lace_bra.png", @@ -523,6 +532,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "microkini_top.png", @@ -566,6 +576,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "plain_bra.png", @@ -596,7 +607,7 @@ function initUnderUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["normal", "athletic"], + type: ["normal", "athletic", "covered"], set: "under_upper", gender: "f", femininity: 300, @@ -609,6 +620,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "sports_bra.png", @@ -684,6 +696,7 @@ function initUnderUpper() { type: ["fetish", "naked", "pushup", "constricting"], set: "under_upper", gender: "n", + formfitting: 1, warmth: 20, cost: 2500, description: "Constricts your tummy and emphasises your chest.", @@ -693,6 +706,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "corset.png", @@ -740,6 +754,8 @@ function initUnderUpper() { breast_acc_img: 1, cursed: 0, location: 0, + mainImage: 0, + accImage: 0, iconFile: "striped_bra.png", accIcon: "striped_bra_acc.png", }, @@ -768,7 +784,7 @@ function initUnderUpper() { colour_combat: 0, exposed: 0, exposed_base: 0, - type: ["chest_bind", "constricting"], + type: ["chest_bind", "constricting", "covered"], set: "under_upper", gender: "n", femininity: 0, @@ -781,6 +797,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "chest_wrap.png", @@ -901,6 +918,7 @@ function initUnderUpper() { set: "classic school swimsuit", gender: "f", femininity: 300, + formfitting: 1, warmth: 20, cost: 2500, description: "Proper school swimwear. Vintage.", @@ -942,7 +960,7 @@ function initUnderUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["swim", "school", "chest_bind", "constricting"], + type: ["swim", "school", "chest_bind", "constricting", "covered"], set: "under_upper", gender: "m", femininity: -100, @@ -986,7 +1004,7 @@ function initUnderUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["normal"], + type: ["normal", "covered"], set: "under_upper", gender: "m", femininity: -200, @@ -1042,6 +1060,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "strapless_bra.png", @@ -1085,6 +1104,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "school_swim_top.png", @@ -1129,6 +1149,7 @@ function initUnderUpper() { sleeve_img: 0, breast_img: 1, breast_combat: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "tape.png", @@ -1171,6 +1192,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "cow_bra.png", @@ -1201,7 +1223,7 @@ function initUnderUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["chest_bind", "constricting"], + type: ["chest_bind", "constricting", "covered"], set: "under_upper", gender: "n", femininity: 0, @@ -1244,7 +1266,7 @@ function initUnderUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["normal"], + type: ["normal", "covered"], set: "under_upper", gender: "m", femininity: -100, @@ -1293,6 +1315,7 @@ function initUnderUpper() { set: "see-through swimsuit", gender: "f", femininity: 300, + formfitting: 1, warmth: 10, cost: 2500, description: "Frames your torso.", @@ -1346,6 +1369,7 @@ function initUnderUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "pushup_bra.png", @@ -1420,9 +1444,10 @@ function initUnderUpper() { colour_combat: 0, exposed: 0, exposed_base: 0, - type: ["dance", "athletic"], + type: ["dance", "covered"], set: "turtleneck leotard", gender: "n", + formfitting: 1, warmth: 25, cost: 3500, description: "Form fitting. Covers your neck.", @@ -1438,6 +1463,150 @@ function initUnderUpper() { accIcon: 0, outfitPrimary: { under_lower: "turtleneck leotard bottom" }, }, + { + index: 33, + name: "camisole", + name_cap: "Camisole", + variable: "camisole", + integrity: 150, + integrity_max: 150, + fabric_strength: 20, + reveal: 500, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "white", "light pink", "lilac", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["normal", "covered"], + set: "under_upper", + gender: "f", + femininity: 200, + warmth: 30, + cost: 1200, + description: "Versatile and supportive.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: [ + "black", + "white", + "light pink", + "lilac", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "custom", + ], + accessory_colour_sidebar: 1, + sleeve_img: 0, + breast_img: 0, + cursed: 0, + location: 0, + iconFile: "camisole.png", + accIcon: "camisole_acc.png", + }, + { + index: 34, + name: "latex leotard", + name_cap: "Latex leotard", + variable: "latexleotard", + integrity: 100, + integrity_max: 100, + fabric_strength: 30, + reveal: 600, + bustresize: 0, + word: "a", + one_piece: 1, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["dance", "fetish", "waterproof"], + set: "latexleotard", + gender: "n", + formfitting: 1, + warmth: 20, + cost: 3000, + description: "Form fitting.", + shop: ["clothing", "adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "bunny_leotard.png", + accIcon: 0, + outfitPrimary: { under_lower: "latex leotard bottom" }, + }, + { + index: 35, + name: "bunny-tie bikini top", + name_cap: "Bunny-tie bikini top", + variable: "buntiebikinitop", + integrity: 20, + integrity_max: 20, + fabric_strength: 20, + reveal: 900, + bustresize: 0, + word: "n", + one_piece: 0, + strap: 1, + open: 1, + state: "midriff", + state_base: "midriff", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["swim"], + set: "under_upper", + gender: "f", + femininity: 300, + warmth: 10, + cost: 2000, + description: "Revealing swimwear.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + mainImage: 0, + cursed: 0, + location: 0, + iconFile: "bunny_tie_bikini_top.png", + accIcon: 0, + }, ]; /* diff --git a/game/base-clothing/clothing-upper.js b/game/base-clothing/clothing-upper.js index 478f74a36..d7b03a7c5 100644 --- a/game/base-clothing/clothing-upper.js +++ b/game/base-clothing/clothing-upper.js @@ -350,7 +350,24 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -358,6 +375,7 @@ function initUpper() { set: "evening gown", gender: "f", femininity: 200, + formfitting: 1, warmth: 40, cost: 12000, description: "For formal nights out.", @@ -411,7 +429,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 0, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "tank_top.png", @@ -440,7 +458,24 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -448,6 +483,7 @@ function initUpper() { set: "ballgown", gender: "f", femininity: 200, + formfitting: 1, warmth: 40, cost: 42000, description: "Extravagant.", @@ -589,6 +625,7 @@ function initUpper() { shopGroup: "maid dress", gender: "f", femininity: 200, + formfitting: 1, warmth: 40, cost: 2500, description: "For looking cute while cleaning.", @@ -734,7 +771,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "tuxedo_jacket.png", @@ -771,6 +808,7 @@ function initUpper() { set: "upper", gender: "f", femininity: 200, + formfitting: 1, warmth: 30, cost: 1000, description: "Loose and comfortable.", @@ -906,6 +944,7 @@ function initUpper() { set: "upper", gender: "f", femininity: 200, + formfitting: 1, warmth: 20, cost: 1500, description: "Show off your tummy.", @@ -961,6 +1000,7 @@ function initUpper() { accessory_colour_sidebar: 1, sleeve_img: 1, breast_img: 1, + breasts_under_acc: 1, cursed: 0, location: 0, iconFile: "serafuku.png", @@ -1090,7 +1130,7 @@ function initUpper() { cost: 6000, description: "Snug and comfy.", shop: ["clothing"], - // shopGroup: "sweater", disabled for now due to them being different enough and the shop not having an issue with the number of items + shopGroup: "sweater", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -1135,7 +1175,7 @@ function initUpper() { cost: 7000, description: "Snug, comfy and long enough to cover your unmentionables. Just.", shop: ["clothing"], - // shopGroup: "oversizedsweater", disabled for now due to them being different enough and the shop not having an issue with the number of items + shopGroup: "oversizedsweater", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -1174,7 +1214,7 @@ function initUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "witch", gender: "f", femininity: 200, @@ -1221,7 +1261,7 @@ function initUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["costume", "event", "bellyShow"], + type: ["costume", "bellyShow"], set: "upper", gender: "m", femininity: -200, @@ -1321,7 +1361,7 @@ function initUpper() { cost: 6000, description: "Snug and comfy.", shop: ["clothing"], - // shopGroup: "sweater", disabled for now due to them being different enough and the shop not having an issue with the number of items + shopGroup: "sweater", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -1367,7 +1407,7 @@ function initUpper() { cost: 7000, description: "Snug, comfy and long enough to cover your unmentionables. Just.", shop: ["clothing"], - // shopGroup: "oversizedsweater", disabled for now due to them being different enough and the shop not having an issue with the number of items + shopGroup: "oversizedsweater", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -1417,7 +1457,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "christmas_shirt.png", @@ -1453,6 +1493,7 @@ function initUpper() { set: "christmasdress", gender: "f", femininity: 200, + formfitting: 1, warmth: 70, cost: 8000, description: "Festive. The skirt is rather short.", @@ -1461,7 +1502,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "christmas_dress.png", @@ -1583,8 +1624,8 @@ function initUpper() { colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], colour_sidebar: 1, colour_combat: "red", - exposed: 1, - exposed_base: 1, + exposed: 0, + exposed_base: 0, type: ["costume", "serving", "dance", "bellyShow"], set: "upper", gender: "f", @@ -1682,15 +1723,17 @@ function initUpper() { cost: 3000, description: "A classic pattern.", shop: ["clothing"], - accessory: 0, + accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_sidebar: 1, sleeve_img: 0, - breast_img: 0, + breast_img: 1, + integrity_mask_img: 1, cursed: 0, location: 0, iconFile: "argyle_sweater_vest.png", - accIcon: 0, + accIcon: "argyle_sweater_vest_acc.png", notuck: 0, pregType: 0, }, @@ -1715,7 +1758,27 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["white", "black", "light pink", "light blue", "olive"], + colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "light pink", + "light blue", + "lilac", + "olive", + "tan", + "russet", + "neon blue", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -1731,7 +1794,10 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + altsleeve: "none", + altposition: "none", + altdisabled: ["sleeves"], + breast_img: 1, has_collar: 1, cursed: 0, location: 0, @@ -1761,7 +1827,22 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -1769,6 +1850,7 @@ function initUpper() { set: "gingham", gender: "f", femininity: 200, + formfitting: 1, warmth: 30, cost: 2000, description: "A comfortable classic.", @@ -1809,8 +1891,9 @@ function initUpper() { state_top_base: "chest", plural: 1, colour: 0, - colour_options: [], + colour_options: ["original", "denim", "light blue", "black", "blue steel", "grey", "custom"], colour_combat: "blue", + colour_sidebar: 1, exposed: 0, exposed_base: 0, type: ["normal", "bellyHide"], @@ -1820,15 +1903,17 @@ function initUpper() { cost: 4000, description: "Hard to take off.", shop: ["clothing"], - accessory: 0, + accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["silver", "blue steel", "gold", "white"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, sleeve_img: 0, breast_img: 0, cursed: 0, location: 0, iconFile: "overalls.png", - accIcon: 0, + accIcon: "overalls_acc.png", outfitPrimary: { lower: "overall bottoms" }, notuck: 0, pregType: 0, @@ -1836,8 +1921,8 @@ function initUpper() { { index: 40, - name: "black leather jacket", - name_cap: "Black leather jacket", + name: "punk leather jacket", + name_cap: "Punk leather jacket", variable: "blackleather", integrity: 240, integrity_max: 240, @@ -1854,8 +1939,26 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: [], + colour_options: [ + "black", + "brown", + "blue steel", + "tan", + "olive", + "wine", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_combat: "black", + colour_sidebar: 1, exposed: 0, exposed_base: 0, type: ["cool", "bellyShow"], @@ -1866,14 +1969,18 @@ function initUpper() { cost: 8000, description: "Delinquent.", shop: ["clothing"], - accessory: 0, + accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["silver", "blue steel", "gold", "white"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, sleeve_img: 1, - breast_img: 0, + breast_img: 1, + altposition: "none", + altdisabled: ["sleeves"], cursed: 0, location: 0, - iconFile: "black_leather_jacket.png", + iconFile: "punk_leather_jacket.png", accIcon: 0, notuck: 1, pregType: "split", @@ -1881,8 +1988,8 @@ function initUpper() { { index: 41, - name: "brown leather jacket", - name_cap: "Brown leather jacket", + name: "leather jacket", + name_cap: "Leather jacket", variable: "brownleather", integrity: 240, integrity_max: 240, @@ -1899,8 +2006,27 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: [], - colour_combat: "brown", + colour_options: [ + "soft brown", + "black", + "brown", + "blue steel", + "tan", + "olive", + "wine", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + colour_combat: 0, + colour_sidebar: 1, exposed: 0, exposed_base: 0, type: ["normal", "bellyShow"], @@ -1915,10 +2041,12 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + breast_img: 1, + altposition: "none", + altdisabled: ["sleeves"], cursed: 0, location: 0, - iconFile: "brown_leather_jacket.png", + iconFile: "leather_jacket.png", accIcon: 0, notuck: 1, pregType: "split", @@ -1959,7 +2087,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "beatnik_shirt.png", @@ -2128,6 +2256,7 @@ function initUpper() { set: "cheongsam", gender: "f", femininity: 200, + formfitting: 1, warmth: 35, cost: 14000, description: "Exotic.", @@ -2175,6 +2304,7 @@ function initUpper() { set: "cheongsamshort", gender: "f", femininity: 200, + formfitting: 1, warmth: 35, cost: 12000, description: "Exotic and revealing.", @@ -2261,7 +2391,7 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "navy", "grey", "olive", "wine", "custom"], + colour_options: ["black", "navy", "grey", "olive", "wine", "navy blue", "custom"], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -2272,15 +2402,18 @@ function initUpper() { cost: 40000, description: "Made of thick wool.", shop: ["clothing"], - accessory: 0, + accessory: 1, accessory_colour: 0, - accessory_colour_options: [], + accessory_colour_options: ["silver", "blue steel", "gold", "white"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, sleeve_img: 1, - breast_img: 0, + breast_img: 1, + breast_acc_img: 1, cursed: 0, location: 0, iconFile: "peacoat.png", - accIcon: 0, + accIcon: "peacoat_acc.png", notuck: 1, pregType: "min", }, @@ -2371,7 +2504,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "gothic_jacket.png", @@ -2462,6 +2595,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, + altsleeve: "none", breast_img: 0, cursed: 0, location: 0, @@ -2600,6 +2734,7 @@ function initUpper() { accessory_colour_sidebar: 1, sleeve_img: 1, breast_img: 1, + breasts_under_acc: 1, cursed: 0, location: 0, iconFile: "sailor_shirt.png", @@ -2646,6 +2781,7 @@ function initUpper() { accessory_colour_sidebar: 1, sleeve_img: 1, breast_img: 1, + breasts_under_acc: 1, cursed: 0, location: 0, iconFile: "short_sailor_shirt.png", @@ -2656,8 +2792,8 @@ function initUpper() { { index: 58, - name: "padded football shirt", - name_cap: "Padded football shirt", + name: "foreign football shirt", + name_cap: "Foreign football shirt", variable: "football", integrity: 240, integrity_max: 240, @@ -2727,7 +2863,7 @@ function initUpper() { type: ["normal", "athletic", "school"], set: "upper", gender: "n", - femininity: 200, + femininity: 0, warmth: 35, cost: 3000, description: "PE shirt from a foreign land.", @@ -2830,7 +2966,7 @@ function initUpper() { accessory_colour_options: [], accessory_colour_combat: "white", sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "hunting_coat.png", @@ -2877,7 +3013,8 @@ function initUpper() { accessory_colour_combat: "white", sleeve_img: 1, sleeve_acc_img: 1, - breast_img: 0, + sleeve_colour: "none", + breast_img: 1, cursed: 0, location: 0, iconFile: "letterman_jacket.png", @@ -2968,7 +3105,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "shadbelly_coat.png", @@ -3050,6 +3187,7 @@ function initUpper() { set: "diving", gender: "n", femininity: 0, + formfitting: 1, warmth: 40, cost: 22000, description: "Will insulate you underwater, letting you swim in the cold.", @@ -3063,6 +3201,7 @@ function initUpper() { breast_acc_img: 1, cursed: 0, location: 0, + mainImage: 0, iconFile: "diving_suit.png", accIcon: "diving_suit_acc.png", outfitPrimary: { lower: "diving suit bottom" }, @@ -3212,8 +3351,8 @@ function initUpper() { { index: 70, - name: "soccer shirt", - name_cap: "Soccer shirt", + name: "football shirt", + name_cap: "Football shirt", variable: "soccer", integrity: 120, integrity_max: 120, @@ -3463,6 +3602,7 @@ function initUpper() { set: "catsuit", gender: "n", femininity: 0, + formfitting: 1, warmth: 25, cost: 12000, description: "Sleek latex.", @@ -3673,7 +3813,7 @@ function initUpper() { colour_sidebar: 0, exposed: 0, exposed_base: 0, - type: ["rag", "bellyShow", "costume"], + type: ["bellyShow", "costume"], set: "upper", gender: "n", warmth: 10, @@ -3874,6 +4014,7 @@ function initUpper() { accessory: 1, accessory_colour: 0, accessory_colour_options: [], + accessory_colour_sidebar: "none", sleeve_img: 1, sleeve_acc_img: 1, breast_img: 1, @@ -3914,6 +4055,7 @@ function initUpper() { type: ["formal"], set: "keyhole", gender: "f", + formfitting: 1, warmth: 25, cost: 12000, description: "Displays your bosom.", @@ -4227,7 +4369,7 @@ function initUpper() { colour_combat: "brown", exposed: 0, exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "scarecrow", gender: "n", femininity: 0, @@ -4390,7 +4532,24 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -4405,7 +4564,8 @@ function initUpper() { accessory: 1, accessory_colour: 0, accessory_colour_options: [], - sleeve_img: 0, + sleeve_img: 1, + sleeve_colour: "none", breast_img: 1, cursed: 0, location: 0, @@ -4436,7 +4596,24 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -4444,6 +4621,7 @@ function initUpper() { set: "shortballgown", gender: "f", femininity: 200, + formfitting: 1, warmth: 40, cost: 48000, description: "Scandalously short.", @@ -4452,7 +4630,7 @@ function initUpper() { accessory_colour: 0, accessory_colour_options: [], sleeve_img: 0, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "short_ballgown.png", @@ -4608,6 +4786,7 @@ function initUpper() { set: "pink nurse dress", gender: "f", femininity: 200, + formfitting: 1, warmth: 30, cost: 2500, description: "Worn by nurses at the local hospital.", @@ -4656,6 +4835,7 @@ function initUpper() { set: "plastic nurse dress", gender: "f", femininity: 200, + formfitting: 1, warmth: 30, cost: 4500, description: "Easy to clean.", @@ -4704,6 +4884,7 @@ function initUpper() { set: "transparent nurse dress", gender: "f", femininity: 200, + formfitting: 1, warmth: 30, cost: 5000, description: "Hides little.", @@ -4772,7 +4953,9 @@ function initUpper() { ], accessory_colour_sidebar: 1, sleeve_img: 1, - breast_img: 0, + breast_img: 1, + breast_acc_img: 1, + mainImage: 0, cursed: 0, location: 0, iconFile: "hanfu.png", @@ -4863,8 +5046,9 @@ function initUpper() { accessory: 1, accessory_colour: 0, accessory_colour_options: ["tan", "white", "custom"], - accessory_colour_sidebar: 1, + accessory_colour_sidebar: "secondary", sleeve_img: 1, + sleeve_acc_img: 1, breast_img: 1, cursed: 0, location: 0, @@ -4911,6 +5095,7 @@ function initUpper() { accessory_colour_options: [], sleeve_img: 0, breast_img: 0, + mask_img: 1, cursed: 0, location: 0, iconFile: "", @@ -4944,7 +5129,7 @@ function initUpper() { colour_combat: "black", exposed: 0, exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "skele", gender: "n", femininity: 0, @@ -4990,7 +5175,7 @@ function initUpper() { colour_sidebar: 0, exposed: 0, exposed_base: 0, - type: ["costume", "event", "formal"], + type: ["costume", "formal"], set: "upper", gender: "m", femininity: -200, @@ -5003,7 +5188,7 @@ function initUpper() { accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], accessory_colour_sidebar: 1, sleeve_img: 1, - breast_img: 0, + breast_img: 1, cursed: 0, location: 0, iconFile: "classy_vampire_jacket.png", @@ -5040,6 +5225,7 @@ function initUpper() { set: "upper", gender: "f", femininity: 300, + formfitting: 1, warmth: 30, cost: 4000, description: "You're asking for trouble if you wear this.", @@ -5081,10 +5267,11 @@ function initUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["costume", "event"], + type: ["costume"], set: "futuresuit", gender: "n", femininity: 0, + formfitting: 1, warmth: 30, cost: 6000, description: "Slick and robotic.", @@ -5131,6 +5318,7 @@ function initUpper() { set: "lacegown", gender: "f", femininity: 200, + formfitting: 1, warmth: 15, cost: 4800, description: "Delicate and revealing.", @@ -5289,6 +5477,7 @@ function initUpper() { accessory_colour_sidebar: 1, accessory_integrity_img: 1, sleeve_img: 1, + altsleeve: "none", breast_img: 1, back_img: 1, back_img_colour: "secondary", @@ -5338,6 +5527,7 @@ function initUpper() { accessory_colour_options: [], accessory_colour_sidebar: 0, sleeve_img: 1, + altsleeve: "none", breast_img: 1, has_collar: 1, cursed: 0, @@ -5466,7 +5656,7 @@ function initUpper() { colour_combat: "black", exposed: 0, exposed_base: 0, - type: ["normal", "costume", "bellyHide"], + type: ["costume", "bellyHide"], set: "shrinemaiden", gender: "f", femininity: 200, @@ -5526,6 +5716,7 @@ function initUpper() { accessory_colour_sidebar: "secondary", sleeve_img: 1, sleeve_acc_img: 1, + sleeve_colour: "primary", breast_img: 1, cursed: 0, location: 0, @@ -5790,7 +5981,24 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_options: [ + "black", + "navy blue", + "wine", + "blue", + "brown", + "green", + "pink", + "light pink", + "purple", + "lilac", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], colour_sidebar: 1, exposed: 0, exposed_base: 0, @@ -5798,6 +6006,7 @@ function initUpper() { set: "haltersundress", gender: "f", femininity: 200, + formfitting: 1, warmth: 30, cost: 1500, description: "Great for frolicking.", @@ -5836,14 +6045,33 @@ function initUpper() { state_top_base: "chest", plural: 0, colour: 0, - colour_options: [], - colour_sidebar: 0, + colour_options: [ + "black", + "wine", + "blue steel", + "light pink", + "silver", + "gold", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "brown", + "custom", + ], + colour_sidebar: 1, exposed: 0, exposed_base: 0, type: ["normal", "formal"], set: "leatherdress", gender: "f", femininity: 200, + formfitting: 1, warmth: 15, cost: 13000, description: "A little black dress, for making heads turn.", @@ -5916,6 +6144,7 @@ function initUpper() { sleeve_img: 1, sleeve_acc_img: 1, breast_img: 1, + breasts_under_acc: 1, cursed: 0, location: 0, iconFile: "serafuku_new.png", @@ -5948,7 +6177,7 @@ function initUpper() { colour_sidebar: 1, exposed: 0, exposed_base: 0, - type: ["normal", "bellyHide"], + type: ["normal", "bellyShow"], set: "upper", gender: "n", warmth: 60, @@ -5995,10 +6224,12 @@ function initUpper() { set: "openshoulderlolita", gender: "f", femininity: 200, + formfitting: 1, warmth: 20, cost: 11000, description: "A blend of fashions.", shop: ["clothing"], + shopGroup: "openshoulderlolita", accessory: 1, accessory_colour: 0, accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], @@ -6052,6 +6283,7 @@ function initUpper() { accessory_integrity_img: 1, sleeve_img: 1, sleeve_colour: "secondary", + altsleeve: "none", breast_img: 1, has_collar: 1, cursed: 0, @@ -6090,11 +6322,12 @@ function initUpper() { set: "jingledress", gender: "f", femininity: 200, + formfitting: 1, warmth: 70, cost: 8000, description: "Festive. The skirt is rather short.", shop: ["forest"], - shopGroup:"jingle", + shopGroup: "jingle", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -6139,7 +6372,7 @@ function initUpper() { cost: 8000, description: "Festive. The skirt is rather short.", shop: ["forest"], - shopGroup:"jingle", + shopGroup: "jingle", accessory: 0, accessory_colour: 0, accessory_colour_options: [], @@ -6392,6 +6625,989 @@ function initUpper() { notuck: 0, pregType: "min", }, + { + index: 136, + name: "leather crop top", + name_cap: "Leather crop top", + variable: "leathercroptop", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 500, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["normal", "bellyShow"], + set: "upper", + gender: "f", + femininity: 200, + warmth: 20, + cost: 6500, + description: "Show off your tummy.", + shop: ["clothing", "cool", "adult"], + shopGroup: "leathercroptop", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "leather_crop_top.png", + accIcon: 0, + notuck: 1, + pregType: 0, + }, + { + index: 137, + name: "zipped leather crop top", + name_cap: "Zipped leather crop top", + variable: "leathercroptopzip", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 500, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["normal", "cool", "bellyShow"], + set: "upper", + gender: "f", + femininity: 200, + warmth: 20, + cost: 6500, + description: "Show off your tummy.", + shop: ["clothing", "adult"], + shopGroup: "leathercroptop", + accessory: 1, + accessory_colour_options: ["silver", "gold", "steel"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "leather_crop_top.png", + accIcon: "leather_crop_top_acc.png", + notuck: 1, + pregType: 0, + }, + { + index: 138, + name: "leather top", + name_cap: "Leather top", + variable: "leathertop", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 500, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["normal", "cool"], + set: "upper", + gender: "f", + femininity: 200, + formfitting: 1, + warmth: 20, + cost: 7000, + description: "Delinquent.", + shop: ["clothing", "cool", "adult"], + shopGroup: "leathertop", + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "leather_top.png", + accIcon: 0, + notuck: 1, + pregType: 0, + }, + { + index: 139, + name: "zipped leather top", + name_cap: "Zipped leather top", + variable: "leathertopzip", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 500, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["normal", "cool"], + set: "upper", + gender: "f", + femininity: 200, + formfitting: 1, + warmth: 20, + cost: 7000, + description: "Delinquent.", + shop: ["clothing", "cool", "adult"], + shopGroup: "leathertop", + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["silver", "gold", "steel"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "leather_top.png", + accIcon: "leather_top_acc.png", + notuck: 1, + pregType: 0, + }, + { + index: 140, + name: "cropped leather jacket", + name_cap: "Cropped leather jacket", + variable: "leathercropjacket", + integrity: 240, + integrity_max: 240, + fabric_strength: 20, + reveal: 500, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: [ + "black", + "brown", + "blue steel", + "tan", + "olive", + "wine", + "blue", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "custom", + ], + colour_combat: "black", + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["cool", "bellyShow"], + set: "upper", + gender: "f", + femininity: 100, + warmth: 20, + cost: 8000, + description: "Delinquent.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["silver", "blue steel", "gold", "white"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + sleeve_img: 1, + breast_img: 1, + altposition: "none", + altdisabled: ["sleeves"], + cursed: 0, + location: 0, + iconFile: "cropped_leather_jacket.png", + accIcon: "cropped_leather_jacket_acc.png", + notuck: 1, + pregType: "min", + }, + { + index: 141, + name: "sexy nun's habit", + name_cap: "Sexy nun's habit", + variable: "nunlewd", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 500, + bustresize: 0, + word: "a", + one_piece: 1, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: [], + colour_combat: "black", + exposed: 0, + exposed_base: 0, + type: ["holy", "costume", "fetish"], + set: "nunlewd", + gender: "f", + femininity: 200, + formfitting: 1, + warmth: 30, + cost: 11000, + description: "A habit better suited for the bedroom.", + shop: ["forest"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "sexy_nuns_habit.png", + accIcon: 0, + outfitPrimary: { lower: "sexy nun's habit skirt" }, + notuck: 0, + pregType: 0, + }, + { + index: 142, + name: "sexy priest's vestments", + name_cap: "Sexy priest's vestments", + variable: "monklewd", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 700, + bustresize: 0, + word: "a", + one_piece: 1, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: [], + colour_combat: "black", + exposed: 2, + exposed_base: 2, + type: ["holy", "costume", "fetish", "bellyShow"], + set: "monklewd", + gender: "m", + femininity: -200, + warmth: 15, + cost: 6000, + description: "Puts your Holy Spirit on display.", + shop: ["forest"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "sexy_priests_vestments.png", + accIcon: 0, + outfitPrimary: { lower: "sexy priest's shorts" }, + notuck: 0, + pregType: 0, + }, + { + index: 143, + name: "waistcoat", + name_cap: "Waistcoat", + variable: "waistcoat", + integrity: 160, + integrity_max: 160, + fabric_strength: 20, + reveal: 600, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["formal"], + set: "upper", + gender: "m", + femininity: -200, + warmth: 40, + cost: 20000, + description: "Smart and sophisticated.", + shop: ["clothing"], + shopGroup: "waistcoat", + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + sleeve_img: 1, + sleeve_colour: "secondary", + breast_img: 1, + has_collar: 1, + altposition: "none", + altdisabled: ["full", "sleeves"], + altsleeve: "none", + cursed: 0, + location: 0, + iconFile: "waistcoat.png", + accIcon: "waistcoat_acc.png", + notuck: 1, + pregType: "min", + }, + { + index: 144, + name: "lapelled waistcoat", + name_cap: "Lapelled waistcoat", + variable: "waistcoatlapel", + integrity: 160, + integrity_max: 160, + fabric_strength: 20, + reveal: 600, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["formal"], + set: "upper", + gender: "m", + femininity: -200, + warmth: 40, + cost: 20000, + description: "Smart and sophisticated.", + shop: ["clothing"], + shopGroup: "waistcoat", + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + sleeve_img: 1, + sleeve_colour: "secondary", + breast_img: 1, + has_collar: 1, + altposition: "none", + altdisabled: ["full", "sleeves"], + altsleeve: "none", + cursed: 0, + location: 0, + iconFile: "waistcoat_lapel.png", + accIcon: "waistcoat_lapel_acc.png", + notuck: 1, + pregType: "min", + }, + { + index: 145, + name: "long waistcoat", + name_cap: "Long waistcoat", + variable: "waistcoatlong", + integrity: 160, + integrity_max: 160, + fabric_strength: 20, + reveal: 600, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["formal"], + set: "upper", + gender: "m", + femininity: -200, + warmth: 40, + cost: 20000, + description: "Smart and sophisticated.", + shop: ["clothing"], + shopGroup: "waistcoat", + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + accessory_colour_sidebar: 1, + sleeve_img: 1, + sleeve_colour: "secondary", + breast_img: 1, + has_collar: 1, + altsleeve: "none", + cursed: 0, + location: 0, + iconFile: "waistcoat_long.png", + accIcon: "waistcoat_long_acc.png", + notuck: 1, + pregType: "min", + }, + { + index: 146, + name: "long lapelled waistcoat", + name_cap: "Long lapelled waistcoat", + variable: "waistcoatlonglapel", + integrity: 160, + integrity_max: 160, + fabric_strength: 20, + reveal: 600, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["formal"], + set: "upper", + gender: "m", + femininity: -200, + warmth: 40, + cost: 20000, + description: "Smart and sophisticated.", + shop: ["clothing"], + shopGroup: "waistcoat", + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + accessory_colour_sidebar: 1, + sleeve_img: 1, + sleeve_colour: "secondary", + breast_img: 1, + has_collar: 1, + altsleeve: "none", + altdisabled: ["sleeves"], + cursed: 0, + location: 0, + iconFile: "waistcoat_long_lapel.png", + accIcon: "waistcoat_long_lapel_acc.png", + notuck: 1, + pregType: "min", + }, + { + index: 147, + name: "shirt and blazer", + name_cap: "Shirt and blazer", + variable: "blazershirt", + integrity: 160, + integrity_max: 160, + fabric_strength: 20, + reveal: 400, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 1, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["formal"], + set: "upper", + gender: "m", + femininity: -100, + warmth: 40, + cost: 16000, + description: "Smart and sophisticated.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + sleeve_img: 1, + sleeve_colour: "primary", + breast_img: 1, + has_collar: 1, + altposition: "none", + cursed: 0, + location: 0, + iconFile: "blazer_shirt.png", + accIcon: "blazer_shirt_acc.png", + notuck: 1, + pregType: "split", + }, + { + index: 148, + name: "oversized button-down", + name_cap: "Oversized button-down", + variable: "oversizedbuttondown", + integrity: 120, + integrity_max: 120, + fabric_strength: 20, + reveal: 500, + bustresize: -1, + word: "an", + one_piece: 1, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["normal", "bellyHide", "sleep"], + set: "oversizedbuttondown", + gender: "n", + warmth: 40, + cost: 7000, + description: "Soft enough to use as a sleep shirt; long enough to cover your unmentionables. Just.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 1, + altsleeve: "none", + breast_img: 0, + has_collar: 1, + cursed: 0, + location: 0, + iconFile: "oversized_button_down.png", + accIcon: 0, + outfitPrimary: { lower: "oversized button-down bottom" }, + notuck: 0, + pregType: "min", + }, + { + index: 149, + name: "button-down", + name_cap: "Button-down", + variable: "buttondown", + integrity: 160, + integrity_max: 160, + fabric_strength: 20, + reveal: 600, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["white", "black", "brown", "light pink", "light blue", "lilac", "russet", "tan", "grey", "olive", "wine", "navy blue", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["formal", "school"], + set: "upper", + gender: "n", + femininity: 0, + warmth: 40, + cost: 12000, + description: "For when you need to look your best.", + shop: ["clothing", "school"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + accessory_colour_sidebar: 0, + sleeve_img: 1, + sleeve_colour: "primary", + altsleeve: "none", + breast_img: 1, + has_collar: 1, + cursed: 0, + location: 0, + iconFile: "button_down.png", + notuck: 0, + pregType: "split", + }, + { + index: 150, + name: "life vest", + name_cap: "Life vest", + variable: "lifevest", + integrity: 200, + integrity_max: 200, + fabric_strength: 30, + reveal: 400, + bustresize: -7, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["swim", "chest_bind", "bellyHide"], + set: "upper", + gender: "n", + warmth: 40, + cost: 1000, + description: "Keeps you afloat.", + shop: ["clothing"], + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_sidebar: 1, + sleeve_img: 0, + breast_img: 0, + cursed: 0, + location: 0, + iconFile: "life_vest.png", + accIcon: "life_vest_acc.png", + notuck: 1, + pregType: "min", + }, + + { + index: 151, + name: "harem vest", + name_cap: "Harem vest", + variable: "haremvest", + integrity: 150, + integrity_max: 150, + fabric_strength: 20, + reveal: 700, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["costume", "serving", "dance", "bellyShow"], + set: "upper", + gender: "m", + femininity: -200, + warmth: 10, + cost: 12000, + description: "Exotic, and sturdier than it looks.", + shop: ["forest"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "harem_vest.png", + accIcon: 0, + notuck: 1, + pregType: "split", + }, + { + index: 152, + name: "long-sleeved shirt", + name_cap: "Long-sleeved shirt", + variable: "regularshirt", + integrity: 200, + integrity_max: 200, + fabric_strength: 20, + reveal: 200, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + colour_combat: 0, + exposed: 0, + exposed_base: 0, + type: ["normal"], + set: "upper", + gender: "n", + warmth: 20, + cost: 2000, + description: "Just a regular, masculine shirt.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 1, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "long_sleeved_shirt.png", + accIcon: 0, + notuck: 0, + pregType: "min", + }, + { + index: 153, + name: "sexy butler top", + name_cap: "Sexy butler top", + variable: "slutler", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 700, + bustresize: 0, + word: "a", + one_piece: 0, + strap: 0, + open: 1, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: [], + colour_combat: "black", + exposed: 0, + exposed_base: 0, + type: ["maid", "costume", "serving"], + gender: "m", + femininity: -200, + warmth: 15, + cost: 2500, + description: "Business in the front, party in the back.", + shop: ["adult"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 0, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "sexy_butler_top.png", + accIcon: 0, + notuck: 1, + pregType: "min", + }, + { + index: 154, + name: "classic open shoulder lolita dress", + name_cap: "Classic open shoulder lolita dress", + variable: "openshoulderlolitaclassic", + integrity: 70, + integrity_max: 70, + fabric_strength: 30, + reveal: 600, + bustresize: 0, + word: "a", + one_piece: 1, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["normal"], + set: "classicopenshoulderlolita", + gender: "f", + femininity: 200, + warmth: 20, + cost: 11000, + description: "A blend of fashions.", + shop: ["clothing"], + shopGroup: "openshoulderlolita", + accessory: 1, + accessory_colour: 0, + accessory_colour_options: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom"], + accessory_colour_sidebar: 1, + accessory_integrity_img: 1, + sleeve_img: 1, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "classic_open_shoulder_lolita_dress.png", + accIcon: "classic_open_shoulder_lolita_dress_acc.png", + outfitPrimary: { lower: "classic lolita skirt" }, + notuck: 0, + pregType: "min", + }, + { + index: 155, + name: "jumpsuit", + name_cap: "Jumpsuit", + variable: "jumpsuitstylish", + integrity: 100, + integrity_max: 100, + fabric_strength: 20, + reveal: 200, + word: "a", + one_piece: 1, + strap: 0, + open: 0, + state: "waist", + state_base: "waist", + state_top: "chest", + state_top_base: "chest", + plural: 0, + colour: 0, + colour_options: [ + "black", + "blue", + "brown", + "green", + "pink", + "purple", + "red", + "tangerine", + "teal", + "white", + "yellow", + "light pink", + "light blue", + "lilac", + "olive", + "tan", + "russet", + "neon blue", + "custom", + ], + colour_combat: 0, + colour_sidebar: 1, + exposed: 0, + exposed_base: 0, + type: ["normal"], + set: "jumpsuitstylish", + gender: "n", + formfitting: 1, + warmth: 40, + cost: 9000, + description: "Trendy and retro.", + shop: ["clothing"], + accessory: 0, + accessory_colour: 0, + accessory_colour_options: [], + sleeve_img: 1, + breast_img: 1, + cursed: 0, + location: 0, + iconFile: "jumpsuit.png", + accIcon: 0, + outfitPrimary: { lower: "jumpsuit trousers" }, + notuck: 0, + pregType: "min", + }, ]; /* diff --git a/game/base-clothing/images.twee b/game/base-clothing/images.twee index 7df595b7e..31d69f2e9 100644 --- a/game/base-clothing/images.twee +++ b/game/base-clothing/images.twee @@ -4,14 +4,14 @@ <> <>
-
- <> - 총 콘돔 갯수: $condoms -
-
- <> - 후추 스프레이: $spray / $spraymax -
+ <> +
<>
+ <> +
+ <
> + <> +
<>
+ <
>
<> diff --git a/game/base-clothing/shop.twee b/game/base-clothing/shop.twee index d7e21c747..48c93a4bb 100644 --- a/game/base-clothing/shop.twee +++ b/game/base-clothing/shop.twee @@ -86,7 +86,7 @@


- <> + <> <> <><><><><><><><>
@@ -219,7 +219,7 @@ <> <> 몇 미터 앞에 있는 바닥의 뚜껑문을 열고 숨겨져 있는 칸막이 안에서 스노우볼을 하나 꺼낸다. 당신은 어리벙벙한 눈으로 쳐다본다. <> - <> 신발 진열대로 똑바로 가서, 샌들 더미 사이를 뒤지더니 속에서 스노우볼을 하나 꺼낸다. 당신은 어리벙벙한 눈으로 쳐다본다. + <> beelines for the shoe section, rummages through the sandals and pulls a snow globe out from the pile. You stare in bemusement. <> <> 마녀 드레스를 밀어 치우고 숨겨져 있는 관리실 같은 곳 안으로 들어간다. <> 스노우볼을 하나 들고 나온다. 당신은 어리벙벙한 눈으로 쳐다본다. <> diff --git a/game/base-clothing/storeActions.twee b/game/base-clothing/storeActions.twee index f2ce88572..62a7ad393 100644 --- a/game/base-clothing/storeActions.twee +++ b/game/base-clothing/storeActions.twee @@ -215,8 +215,10 @@ <> <> - 당신은 <> 내려놓았다. -
+ <> + 당신은 <> 내려놓았다. +
+ <
> <> <
> @@ -240,7 +242,7 @@ <
> <> - <> + <>
<><><><>
@@ -261,90 +263,92 @@ _under_lower_protected to !$worn.under_lower.exposed; >> - <> - - <> - - <><> + <> + <> + + <> + + <><> + <> <> - <> - <> - - <> - <><> + <> + + <> + <><> + <> <> - <> - <> - - <> - <><> + <> + + <> + <><> + <> <> - <> - <> - <> - <><> + <> + <> + <><> + <> <> - <> - <> - <> - <><> + <> + <> + <><> + <> <> - <> - <> - <> - <><> + <> + <> + <><> + <> <> - <> - <> - <> - <><> + <> + <> + <><> + <> <> - <> - <> - <> - <><> + <> + <> + <><> + <> <> - <> - <> - <> - <><> + <> + <> + <><> + <> <> - <> - <> - <><> - <> + <> + <><> + <> - <> - <><> - <> + <> + <><> + <> - <> - <><> - <> + <> + <><> + <> - <> - <><> - <> + <> + <><> + <> - <> - <><> - <> + <> + <><> + <> - <> - <><> - <> + <> + <><> + <> - <> - <><> + <> + <><> + <> <> <> @@ -352,35 +356,44 @@ <> <><> - 당신의 옷을 벗<>고 숨긴<>는<>다... - - <> - <> - <> - <> - <> - <> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <><> - <> - <> - <> -

+ <> + 당신의 옷을 벗<>고 숨긴<>는<>다... + <> + <> + <> + <> + <> + <> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <><> + <> + <> + <> +

+ <> +
+ <> + <> + <> + <> + <> +
+ <
> <
> <> diff --git a/game/base-clothing/updateClothes.js b/game/base-clothing/update-clothes.js similarity index 73% rename from game/base-clothing/updateClothes.js rename to game/base-clothing/update-clothes.js index e634fc0e9..1a5e53c71 100644 --- a/game/base-clothing/updateClothes.js +++ b/game/base-clothing/update-clothes.js @@ -16,20 +16,28 @@ function updateClothingColours(item, itemRef) { if (item.accessory_colour === 0) item.accessory_colour = "tan"; break; // eslint-disable-next-line no-fallthrough - case "cool shades": - case "square shades": + case "long leather gloves": + case "leather dress": case "round shades": + case "witch shoes": + if (item.colour === 0) item.colour = "black"; + break; + case "square shades": case "shield shades": case "punk shades": - case "witch shoes": if (item.colour === 0) item.colour = "black"; + if (item.accessory_colour === 0) item.colour = "black"; break; case "aviators": if (item.colour === 0) item.colour = "grey"; + if (item.accessory_colour === 0) item.colour = "original"; break; case "glasses": if (item.colour === 0) item.colour = "silver"; break; + case "cat eye shades": + if (item.accessory_colour === 0) item.colour = "original"; + break; case "lace choker": if (item.colour === 0) item.colour = "black"; break; @@ -39,6 +47,36 @@ function updateClothingColours(item, itemRef) { item.accessory_colour_combat = "light blue"; } break; + case "brown leather jacket": + if (item.colour === 0) item.colour = "brown"; + break; + case "love locket": + if (item.colour === 0) item.colour = "bronze"; + break; + case "black leather jacket": + if (item.colour === 0) item.colour = "black"; + if (item.accessory_colour === 0) item.accessory_colour = "silver"; + break; + case "overall bottoms": + case "overalls": + if (item.colour === 0) item.colour = "original"; + if (item.accessory_colour === 0) item.accessory_colour = "gold"; + break; + case "jeans": + if (item.colour === 0) item.colour = "original"; + break; + case "loose socks": + if (item.colour === 0) item.colour = "white"; + break; + case "argyle sweater vest": + if (item.accessory_colour === 0) { + item.accessory_colour = item.colour !== "custom" ? item.colour : "black"; + } + break; + case "cowboy hat": + if (item.colour === 0) item.colour = "sand"; + if (item.accessory_colour === 0) item.accessory_colour = "black"; + break; default: // Catch-all case if people forget to adjust this widget for whatever clothing item is updated. Can make weird looking clothes if "custom" is selected. if (item.colour === 0) item.colour = itemRef.colour_options.random(); @@ -141,7 +179,7 @@ function updateClothesItem(slot, item, debug) { if (item.accessory_colour_combat !== undefined && itemRef.colour_options.length === 0) item.accessory_colour = 0; // end of fix if (slot === "genitals") return; - // put renamed clothes here + // put renamed clothes and updated types here switch (item.name) { case "Crop top": item.name = "crop top"; @@ -153,6 +191,104 @@ function updateClothesItem(slot, item, debug) { case "sleeveless jingle-bell dress": if (item.outfitPrimary.lower === "jingle-bell skirt") item.outfitPrimary.lower = "sleeveless jingle-bell skirt"; break; + case "Rib-knit ankle socks": + item.name = "rib-knit ankle socks"; + break; + case "Striped kneehighs": + item.name = "striped kneehighs"; + break; + case "brown leather jacket": + item.name = "leather jacket"; + item.name_cap = "Leather jacket"; + break; + case "black leather jacket": + item.name = "punk leather jacket"; + item.name_cap = "Punk leather jacket"; + break; + case "swim shirt": + item.type = ["swim", "school", "chest_bind", "constricting", "covered"]; + break; + case "undershirt": + case "long johns": + item.type = ["normal", "covered"]; + break; + case "unitard bottom": + case "leotard bottom": + case "unitard": + case "leotard": + case "turtleneck leotard": + case "skimpy leotard": + item.type = ["dance", "covered"]; + break; + case "turtleneck leotard bottom": + case "skimpy leotard bottom": + item.type = ["dance"]; + break; + case "sports bra": + item.type = ["normal", "athletic", "covered"]; + break; + case "witch dress": + case "scarecrow shirt": + case "rag skirt": + case "skeleton outfit": + case "pom poms": + case "futuristic bodysuit": + case "witch skirt": + case "scarecrow skirt": + case "futuristic bodysuit pants": + case "skeleton bottoms": + case "cheerleader gloves": + item.type = ["costume"]; + break; + case "rag top": + case "vampire jacket": + item.type = ["costume", "bellyShow"]; + break; + case "classy vampire jacket": + item.type = ["costume", "formal"]; + break; + case "skeleton mask": + item.type = ["costume", "mask"]; + break; + case "riding helmet": + case "racing helmet": + item.type = ["costume", "riding"]; + break; + case "scout shorts": + case "baseball cap": + item.type = ["normal"]; + break; + case "purse": + case "backpack": + case "messenger bag": + case "heart purse": + item.type = ["school", "bookbag"]; + break; + case "boy's gym socks": + case "girl's gym socks": + item.type = ["school", "athletic"]; + break; + case "padded football shirt": + item.name = "foreign football shirt"; + item.name_cap = "Foreign football shirt"; + break; + case "football shorts": + item.name = "foreign football shorts"; + item.name_cap = "Foreign football shorts"; + break; + case "football helmet": + item.name = "foreign football helmet"; + item.name_cap = "Foreign football helmet"; + item.type = ["costume"]; + break; + case "soccer shorts": + item.name = "football shorts"; + item.name_cap = "Football shorts"; + break; + case "soccer shirt": + item.name = "football shirt"; + item.name_cap = "Football shirt"; + break; } if (debug) console.log("updateClothesItem:", slot, itemOld, clone(item)); } @@ -342,5 +478,11 @@ function wardrobesUpdate() { if (wardrobe && Array.isArray(wardrobe.upper) && !wardrobe.handheld) wardrobe.handheld = []; }); } + if (!V.wardrobes.officeBuilding) { + V.wardrobes.officeBuilding = clone(defWardrobe); + V.wardrobes.officeBuilding.name = "Office agency changing room"; + V.wardrobes.officeBuilding.unlocked = V.officejobintro === 1; + V.wardrobes.officeBuilding.space = 5; + } } DefineMacro("wardrobesUpdate", wardrobesUpdate); diff --git a/game/base-clothing/wardrobes.twee b/game/base-clothing/wardrobes.twee index 5d7096f1a..b18f4c122 100644 --- a/game/base-clothing/wardrobes.twee +++ b/game/base-clothing/wardrobes.twee @@ -139,7 +139,7 @@ <> <> <> - 당신은 귓속의 슬라임이 속옷을 입지 말라고 명령해서 <>_trResult 입을 수가 없다 + You are unable to equip your _item.name, as the slime in your ear is forcing you to go commando.
<> <> @@ -151,7 +151,7 @@ <> <> <> - 당신은 귓속의 슬라임이 더 <> 복장만을 입도록 해서 <>_trResult 입을 수가 없다. + You are unable to equip your _item.name, as the slime in your ear is only permitting more <> clothing.
<> <> @@ -335,6 +335,26 @@
<><><>
| + <> +
+ + <> + <> + <> + <> + <> + <> + + + <> + <> + <> + <> + <> + <> + +
| + <
> <> +<> + <> + <> +
+ <> + <> + <> + <> + <> + <> +
+<
> + <> <> <> @@ -715,6 +748,9 @@ <> <> <><> + <><> + <><> + <><> <><> <> <> @@ -722,6 +758,19 @@ <><> <><> <> + <> + <> + <> + <><> + <><> + <> + <> + <> + <><> + <><> + <><> + <> + <> <><> <> <> @@ -766,28 +815,41 @@ <> <> <
> - <> - <> - <> - <> - <> - <> + <> + <> + <> + <> + <> + <> <> <> <> +<> + <> + <> +
+ <> + <> + <> + <> + <> + <> +
+<
> + <> /*changes z-index */ <> <>
- <> + <> <> <> - <> + <> <> <> - <> + <> <> <>
@@ -901,15 +963,20 @@
<>

<
> - <> + <>
<>
+ <> +
<>
+ <
> + <> +
<>
<
> <>
<>
<
> <> - + <> <
>
@@ -1458,7 +1525,7 @@ <> <> - <><><><><> + <><><><><><> <> <> @@ -1471,7 +1538,7 @@ <> <><><><> <> - <><><><> + <><><><><> <> 이 꼴로는 나갈 수 없다! <> @@ -1485,7 +1552,7 @@ <> <> <> - 귓속의 슬라임이 수영복을 입은 채 나가는 것을 허락하지 않는다. + The slime in your ear refuses to allow you to leave with <> on. <> <><><><> <> @@ -1545,10 +1612,30 @@ <><><> <> <><><> + <> + <> + <><><><> + <> + <><><><><> + <> + 이 꼴로는 나갈 수 없다! + <> <>

<
> +<><> + <> + <> + <> + <> + <> + <> + <> + <> +<><><> +<> + :: Wardrobe [exitCheckBypass] <> <> @@ -1658,6 +1745,16 @@ <> +:: Office Agency Changing Room [exitCheckBypass] +<><> +<> +<> +You are in the small storage room sanctioned for temps to use for changing. +

+
<>
+
<>
+<> + :: Wardrobe Sale Crate 정말 당신의 모든 diff --git a/game/base-clothing/widgets.twee b/game/base-clothing/widgets.twee index b3ca2eff0..eda537aa5 100644 --- a/game/base-clothing/widgets.twee +++ b/game/base-clothing/widgets.twee @@ -456,18 +456,9 @@ /*temporarily equips a handheld "prop" for visual effect in scenes. props can be cleared earlier in a scene via <> or <>, and are cleared in <>*/ <> - <> - <><> - <><> - <><> - <><> - <><> - <><> - <>ERROR: _args[0] is not a valid prop. Please report this bug. - <> - - <> + < item.variable is _args[0])>> <> + <> <> <> <> @@ -916,8 +907,16 @@ <> <> + <> - <> + <> + <> + <> + <> + <> + <> + <> + <> <> <> @@ -1226,7 +1225,7 @@ <> <
> <> - <> + <> <> <> <> @@ -1388,7 +1387,7 @@ <> < item.name is $_item.name and item.location is _args[0])>> <> - <> + <> <> <> <> @@ -1449,6 +1448,7 @@ <> <> <> + <>/* Slot not found */<><> < item.location is $_location)>> <> <> @@ -1477,7 +1477,7 @@ <> <> <> - <> + <> <> <> <> @@ -1808,7 +1808,7 @@ <> <> -
+
<>
@@ -1837,7 +1837,7 @@ <> <> -
+
diff --git a/game/base-combat/actions-hands.twee b/game/base-combat/actions-hands.twee index 6457b048c..de8c5afa6 100644 --- a/game/base-combat/actions-hands.twee +++ b/game/base-combat/actions-hands.twee @@ -1495,7 +1495,7 @@ <> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1505,7 +1505,7 @@ <><><> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1521,7 +1521,7 @@ <> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1531,7 +1531,7 @@ <><><> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1607,7 +1607,7 @@ <> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1620,7 +1620,7 @@ <><><> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1638,7 +1638,7 @@ <> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1651,7 +1651,7 @@ <><><> <> 당신은 <><> 손에서 <> 쳐낸다. - <> + <> <> 뿌루퉁하게 당신에게 입술을 내민다. <> <> @@ -1865,7 +1865,7 @@ <> <> - <> + <> 당신은 <> 손목을 잡고, <> <> 손가락을 당신의 <>에서 비틀어 떼어낸다. @@ -1907,7 +1907,7 @@ <> <> - <> + <> 당신은 <> 손목을 잡고, <> <> 손가락을 당신의 <>에서 비틀어 빼낸다. @@ -1949,7 +1949,7 @@ <> <> - <> + <> 당신은 <> 손목을 잡고, <> <> 손가락을 당신의 뒷구멍에서 비틀어 빼낸다. @@ -2270,14 +2270,14 @@ <> <> - <> + <> <> <> <> <> <> <> - <> + <> <> <> <> @@ -2295,7 +2295,7 @@ <><><><><> <> <> - <> + <> <> <> <> @@ -2308,7 +2308,7 @@ <><><><> <> <> - <> + <> <> <> <> @@ -2326,10 +2326,10 @@ <> <> <> - <> + <> <> 당신은 기생충 정조대를 꽉 쥐어짜며, 당신의 몸을 타고 흐르는 쾌감의 물결을 즐긴다. - <> + <> <> <> <> @@ -2339,7 +2339,7 @@ <> <> 당신은 안쪽 허벅지를 애무한다. - <> + <> 당신은 <> 보지를 희롱하며, 당신의 보지를 벌린다. <> 당신은 <> 보지를 희롱하며, 당신의 보지를 가리킨다. @@ -2355,7 +2355,7 @@ <> <> 당신이 부드럽게 기생충 정조대를 쥐어짜자, 그것은 당신의 몸을 타고 흐르는 쾌감의 물결을 보낸다. - <> + <> <> <> <> @@ -2365,7 +2365,7 @@ <> <> 당신은 부드럽게 안쪽 허벅지를 애무한다. - <> + <> 당신은 부드럽게 <> 보지를 희롱하며, 당신의 보지를 벌린다. <> 당신은 부드럽게 <> 보지를 희롱하며, 당신의 보지를 가리킨다. @@ -2390,14 +2390,14 @@ <> <> - <> + <> <> <> <> <> <> <> - <> + <> <> <> <> @@ -2415,7 +2415,7 @@ <><><><><> <> <> - <> + <> <> <> <> @@ -2430,7 +2430,7 @@ <><><><> <> <> - <> + <> <> <> <> @@ -2448,10 +2448,10 @@ <> <> <> - <> + <> <> 당신은 기생충 정조대를 꽉 쥐어짜며, 당신의 몸을 타고 흐르는 쾌감의 물결을 즐긴다. - <> + <> <> <> <> @@ -2461,7 +2461,7 @@ <> <> 당신은 안쪽 허벅지를 애무한다. - <> + <> 당신은 자지를 희롱하면서, 한 손의 손가락으로 육봉을 따라 훑으며 다른 손으로 그 끝에 원을 그린다. <> 당신은 <> 자지를 희롱하며, 한 손가락으로 육봉을 따라 훑는다. @@ -2477,7 +2477,7 @@ <> <> 당신이 부드럽게 기생충 정조대를 쥐어짜자, 그것은 당신의 몸을 타고 흐르는 쾌감의 물결을 보낸다. - <> + <> <> <> <> @@ -2487,7 +2487,7 @@ <> <> 당신은 부드럽게 안쪽 허벅지를 애무한다. - <> + <> 당신은 부드럽게 자지를 희롱하면서, 한 손의 손가락으로 육봉을 따라 훑으며 다른 손으로 그 끝에 원을 그린다. <> 당신은 부드럽게 <> 자지를 희롱하며, 한 손가락으로 육봉을 따라 훑는다. @@ -2512,14 +2512,14 @@ <> <> - <> + <> <> <> <> <> <> <> - <> + <> <> <> <> @@ -2537,7 +2537,7 @@ <><><><> <> <> - <> + <> <> <> <> @@ -2550,7 +2550,7 @@ <><><> <> <> - <> + <> <> <> <> @@ -2563,8 +2563,8 @@ <><><> <> <> - <> - <> + <> + <> 당신은 <> 더듬으며, 양손으로 엉덩이살을 벌린다. <> 당신은 <> <> 더듬으며, 찰싹하고 살짝 때린다. @@ -2578,7 +2578,7 @@ <> 그 생각은 당신의 정신 속에서 쫓겨나고, 다른 것으로 바뀐다. <> - <> + <> 당신은 <> 더듬으며, 양손으로 엉덩이살을 벌린다. <> 당신은 <> <> 더듬으며, 찰싹하고 살짝 때린다. diff --git a/game/base-combat/actions-text.twee b/game/base-combat/actions-text.twee index 616737191..209563888 100644 --- a/game/base-combat/actions-text.twee +++ b/game/base-combat/actions-text.twee @@ -1717,7 +1717,7 @@ <> <> <> - <> + <> "너는 어떻게 나를 흥분시키는지 너무 잘 아는 것 같아." <> "녹아버릴 것 같아, 네가 나를 너무나도 행복하게 해줘." @@ -2355,7 +2355,7 @@ <> "꺼져, 추잡한 녀석아." <> <> - "네가 비켜줬으면 좋겠어, 그게 깨끗하지 않아?" + "I want you to go away, isn't that clear?" <> "네-네가 싫어, 멍청한 <>." <> @@ -2373,7 +2373,7 @@ <> <> "로빈한테 뭔 짓을 한거야?" - <> + <> "로빈이 다친건 너 때문이야!" <> "더 이상 갈취하지 마!" @@ -2583,7 +2583,7 @@ <> "꺼져! 난 사냥꾼거야!" <> - "너...넌 무단침해하고 있어... 에덴의 소.... 아악!! 에덴의 소유물을!!" + "Y...you're trespassing on Eden's... AAH! Property!" <> <> <> @@ -2591,7 +2591,7 @@ <> "꺼져! 난 사냥꾼거야!" <> - "너...넌 무단침해하고 있어... 에덴의 소.... 아악!! 에덴의 소유물을!!" + "Y...you're trespassing on Eden's... AAH! Property!" <> <> <> diff --git a/game/base-combat/actions.twee b/game/base-combat/actions.twee index bf050263f..8780f283a 100644 --- a/game/base-combat/actions.twee +++ b/game/base-combat/actions.twee @@ -28,7 +28,6 @@ <> <> <> -
<
> <
> <
> @@ -126,4 +125,4 @@

<> <
> -<
> \ No newline at end of file +<> diff --git a/game/base-combat/actionsGeneration.twee b/game/base-combat/actionsGeneration.twee index 46a07184b..facb76f2c 100644 --- a/game/base-combat/actionsGeneration.twee +++ b/game/base-combat/actionsGeneration.twee @@ -2914,7 +2914,6 @@ <> <> <> -
<
> <
> <
> diff --git a/game/base-combat/audience.twee b/game/base-combat/audience.twee index 0504bbfb8..3425e0a3e 100644 --- a/game/base-combat/audience.twee +++ b/game/base-combat/audience.twee @@ -1231,7 +1231,11 @@ <> "누가 이 정조대 좀 풀거나 부숴 봐, 누군가는 좋은 시간을 보낼 수 있겠는데." <> - "저 장치 밑에 뭐가 있는지 볼 수 없다는 게 아쉽네." + <> + "저 장치 밑에 뭐가 있는지 볼 수 없다는 게 아쉽네." + <> + "Such a shame, I was looking forward to playing with it." + <> <
> <> <> diff --git a/game/base-combat/close-images.twee b/game/base-combat/close-images.twee index 8435d2763..e73497ee4 100644 --- a/game/base-combat/close-images.twee +++ b/game/base-combat/close-images.twee @@ -103,6 +103,7 @@ <> <> <> + <> <> <> <> @@ -247,9 +248,9 @@ <> <> <> - + <> - + <> <> diff --git a/game/base-combat/effects.twee b/game/base-combat/effects.twee index 3c71b79b7..8bd35c275 100644 --- a/game/base-combat/effects.twee +++ b/game/base-combat/effects.twee @@ -1274,7 +1274,7 @@ <><><> <><> <> - <> + <> <> <> @@ -1311,7 +1311,7 @@ <><><><><> <><> <> - <> + <> <> <> @@ -1348,7 +1348,7 @@ <><><><><> <><> <> - <> + <> <> <> @@ -1630,13 +1630,13 @@ <> <> <> - 당신은 당신의 <> 노출된 항문에 넣었다 뺐다 하면서 기대감에 차 몸을 떤다. + You pull your $worn.butt_plug.name in and out of your exposed anus and shiver in anticipation. <> 당신은 당신의 <> 노출된 항문에 넣었다 뺐다 한다. <> <> <> - 당신은 당신의 손가락을 노출된 항문에 넣었다 뺐다 하면서 기대감에 차 몸을 떤다. + You run your fingers in and out of your exposed anus and shiver in anticipation. <> 당신은 당신의 손가락을 노출된 항문에 넣었다 뺐다 한다. <> @@ -1684,13 +1684,13 @@ <> <> <> - 당신은 당신의 <> 노출된 항문에 넣었다 뺐다 하면서 기대감에 차 몸을 떤다. + You pull your $worn.butt_plug.name in and out of your exposed anus and shiver in anticipation. <> 당신은 당신의 <> 노출된 항문에 넣었다 뺐다 한다. <> <> <> - 당신은 당신의 손가락을 노출된 항문에 넣었다 뺐다 하면서 기대감에 차 몸을 떤다. + You run your fingers in and out of your exposed anus and shiver in anticipation. <> 당신은 당신의 손가락을 노출된 항문에 넣었다 뺐다 한다. <> @@ -2581,7 +2581,7 @@ <><><> <><><><><> <> - <> + <> <><><><><> <><> <>당신은 <> <> <>당신의 다리로 감싸<>향해 몸을 뒤로 밀어대며<>, <> 자지를 뽑아내는 것을 막는다. @@ -2593,7 +2593,7 @@ <> <><><> <> - <> + <> <><><><> 당신은 계속 <> <>당신의 다리로 감싸<>향해 몸을 뒤로 밀어대며<>, <> 몸을 빼낼 틈을 주지 않는다. <> @@ -3247,7 +3247,7 @@ <> <><> <> - <> + <> <><> <> <> @@ -3510,7 +3510,7 @@ 당신은 당신의 입을 침범하는 자지를 필사적으로 빤다. <> <> - <> + <> <> <> <><><> 당신을 제지하지 않는다. @@ -3552,7 +3552,7 @@ 하지만, 당신은 달콤한 꿀에서 당신을 떼어놓을 수가 없다. 당신은 필사적으로 당신의 입을 누르고 있는 보지를 핥는다. <> <> - <> + <> <><><> <><> 당신을 제지하지 않는다. <> @@ -3624,7 +3624,7 @@ <> <> <> - <> + <> <><> <><> 당신을 제지하지 않는다. <> @@ -3658,7 +3658,7 @@ <><><> 당신은 당신의 입을 위협하는 항문을 밀어내려 한다. <><> - <> + <> <> 당신을 제지하지 않는다. <><> <><><> @@ -3776,7 +3776,7 @@ 당신은 <>에게 그만 하고 싶다고 말한다. <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <> <> <> @@ -3817,7 +3817,7 @@ <> <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <><>그들은<><><> 수긍하며 고개를 끄덕인다. <> <><> @@ -3858,7 +3858,7 @@ <> <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <><>그들은<><><> 수긍하며 고개를 끄덕인다. <> <><> @@ -3899,7 +3899,7 @@ <> <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <><>그들은<><><> 수긍하며 고개를 끄덕인다. <> <> @@ -3947,7 +3947,7 @@ <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. <> "싫어," <>그들은<><><> 웃는다. "옷이 없는게 더 보기 좋다고." - <> + <> <><>그들은<><><> 수긍하며 고개를 끄덕인다. <> <> @@ -3996,7 +3996,7 @@ <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. <> "싫어," <>그들은<><><> 웃는다. "옷이 없는게 더 보기 좋다고." - <> + <> <><>그들은<><><> 수긍하며 고개를 끄덕인다. <> <> @@ -4029,7 +4029,7 @@ 당신은 <>에게 <> 손이 목을 조르는 것을 원하지 않는다고 말한다. <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <><>그들은<><><> 수긍하며 고개를 끄덕인다. <> <> @@ -4094,7 +4094,7 @@ 당신은 <>에게 콘돔을 사용해 달라고 말한다. <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <> <> <> @@ -4377,7 +4377,7 @@ <> <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <> <> <> @@ -4665,7 +4665,7 @@ <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. <> <> 잠시동안 얼어붙는다. "안돼, 내 사랑." - <> + <> <> <> <> 잠시 멈칫하더니 대답한다. @@ -4735,8 +4735,8 @@ 당신은 <>에게 <> 페니스 밴드를 착용하라고 부탁한다. <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. - <> + <> <>.","입닥쳐 <>, 여기서는 내가 결정해.")>> <><> <> @@ -4758,12 +4758,12 @@ <> <>그들은<><><> 당신의 말을 알아듣지 못해서, 그 말을 무시한다. <><><> - <> + <> <> 끄덕이고 <> <> 벗는다. /* Remove their strap-on. If they have a penis, give them back their penis. */ <> - <> + <> <> <><> <> @@ -5856,7 +5856,7 @@ <> <> 당신은 뽑아내려 하지만, 당신의 묶인 다리 때문에 그럴 수가 없다. - <> + <> <> <> <> diff --git a/game/base-combat/ejaculation-alex.twee b/game/base-combat/ejaculation-alex.twee new file mode 100644 index 000000000..f5a57f5e8 --- /dev/null +++ b/game/base-combat/ejaculation-alex.twee @@ -0,0 +1,662 @@ +:: Widgets Ejaculation-ALEX [widget] + +<> + + <> + + + + <> + <> + <> + <> + <> + <> groans and rubs <> crotch against your face with <> hips, small thighs shaking with orgasm. + + <> + <> goes red in the face and neck, mouth agape as <> cums silently, body twitching uncontrollably. + + <> + <> screams, cupping the back of your head in <> hands and grinding <> clit against your nose. + + <> + + <> + <> + <> + <> sighs as you finish with <> pussy, shaking <> slim thighs together against your hand as <> body convulses. + <><><><> + + <> + <> goes red in the face and neck, mouth agape as <> cums silently, cunt spasming against your hand. + <><><><> + + <> + <> screams, grabbing your hand and fucking <> pussy with your fingers. Your fingers are soaked in ejaculate. + <><><><> + + <> + + <> + <> + <> + <> sighs as you finish with <> pussy, knees knocking as <> body convulses. + <><><><> + + <> + <> goes red in the face and neck, mouth agape as <> cums silently, cunt spasming against your hand. + <><><><> + + <> + <> screams, grabbing your hand and fucking <> pussy with your fingers. Your fingers are soaked in ejaculate. + <><><><> + + <> + + <> + <> + <> + <> sighs as <> cums, releasing you and pulling back <> hand, licking your wetness off <> fingers. + + <> + <> moans loudly and shakes, dripping ejaculate on the ground; <> withdraws <> hand and sighs. + + <> + <> screams as <> comes, soaking <> own pussy and thighs in ejaculate. "I love pleasuring myself with you." + <><><><> + + <> + + <> + <> + <> + <> ?orgasmMoans as <> body convulses, <> thighs trembling against your $currentSexToyLeft.name. + + <> + <> blush deepens and <> mouth gapes as <> cums against your $currentSexToyLeft.name. + + <> + <> ?orgasmMoans and grasps your arm as your $currentSexToyLeft.name brings <> to orgasm. <> juices soak your $currentSexToyLeft.name. + + <> + <><><><> + + <> + <> + <> + <> ?orgasmMoans as <> body convulses, <> thighs trembling against your $currentSexToyRight.name. + + <> + <> blush deepens and <> mouth gapes as <> cums against your $currentSexToyRight.name. + + <> + <> ?orgasmMoans and grasps your arm as your $currentSexToyRight.name brings <> to orgasm. <> juices soak your $currentSexToyRight.name. + + <> + <><><><> + + <> + <> + <> + <> ?orgasmMoans as <> body convulses, <> thighs trembling against your $currentSexToyLeft.name. + + <> + <> blush deepens and <> mouth gapes as <> cums against your $currentSexToyLeft.name. + + <> + <> ?orgasmMoans and grasps your arm as your $currentSexToyLeft.name brings <> to orgasm. + + <> + <><><> + + <> + <> + <> + <> moans as you knead <> pussy, jamming your toes inside with <> hands as <> finishes cumming. + <><><><> + + <> + <> shakes bodily as <> cums, twitching underneath your feet and cumming between your toes. + <><><><> + + <> + + <> + <> gasps as <> cums against your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> cums as you scissor your pussies together, moaning with pleasure. + + <> + <> moans as <> grinds downs hard on your <> to finish, thin legs shaking as <> cums. + + <> + <> screams, cupping <> hands under your shoulders as <> entire body shakes. Ejaculate drenches your crotch as <> squirts. + <><><><> + + <> + + <> + <> + <> + <> shudders with pleasure, exhaling warmly against your crotch as <> comes, leaving you wet with saliva and your own fluids. + <><><><> + + <> + <> quietly ejaculates, shaking as <> grinds <> nose into your crotch and giving you a long, final, wet lick. + <><><><> + + <> + <> screams into your crotch as <> cums, pinching <> hands between <> thighs and buckling over, ejaculate streaming down <> thighs. + + <> + + <> + <> gasps as <> cums against your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> shakes as <> cums, driving <> ass hard against you as <> releases a final moan. "I don't know why, but I just can't get enough of this." + + <> + <> quietly ejaculates, shaking as <> grinds <> ass deeply with your cock and moaning. "I wouldn't mind going a little longer." + + <> + <> screams and bucks into your crotch as <> cums, squirting ejaculate from <> pussy and drenching your legs. "Wish I could ride you all day." + <><><><> + + <> + + <> + <> + <> + <> grabs your shoulders with <> small hands as <> cums, bouncing down hard against your cock as <> shudders with pleasure. "What about a second round, partner?" + + <> + <> sighs as <> cums, reddening and going into a full body convulsion. Ejaculate soaks your crotch. "Hope I didn't dry you out," <> laughs. + + <> + <> cries out and bucks on your lap as <> cums, squirting ejaculate from <> pussy and drenching your crotch and legs. "Now that was a hell of a ride." + <><><> + + <> + <><><> + + <> + <> gasps as <> cums against your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> cums on your <> as you rub against the entrance and moans with pleasure. "You're gonna drive me crazy with this." + + <> + <> gasps as <> cums against your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> bucks <> ass against your cock as <> cums. <> frows, "I do hope to get a real ride next time." + + <> + <> gasps as <> cums against your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> grinds <> clit hard against your cock as <> cums, ejaculating on your cock. "You're gonna drive me crazy with this." + + <><><> + + + <> + Alex grabs your thighs as <> finishes. <> takes you deeper into <> mouth, + <> + swallowing your cum in loud gulps. + <> + <> + "Always tastes better from the source," + + <> + "Couldn't ask for a tastier milk," + + <> + + <> + as if trying to milk you. + <> + "Now ain't you a hard bull to milk," + + <> + "You still owe me some of that milk," + + <> + + <> + + <> says as <> pulls <> mouth away, cleaning your penis in the process. + + <> + <> convulses in orgasmic bliss. + + <> + + <> + <> cries out as <> orgasms. <> stops moving save for ragged breathing. + + <> +

+ + <
> + + + <> + <> + <> groans as a wet patch forms on <> trousers. You giggle at the sight. + + <> + <> + <> + <> shakes as <> cums, pulling <> cock out and ejaculating onto the backs of your thighs. + <><><><> + + <> + <> grabs your legs, pushing <> crotch hard into yours as <> cums and ejaculating onto your tummy. + <><><><> + + <> + <> groans as <> cums, ejaculating a massive load that covers your thighs and tummy. "Got some strong thighs, should be careful next time I service ya." + <><><><> + <><><> + + <> + + <> + <> gasps as <> cums onto your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> holds you still by the hips with one hand and with the other on <> cock, ejaculates onto your <>. Semen coats your outer lips and pools around your legs. + + <> + <> strokes <> cock and ejaculates onto your <> without a word, sending white streams of semen running down your cunt. + + <> + <> moans and strokes <> cock as <> cums, sending thick ropes of semen onto your <> and ass. + <><><> + + <> + <><><><> + <> + + <> + <> gasps as <> cums onto your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> cums before <> can penetrate your <>, ejaculating on the outside. Semen coats your outer lips and tummy; <> seems disappointed. + + <> + <> ejaculates onto your <> before <> can penetrate you, sending white streams of semen running down your <>; <> looks disappointed. + + <> + <> moans and strokes <> cock as <> cums, sending thick ropes of semen onto your <> and ass. + <><><> + + <> + <><><><> + <> + + <> + <> + <> + <> moans and thrusts deeply into your <> as <> cums, holding <> cock deep in your womb. You feel warm as semen fills your vagina. "You really know how to milk a farmer dry." + + <> + <> quiets and grabs your shoulders with <> small hands, pulling you onto <> cock as <> cums. Semen fills your <>, leaving you breathless. "Wanna go for another ride?" + + <> + <> convulses and grinds down on your crotch as <> cums, sending thick waves of cum into your <>. Semen oozes from your vagina and down your legs. + + <><><> + <><><> + + <> + <><><><> + <> + + <> + <> gasps as <> cums onto your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> shakes as <> cums, withdrawing <> cock from your cheeks and ejaculating onto your ass. + + <> + <> grabs your thighs, pushing <> crotch hard against your ass and ejaculating between your cheeks. + + + <><><><> + + <> + <> squeezes your cheeks together and grunts as <> cums, sending out massive load that covers your ass and drips down your crotch. <> seems pleased. "Maybe I should let you sit around more if it keeps your ass soft like that." + <><> + + <> + <><><><> + + <> + <> gasps as <> cums onto your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> palms your asscheek with one hand and strokes <> cock with the other, ejaculating onto your bottom. Semen drips down your butt and into your crack. + + <> + <> strokes <> cock and quietly ejaculates onto your ass. You feel warm semen dripping down your bottom. + + <> + <> moans and strokes <> cock as <> cums, sending thick ropes of semen onto your ass and down your crotch. + <><> + + <> + <><><><> + <> + + <> + <> gasps as <> cums onto your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> cums before <> can penetrate your ass, ejaculating on the outside. Semen coats your ass and drips down your crack; <> seems disappointed. + + <> + <> strokes <> cock and ejaculates onto your ass before <> can penetrate you, sending white streams of semen running down your crack; <> seems upset. + + <> + <> moans and strokes <> cock as <> cums, sending thick ropes of semen onto your ass and crotch. + <><> + + <> + <><><><> + <> + + <> + <> + <> + <> moans and thrusts into your ass as <> cums. You gasp as you feel semen filling you. + + <> + <> wordlessly grabs your shoulders, pulling you back onto <> cock as <> cums. Semen fills your ass as you moan with pleasure. + + <> + <> convulses and, with a final shove, cums in your ass. Thick waves of semen pump into you. You're surprised at the amount; cum oozes from your ass and runs down your legs. + <><><> + + <> + <><><><> + <> + + <> + <> + <> + <> pushes your <> together tight on <> cock as <> cums, ejaculating onto them. Your body feels slick with cum. + + <> + <> strokes the tip of <> cock between your <> and quietly ejaculates on them. Your <> feel cool as semen drips down them. "Now let's clean those off so I got a pair of nice pillows to sleep on tonight." + + <> + <> puts <> hand behind your neck and bears down on your <>, grunting as <> cums; semen shoots between your <> and covers your <>face<><><><><>stomach<><><><><>. "You look so damn cute like that!" + + <> + <><><><> + + <> + <> + <> + <> cups you under the chin with one hand and strokes <> cock with the other, ejaculating onto your face. <> face looks excited. + <><> + + <> + <> strokes <> cock and quietly ejaculates onto your face. Warm cum drips down your forehead and nose. <> smiles and cleans you off. + + <> + <> gasps and strokes <> cock while aiming straight at your mouth. Thick ropes of cum cover your face and push past your closed lips into your mouth. You're impressed by the volume and <> looks oddly pleased with <>. + <><> + <> + + <> + <><> + + <> + <> + <> + <> cups the back of your head, ejaculating onto your lips as you barely brush against the tip of <> penis; <> seems disappointed with <> lack of control. + + <> + <> brushes <> cock against your face, causing <> to ejaculate early. Cum drips down your lips and chin; <> seems disappointed. + + <> + <> cups your head but in <> excitement cums before <> can enter your mouth. Thick ropes of cum cover your cheeks, chin, and into your waiting mouth. + <> + + <> + <><><><> + + <> + <> + <> + <> cups you under the chin with one hand and holds the back of your head with the other, thrusting into your mouth as <> cums. Ejaculate streams down your throat and fills your mouth. + <><><> + "You milked me dry, and thought you were not gonna have some?" + + <> + <> grasps the base of <> cock and moans, quietly ejaculating into your mouth. You feel warm cum fill your mouth and drip down your throat. + <><><> + "Really like all my milk, I see..." + + <> + <> palms your head, wildly thrusting <> cock in your mouth as <> cums. Semen streams down your throat, fills your mouth and covers your face as <> flails. + <><><> + "<>Didn't think you could swallow it all<>F-uck...You really did get all of it<>." + <><><> + + <> + + <> + <> + <> + <> pushes your feet together and groans as <> cums. Ejaculate covers your feet. + + <> + <> quietly cums on your feet, slickening them with semen. + + <> + <> grunts and thrusts <> cock between your feet as <> cums, splattering cum on your feet and legs in the process. "Kept teasing me like that, and now I got to wait for a round two to fuck your cute bottom." + + <> + <><><><> + + <> + <> + <> + <> groans as <> cums, penis pulsing in your hands, covering them both in cum. + + <> + <> quietly cums on your hands, slickening them with semen. + + <> + <> spasms and moans, shooting thick streams of cum. You are so intent on your work you don't notice <> penis is aimed at your face, which is splattered with cum in the process. <> laughs and apologises. + <><><> + + <> + <><><><><> + + <> + <> + <> + <> groans as <> cums, penis sliding hotly in your left hand and covering it with cum. + + <> + <> quietly cums on your left hand. Semen drips down your fingers. "Aren't you gonna have some?" + + <> + <> spasms and moans, shooting thick streams of cum and splattering your <> in the process. You're impressed by the volume. " I didn't realise that keeping those big mooing buggers around would come with some bonus perks." + <><><> + + <> + <><><><> + + <> + <> + <> + <> groans as you work <> shaft with your right hand, cumming as you give <> a final squeeze. Your hand is covered in warm semen. + + <> + <> quietly cums on your right hand. Semen cools on your fingers. "Aren't you gonna have some?" + + <> + <> grunts and shoots thick ropes of semen onto your hands, making an impressive mess. You're impressed by the volume. "I didn't realise that keeping those big mooing buggers around would come with some bonus perks." + + <> + <><><><> + + <> + <> + <> + <> groans as <> works <> shaft and, giving <> cock a final squeeze, ejaculates onto your tummy. <> laughs at the mess. + <><><><> + + <> + <> rapidly rubs <> cock and moans, ejaculating on the ground. + + <> + <> strokes <> penis and, aiming at your ass, ejaculates on your bottom. "That was fun!" + <><><><> + + <> + <> strokes <> penis and moans, ejaculating on your <>. <> laughs at the mess <> made. + + <> + + <> + <> + <> + <> groans as you fuck <> ass, <> ejaculate spraying on <> own stomach. "I guess it's okay to let you be on top sometimes." + + <> + <> whimpers as you pound <> ass, moaning in pleasure as <> ejaculates on the ground. You roll off <>. + + <> + <> sighs as <> ejaculates onto <> own chest. "Next time, you'll be giving me a ride." + + <> + + <> + <> gasps as <> cums onto your $worn.genitals.name. "Wasn't I rough enough?" <> laughs. + + <> + <> + <> + <> noisily cums as you fuck <> cheeks, grabbing your ass from behind and grinding your cock against <> ass to finish. + + <> + <> moans as you rub against <> anus and ejaculates on the ground. <> smiles contentedly. + + <> + <> sighs as <> ejaculates onto <> own chest. + + <> + + <> + <> gasps as <> shoots <> cum over your $worn.genitals.name. <> strokes your face. "You're so cute." + + <> + <> + <> + <> cums as you knead your <> together, moaning with pleasure. + + <><><><> + + <> + <> cums as you rub your <> together, moaning with pleasure. <> rubs <> own cum all over your shaft. + + <><><><> + + <> + <> cums as you frot your <> together, shooting <> impressive load over your <> and stomach. + + <><><><> + + <> + <> moans as <> grinds downs hard on your <> to finish, thin legs shaking as <> cums. <> uses the cum as lube, furiously milking out the last of <> cum against your <>. + + <><><><> + + <> + <> giggles as <> rubs hard against your <> to finish. <> uses the cum as lube, furiously milking out the last of <> semen against your <>. + + <><><><> + + <> + <> screams as <> grinds hard against your <> to finish, thin legs shaking as <> cums. + + <><><><> + + <> + <> screams, cupping <> small hands under your shoulders as <> entire body shakes; you are surprised by how strong <> is. Ropes of semen drench your crotch as <> cums. + + <><><><> + <> + + <> + <> + <> + <> cums before <> can frot your <>, ejaculating on the outside. Semen coats your cock and tummy; <> seems upset with <>. + + <><><><> + + <> + <> ejaculates onto your <> before <> can frot your <> together, sending white streams of semen running down your <>; <> seems disappointed. + + <><><><> + <> + <> moans and strokes <> cock as <> cums, sending thick ropes of semen onto your <> and ass. + + <><><><> + <><><> + + <> + <> cums before your <> touch, covering you in <> massive load. Semen coats your tummy, and crotch. + + <><><><> + <> + + + <> + <> + <> + <> cums as you fuck <> mouth, gasping against your cock. "God I love you so much." + + <> + <> + <> cums on the ground as you fuck <> mouth. <> backs away, "I'm sorry; I can't take anymore. Please forgive me." + + <> + <> grabs you by the ass and drives your cock deeply into <> throat, ejaculating on <> chest as <> throats your cock. + + <> + + <> + <> shakes with arousal as <> cums, wiping <> wet face clean on your body. + + <> + + <> + <> cries out as <> orgasms. <> stops moving save for ragged breathing. + + <> + +<
> diff --git a/game/base-combat/ejaculation-eden.twee b/game/base-combat/ejaculation-eden.twee index 0f7d7a67c..271cdb9ec 100644 --- a/game/base-combat/ejaculation-eden.twee +++ b/game/base-combat/ejaculation-eden.twee @@ -394,6 +394,7 @@ <><><><> <
> + <> <> <> 가버리며 신음했고, 당신의 <> 위에 사정한다. "저 엉덩이를 유린해주지," <> 헐떡인다. "내가 이 물건을 네게서 벗겨내기만 하면 말야." <> @@ -410,6 +411,7 @@ <> <> 당신의 <>에 눌러대며 <> 가버렸다. 두꺼운 정액 줄기가 당신의 엉덩이살 위로 분출한다. <><><><> <
> + <> <> <> <> @@ -715,6 +717,7 @@ <> 신음소리를 내며 <> <> 당신의 <>살 사이에서 분출시켰고, 두꺼운 정액 줄기로 뒤뎦는다. "이번에는 살살 해 준 거야," <> 헐떡인다. "네가 내 육봉을 전부 안에 넣고 어떻게 다룰지 궁금하군." <><><><> <> + <> <> <> 가버리며 헉 소리를 내었고, 당신의 <> 위에 사정한다. "내가 열쇠만 찾으면," <> 헐떡인다. "네 항문에 세게 박아줄 줄 알아." <> @@ -732,6 +735,7 @@ <> 신음하며 <> <> 당신의 <> 위에서 씰룩거렸고, 두꺼운 정액 줄기로 뒤덮는다. "이번에는 살살 해 준 거야. 다음 번에는 내 자지 전부를 받아넣어야 해." <><><><> <
> + <> <> <> <> diff --git a/game/base-combat/ejaculation-gloryhole.twee b/game/base-combat/ejaculation-gloryhole.twee index fbffab97d..44e7402a1 100644 --- a/game/base-combat/ejaculation-gloryhole.twee +++ b/game/base-combat/ejaculation-gloryhole.twee @@ -321,6 +321,8 @@ <><><><> <><><> <> + <> + <> groans as <> ejaculates over your $worn.genitals.name. <> <> <> @@ -340,6 +342,8 @@ <><><> <> <> + <> + <> moans as <> ejaculates onto your $worn.genitals.name. <> <> <> @@ -394,6 +398,8 @@

<><><><> <
> + <> + <> gasps as <> strokes <> cock, ejaculating onto your $worn.genitals.name. <> <> <> @@ -409,6 +415,9 @@

<><><><> <
> + <> + <> + <> strokes <> cock and ejaculates onto your $worn.genitals.name. <> <> <> @@ -424,6 +433,7 @@

<><><><> <
> + <> <><> <> <> diff --git a/game/base-combat/ejaculation-kylar.twee b/game/base-combat/ejaculation-kylar.twee index 9bdf1c791..7b16a791e 100644 --- a/game/base-combat/ejaculation-kylar.twee +++ b/game/base-combat/ejaculation-kylar.twee @@ -432,6 +432,7 @@ <><><><> <> + <> <> <> 당신의 <> 위에 사정하며 헉 하는 소리를 내었다. "나-나는 더 참을 수 없어," <> 말한다. "나는 너를 채워줘야 해. 그-그 정조대에는 분명 마스터 키가 있을 거야." @@ -450,6 +451,7 @@ <><><><> <> + <> <> <> <> @@ -855,6 +857,7 @@ <><><><> <> + <> <> <> 당신의 <> 위에 사정하며 헉 하는 소리를 내었다. "나-나는 더 참을 수 없어," <> 말한다. "나는 너를 채워줘야 해. 그-그 정조대에는 분명 마스터 키가 있을 거야." @@ -873,6 +876,7 @@ <><><><> <> + <> <> <> <> diff --git a/game/base-combat/ejaculation-leighton.twee b/game/base-combat/ejaculation-leighton.twee index 6e890f284..f2f1a2c8e 100644 --- a/game/base-combat/ejaculation-leighton.twee +++ b/game/base-combat/ejaculation-leighton.twee @@ -395,6 +395,7 @@ <><><><> <> + <> <> <> 가버리며 몸을 긴장시켰고, 당신의 <> 위에 사정한다. "네게 그것을 채운 사람이 누구든 간에 좋은 생각을 했어," <> 헐떡인다. "내가 그 열쇠를 갖고 있다면 말이지만." @@ -413,6 +414,7 @@ <><><><> <> + <> <> <> <> @@ -754,6 +756,7 @@ <><><><> <> + <> <> <> 가버리며 몸을 긴장시켰고, 당신의 <> 위에 사정한다. "네게 그것을 채운 사람이 누구든 간에 좋은 생각을 했어," <> 헐떡인다. "내가 그 열쇠를 갖고 있다면 말이지만." @@ -773,6 +776,7 @@ <><><><> <> + <> <> <> <> diff --git a/game/base-combat/ejaculation-pillory.twee b/game/base-combat/ejaculation-pillory.twee index b47d58ec6..34100a2d5 100644 --- a/game/base-combat/ejaculation-pillory.twee +++ b/game/base-combat/ejaculation-pillory.twee @@ -285,6 +285,7 @@

<><><><> <
> + <> <> <> 가버리며 헉 하는 소리를 내었고, 당신의 <> 위에 사정한다. "아래쪽까지 잠궈 놓을 필요까지는 없었는데," <> 말한다. "그렇지 않았다면 훨씬 더 재미를 볼 수 있었을 테니까 말이야."

@@ -304,6 +305,7 @@

<><><><> <> + <> <> <> <> @@ -552,4 +554,4 @@ <
> <
> -<> \ No newline at end of file +<> diff --git a/game/base-combat/ejaculation-robin.twee b/game/base-combat/ejaculation-robin.twee index ffaca925d..9689bf20c 100644 --- a/game/base-combat/ejaculation-robin.twee +++ b/game/base-combat/ejaculation-robin.twee @@ -307,6 +307,7 @@ <><> <> <><><><> + <> <> <> 당신의 <>에 대고 가버리면서 헉 하는 소리를 낸다. "그거 불편하지 않아?" <> 묻는다. "혹시 좀 더 부드러운 것을 만들지 않을까?" @@ -322,6 +323,7 @@ <><> <> <><><><> + <> <> <> @@ -506,4 +508,4 @@ <
>

-<> \ No newline at end of file +<> diff --git a/game/base-combat/ejaculation-sydney.twee b/game/base-combat/ejaculation-sydney.twee index cd2c8ee96..79b2e9105 100644 --- a/game/base-combat/ejaculation-sydney.twee +++ b/game/base-combat/ejaculation-sydney.twee @@ -286,7 +286,7 @@ "이건 불공평해..." <> 당신의 <> 노려보며, 입술을 불룩 내민다. <
>

- <> + <> <> 절정에 이르면서 <> 정조대 벨트를 필사적으로 잡는다. <> 불만스러워하는 소리를 몇 번 질렀지만, 곧 <> 마음을 가라앉힌다. <> <> <> 엉덩이를 당신의 자지에 밀어대며 가버리며, 본의 아니게 몸을 떤다. @@ -586,6 +586,7 @@ <> <> 당신이 <> 절정할 때까지 섹스하자 <> 등을 구부리며 크게 신음한다. <> 당신의 배를 쓰다듬는다. "너도 기분 좋았던 거야?" <><>

+ <><><><> <> <> <> @@ -683,6 +684,7 @@ <><><><> <> <
> + <> <> <> 당신의 <> 위에 사정하며 헉 하는 소리를 낸다. <> @@ -715,6 +717,7 @@ <><><><> <> <> + <> <> <> <> 크게 신음하며 정액 전부를 당신의 장 안에 풀어놓는다. "뭐어... 최소한 너는 아직 순결하다고 여겨질 수 있을 거야." <><> @@ -1160,6 +1163,7 @@

<><><><> <> + <> <> <> 당신의 <> 위에 사정하며 신음 소리를 내었다. <> 화난 듯 하다. <> @@ -1190,6 +1194,7 @@

<><><><> <> + <> <> <> <> diff --git a/game/base-combat/ejaculation-wall.twee b/game/base-combat/ejaculation-wall.twee index a500c25b4..8c68ae8cd 100644 --- a/game/base-combat/ejaculation-wall.twee +++ b/game/base-combat/ejaculation-wall.twee @@ -307,6 +307,7 @@

<><><><> <
> + <> <> <> 헉 하는 소리를 내며 당신의 <> 위에 사정한다. "누군가가 너를 이렇게 단단히 잠가 둬서 아쉽군," <> 말하며, 당신의 <> 살짝 때린다.

@@ -326,6 +327,7 @@

<><><><> <> + <> <> <> <> @@ -503,6 +505,9 @@ <> 한숨을 쉬며 <> 자신의 가슴에 사정한다.

<
> + <> + <> shakes with arousal as <> cums all over your <>. + <> <> <> diff --git a/game/base-combat/ejaculation.twee b/game/base-combat/ejaculation.twee index b2e939000..16f0de802 100644 --- a/game/base-combat/ejaculation.twee +++ b/game/base-combat/ejaculation.twee @@ -106,6 +106,8 @@ <> <> <> + <> + <> <> <> <> @@ -201,6 +203,7 @@ <> 당신의 <> 위에 사정한다. <> <> 당신의 <> 사이에 사정한다. + <> <
> <><><><> <
> @@ -210,6 +213,7 @@ <> 당신의 <> 위에 사정한다. <> <> 당신의 <> 사이에 사정한다. + <> <
> <><><><> <
> @@ -316,7 +320,7 @@ <> <> <> - <> + <> <> 싱긋 웃으며 당신의 머리카락을 헝클어놓는다. <>."` )>> - <> + <> <> 당신을 가까이 끌어당기고, 당신의 가슴에 대고 한숨을 쉰다. <>여자<>남자<>야..."` @@ -335,7 +339,7 @@ <> <> <> 이마를 당신의 이마에 맞댄다. <>여자<>남자<>야..."` @@ -346,13 +350,13 @@ <>니까."` + `"You're a bad <>, getting me to slack off."` )>> <> 당신의 얼굴은 기쁨으로 상기된다. <><><><> <> - <> + <> <> 당신의 턱 아래를 쓰다듬어, <> <> <> 머리를 당신의 어깨에 기대어, @@ -360,7 +364,7 @@ 당신이 몸속에서부터 따뜻함을 느끼게 한다. <><><><> <> - <> + <> <> 당신을 <> 팔로 감싸, <> <> 당신의 팔 안에 누워, @@ -371,10 +375,10 @@

<
> <> - <> + <> <> <> - <> + <> <> <> 한 팔을 당신의 가슴에 감고 @@ -389,7 +393,7 @@ )>> <> 당신의 젖꼭지를 난폭하게 꼬집고는, 당신을 옆으로 밀쳐낸다. <><><><><><> - <> + <> <> <> 한 팔을 당신의 가슴에 감고 @@ -420,7 +424,7 @@ <><><><><><> <> <> - <> + <> <> 당신의 머리카락을 뒤로 쓸어넘기지만, <> 표정에서는 아무런 감정을 찾을 수가 없다. <>잘생긴<>예쁜<> 얼굴이야. 내가 그것을 망치게 하지 말아 줘."`, @@ -428,7 +432,7 @@ `"최소한 너는 무언가에는 쓸만하구나."` )>> <><><><><><> - <> + <> <> 당신의 머리카락을 뒤로 쓸어넘기면서, 당신의 볼을 꼬집으며 미소를 짓는다. <><><><><> <> <> - <> + <> <> 당신의 턱을 들어올리며, 한손을 <> 엉덩이에 댄 채로 당신을 내려다본다. <> <><><><><><> - <> + <> <> 당신의 턱을 들어올려, 당신의 시선을 강제로 <> 시선에 맞춘다. <> <>

- <> + <> 에이버리는 경멸하며, <> 당신의 얼굴에 주먹을 날린다. <> <> 손을 소매에 닦는다. @@ -491,21 +495,21 @@ <> <> <> - <> + <> <> 당신을 계속해서 발로 차며, <> 발이 당신의 옆구리에 부딪힐 때 마다 으르렁거린다. <> <> 공격에 무력한 채로, 당신은 <> 화가 마침내 잦아들 때 까지 몸을 구부려 자신을 보호할 수 밖에 없었다. <><><><> - <> + <> <> 당신을 밀쳐내며 경멸한다. <> <><><><> <> @@ -513,7 +517,7 @@ <><><><><><> <> <> - <> + <> <> 당신의 뺨을 좌우로 때리며, 때릴 때 마다 점점 강도를 올려간다. <> 끝냈을 때, <> 헐떡인다. <>."` )>> <><><><> - <> + <> <> 당신의 뺨을 좌우로 때린다. <> <> 손을 <> 소매에 닦으며, 으르렁거린다. <> <><><><> @@ -533,14 +537,14 @@ <> 주먹이 꽉 쥐어졌지만, <> 움직이지 않는다. <> 깊게 숨을 들이마시고는, 미소를 짓는다. <> <><><><><><> <> <> <> - <> + <> <> 양손으로 당신의 목을 잡아, 손톱이 당신의 피부를 찌른다. <> 같으니."`, @@ -551,7 +555,7 @@ )>> <> 마침내 당신을 보내줄 때 까지, <> 손톱을 당신의 목 안에 찔러넣는다. <><><><> - <> + <> <> 당신 목을 잡고 있는 <> 손아귀에 힘을 주며, 당신에게 시선을 고정한다. <> 되었구나."`, @@ -559,7 +563,7 @@ `"내 너그러움도 여기까지만이야."`, `"다음 번에는 순종하기를 기대하지."`, `"예의를 지키라고, 애새끼 같으니."`, - `"점점 나를 짜증나게 하고 있어."`, + `"You're starting to grate."`, `"내 인내심을 시험하지 마."` )>> <> <> 손톱을 당신의 피부 위로 천천히 긁더니 당신을 보내준다. @@ -568,14 +572,14 @@ <> <> 손가락으로 당신의 목을 쓸어내린다. <> 당신은 몸을 떤다. <><><><><><> <> <> - <> + <> <> 당신이 턱을 잡고 위로 확 비틀어 올리며, 분노에 찬 얼굴로 내려다본다. <> 같으니."`, @@ -586,7 +590,7 @@ )>> <> 당신의 뺨을 손톱으로 잡더니, 빠르게 손등으로 때린다. 당신은 아픔에 비명을 지른다. <><><><> - <> + <> <> 당신의 턱을 잡고 강제로 올리면서, 경멸에 찬 얼굴로 내려다본다. <> 되었구나."`, @@ -594,7 +598,7 @@ `"내 너그러움도 여기까지만이야."`, `"다음 번에는 순종하기를 기대하지."`, `"예의를 지키라고, 애새끼 같으니."`, - `"점점 나를 짜증나게 하고 있어."`, + `"You're starting to grate."`, `"내 인내심을 시험하지 마."` )>> <> 당신을 보내주기 전에 <> 손톱을 당신의 뺨에 쿡 찌른다. @@ -603,7 +607,7 @@ <> 한 손을 당신의 턱 아래 받치고 위로 젖혀올린다. <> <> 미소를 지으며 당신을 보내주기 전에 <> 엄지손가락으로 당신의 뺨을 쓸어내린다. @@ -631,28 +635,28 @@ <> <> 한 손으로 당신의 머리카락을 쓸어내린다.`, - `<> 당신의 얼굴에서 살짝 몸을 띄우고는 당신을 내려다보며 미소 짓는다.` + `<> lifts off your face slightly and smiles down at you.` )>> <> <> 한 손으로 당신의 머리카락을 쓸어내린다.`, - `<> 당신의 귀를 깨물고는, 속삭인다.` + `<> bites your ear, and whispers.` )>> <> <> - <> + <> <>."` )>> <> <> <> <> - <> + <> <> 골랐다는 것을 알았다니까."`, `"그러니까 네가 좋은 <>."`, @@ -682,7 +686,7 @@ <> <> <>."` )>> <> @@ -692,7 +696,7 @@ `"웃어, <>."` )>> <> - <> + <> <> 카메라 플래시가 터지자 <>당신을 토닥거린다<>당신의 볼에 키스한다<>. <><><><><><> <> @@ -729,7 +733,7 @@ <> <> <>."` )>> <> @@ -1126,10 +1130,9 @@ 콘돔의 끝이 터지며 찢어져, <> 자지 귀두를 노출시킨다. <> <> + <> <> 바지에 젖은 자국을 만들며 신음한다. <> - <> <> 바지에 젖은 자국을 만들며 신음한다. 당신은 <> 한심한 모습에 터지려는 웃음을 겨우 참는다. - <> - <> <> 바지에 젖은 자국을 만들며 신음한다. + You stifle a giggle at how pathetic <> looks. <> <> <> @@ -1299,6 +1302,7 @@ <><> <> <><><><> + <> <> <> 당신의 엉덩이에 삽입하기 전에 가버렸고, 당신의 엉덩이살 위에 사정했다. 정액이 당신의 <> 뒤덮고 그 아래 웅덩이를 만든다. <> @@ -1316,6 +1320,7 @@ <><> <
> <><><><> + <> <> <> <> @@ -1408,6 +1413,11 @@ <>."`>> <><><><> <> + <> + <> + <> + <> + <> <> <> @@ -1421,42 +1431,62 @@ <> <><><><> <> - <> - <> - <> <> <> 꽉 잡고, 당신의 얼굴을 겨냥하고 앞뒤로 비벼댄다. 두꺼운 정액 줄기가 당신의 얼굴과 입을 뒤덮는다. - <> - <><><><> - <><> - <> - <> 신음하며 <> <>_trResult에서 한심한 양의 정액 방울을 당신의 빰에 묻혀놓는다. 당신은 나오려는 웃음을 어떻게든 참아낸다. - <> - <> - <> - <> 당신의 턱을 한 손으로 받치고 다른 손으로 <> <> 비벼대어, 당신의 얼굴 위에 사정한다. <> 당신의 추잡한 모습이 만족스러운 듯 하다. - <> - <> <> <> 비벼대며 당신의 얼굴 위에 사정한다. 따뜻한 정액이 당신의 이마와 코를 타고 뚝뚝 떨어진다. - <> - <> - <> 당신의 머리카락을 잡고, 당신의 입을 정확히 겨냥하여 <> <> 비벼댄다. 두꺼운 정액 줄기가 당신의 얼굴과 입을 뒤덮는다. 그중 약간은 당신의 입 속으로 들어온다. - <> - <> + <> + + <> grabs <> $NPCList[_nn].penisdesc with a moan as <> sprays warm ejaculate across your face. + Unable to help yourself at the smell of fresh cum, you scoop what you can off your face and into your mouth with your hands. + You involuntarily moan a little at its <<= either("strong, bitter", "sweet, creamy", "sharp, salty")>> taste. <><> + <> + <><><><> + <> + <> + <> + <> + <> <> <> 꽉 잡고, 당신의 얼굴을 겨냥하고 앞뒤로 비벼댄다. 두꺼운 정액 줄기가 당신의 얼굴과 입을 뒤덮는다. + <> + <><><><> + <><> + <> + <> 신음하며 <> <>_trResult에서 한심한 양의 정액 방울을 당신의 빰에 묻혀놓는다. 당신은 나오려는 웃음을 어떻게든 참아낸다. + <> + <> + <> + <> 당신의 턱을 한 손으로 받치고 다른 손으로 <> <> 비벼대어, 당신의 얼굴 위에 사정한다. <> 당신의 추잡한 모습이 만족스러운 듯 하다. + <> + <> <> <> 비벼대며 당신의 얼굴 위에 사정한다. 따뜻한 정액이 당신의 이마와 코를 타고 뚝뚝 떨어진다. + <> + <> + <> 당신의 머리카락을 잡고, 당신의 입을 정확히 겨냥하여 <> <> 비벼댄다. 두꺼운 정액 줄기가 당신의 얼굴과 입을 뒤덮는다. 그중 약간은 당신의 입 속으로 들어온다. + <> + <> + <> + <><><><> <> - <><><><> - <> + <
> <> - <> - <> - <> 당신의 머리카락을 받치고, <> <>_trResult 끝이 당신의 입술에 살짝 스치게 하자 사정한다. <> 실망스러운 듯 하다. - <> - <> - <> <> <> 당신의 얼굴을 쓸어대었고, 사정했다. 정액이 당신의 입술과 뺨을 타고 뚝뚝 떨어진다. - <> - <> - <> 당신의 머리카락을 잡고 <> <> 당신의 입에 강제로 넣으려 했지만, <> 경련이 그 노력을 배신했다. 두꺼운 정액 줄기가 당신의 얼굴을 뒤덮는다. 그중 약간은 당신의 입 속으로 들어온다. - <> + <> + + <> grabs <> $NPCList[_nn].penisdesc with a moan as <> sprays warm ejaculate across your face. + Unable to help yourself at the smell of fresh cum, you scoop what you can off your face and into your mouth with your hands. + You involuntarily moan a little at its <<= either("strong, bitter", "sweet, creamy", "sharp, salty")>> taste. <><> + <> + <><><><> <> - <> - <><><><> + <> + <> + <> + <> 당신의 머리카락을 받치고, <> <>_trResult 끝이 당신의 입술에 살짝 스치게 하자 사정한다. <> 실망스러운 듯 하다. + <> + <> + <> <> <> 당신의 얼굴을 쓸어대었고, 사정했다. 정액이 당신의 입술과 뺨을 타고 뚝뚝 떨어진다. + <> + <> + <> 당신의 머리카락을 잡고 <> <> 당신의 입에 강제로 넣으려 했지만, <> 경련이 그 노력을 배신했다. 두꺼운 정액 줄기가 당신의 얼굴을 뒤덮는다. 그중 약간은 당신의 입 속으로 들어온다. + <> + <> + <> + <><><><> + <
> <> <> <> @@ -1862,6 +1892,7 @@ <><> <> <><><><> + <> <> <> <> <> 비벼대며 당신의 <> 위에 사정했고, 하얀 정액 줄기를 내뿜어 당신의 허벅지를 타고 흘러내리게 한다. <> @@ -1881,6 +1912,7 @@ <><> <> <><><><> + <> <> <> <> @@ -1892,6 +1924,7 @@ <> <> 신음하며 <> <>_trResult에서 한심한 양의 정액 방울을 당신의 <> 위에 떨어뜨린다. + <> <> <> @@ -1980,6 +2013,11 @@ <><><><> <><><><><><> <> + <> + <> + <> + <> + <> <> <> @@ -1995,44 +2033,63 @@ <> <><><><> <> - <> - <> - <> <> <> 꽉 잡고, 당신의 얼굴을 겨냥하고 앞뒤로 비벼댄다. 두꺼운 정액 줄기가 당신의 악문 입술을 밀고 지나쳐 들어와 당신은 캑캑거리며 토한다. - <>."`>> - <><><><> + <> + + <> grabs your hair with one hand and strokes <> $NPCList[_nn].penisdesc with the other as <> sprays warm ejaculate across your face. + Unable to help yourself at the smell of fresh cum, you start to scoop what you can off your face and into your mouth with your hands. + You force yourself to stop once you realise what you're doing, disgusted with your own behaviour. <><><> + <> + <><><><> <> - <> - <> 신음하며 <> <>_trResult에서 한심한 양의 정액 방울을 당신의 빰에 묻혀놓는다. 당신은 나오려는 웃음을 어떻게든 참아낸다. - - <> - <> - <> - <> 당신의 턱을 한 손으로 잡고 다른 손으로 <> <> 비벼대어, 당신의 얼굴 위에 사정한다. <> 당신이 <>에게서 움츠려 떨어지는 모습을 보고 웃는다. - <> - <> 한 손으로 당신의 귀를 잡고 <> <> 다른 손으로 비벼댄다. <> 당신의 얼굴 위에 사정한다. - <> - <> - <> 당신의 머리카락을 <> 주먹으로 꽉 잡고 <> <> 비벼댄다. <> 당신의 입을 겨냥하고 사정한다. 두꺼운 정액 줄기가 당신의 악문 입술을 밀고 지나쳐 입 안으로 들어와 당신은 캑캑거리며 토한다. - <> 정액을 토해내는 걸 보는 게 좋아."`>> - <> + <> + <> + <> + <> <> <> 꽉 잡고, 당신의 얼굴을 겨냥하고 앞뒤로 비벼댄다. 두꺼운 정액 줄기가 당신의 악문 입술을 밀고 지나쳐 들어와 당신은 캑캑거리며 토한다. + <>."`>> + <><><><> + <> + <> + <> 신음하며 <> <>_trResult에서 한심한 양의 정액 방울을 당신의 빰에 묻혀놓는다. 당신은 나오려는 웃음을 어떻게든 참아낸다. + <> + <> + <> + <> 당신의 턱을 한 손으로 잡고 다른 손으로 <> <> 비벼대어, 당신의 얼굴 위에 사정한다. <> 당신이 <>에게서 움츠려 떨어지는 모습을 보고 웃는다. + <> + <> 한 손으로 당신의 귀를 잡고 <> <> 다른 손으로 비벼댄다. <> 당신의 얼굴 위에 사정한다. + <> + <> + <> 당신의 머리카락을 <> 주먹으로 꽉 잡고 <> <> 비벼댄다. <> 당신의 입을 겨냥하고 사정한다. 두꺼운 정액 줄기가 당신의 악문 입술을 밀고 지나쳐 입 안으로 들어와 당신은 캑캑거리며 토한다. + <> 정액을 토해내는 걸 보는 게 좋아."`>> + <> + <> + <><><><> <> - <><><><> - <> + <> <> - <> - <> - <> 당신의 머리카락을 목 뒤에서 잡고, <> <> 당신 입 속에 밀어넣으려 한다. <> 너무 일찍 사정했고, 정액을 당신의 입술 위로 쏘아댄다. <> 당신의 얼굴을 찰싹 때린다. - <> - <><><><> - <> - <> <> <> 당신의 얼굴을 쓸어대었고, 일찍 사정했다. 정액이 당신의 얼굴을 타고 뚝뚝 떨어진다. - <><><> - <> - <> 당신의 머리카락을 잡고 <> <> 당신의 입에 강제로 넣으려 한다. <> 너무 흥분했고, 당신에게 집어넣기 전에 사정한다. 두꺼운 정액 줄기가 당신의 볼과 턱을 뒤덮는다. 그중 약간은 당신의 입 속으로 들어온다. - <>."`>> - <><><><> + <> + + <> grabs your hair with one hand and strokes <> $NPCList[_nn].penisdesc with the other as <> sprays warm ejaculate across your face. + Unable to help yourself at the smell of fresh cum, you start to scoop what you can off your face and into your mouth with your hands. + You force yourself to stop once you realise what you're doing, disgusted with your own behaviour. <><><> + <> + <><><><> <> - <> + <> + <> + <> + <> 당신의 머리카락을 목 뒤에서 잡고, <> <> 당신 입 속에 밀어넣으려 한다. <> 너무 일찍 사정했고, 정액을 당신의 입술 위로 쏘아댄다. <> 당신의 얼굴을 찰싹 때린다. + <> + <><><><> + <> + <> <> <> 당신의 얼굴을 쓸어대었고, 일찍 사정했다. 정액이 당신의 얼굴을 타고 뚝뚝 떨어진다. + <><><> + <> + <> 당신의 머리카락을 잡고 <> <> 당신의 입에 강제로 넣으려 한다. <> 너무 흥분했고, 당신에게 집어넣기 전에 사정한다. 두꺼운 정액 줄기가 당신의 볼과 턱을 뒤덮는다. 그중 약간은 당신의 입 속으로 들어온다. + <>."`>> + <><><><> + <> + <> + <
> <> <> <> @@ -2332,13 +2389,10 @@ <
> <
> <
> -


<> <> - <> -
<
> <
> <> @@ -2524,6 +2578,7 @@ 정액이 <> <>_trResult에서 솟구쳐 나와, 당신의 <> 흠뻑 적시고 허벅지를 타고 흘러내린다. <> 정액이 <> <>_trResult에서 솟구쳐 나와, 당신의 <> 흠뻑 적시고 허벅지를 타고 흘러내린다. + <> <
> <><><><><><> <> @@ -2531,6 +2586,7 @@ <> 당신의 <> 위에 사정한다. <> <> 당신의 <> 위에 사정한다. + <> <
> <><><><> <
> @@ -2541,6 +2597,7 @@ 정액이 <> <>_trResult에서 솟구쳐 나와, 당신의 <> 흠뻑 적시고 허벅지를 타고 흘러내린다. <> 정액이 <> <>_trResult에서 솟구쳐 나와, 당신의 <> 흠뻑 적시고 허벅지를 타고 흘러내린다. + <> <
> <><><><><><> <> @@ -2548,6 +2605,7 @@ <> 당신의 <> 위에 사정한다. <> <> 당신의 <> 위에 사정한다. + <> <
> <><><><> <
> @@ -2809,11 +2867,9 @@ <
> <> 그 모습을 구경꾼들에게 보여지고 있다.<> 그 모습을 누군가에게 보여지고 있다.<> 있다.<> <
> -


<> <> -
<
> <> diff --git a/game/base-combat/end.twee b/game/base-combat/end.twee index 7672fb8bd..3138bf8b2 100644 --- a/game/base-combat/end.twee +++ b/game/base-combat/end.twee @@ -32,7 +32,7 @@ <> <
> <><> - 당신의 선택된 파트너와 성교하자 본능적인 욕구가 충족된다.<><> + Copulating with your chosen partner fulfils an instinctual desire.<><> <>
<
> @@ -365,11 +365,8 @@ <> <> - <> - <> - <> - <> <> + <> <> @@ -599,7 +596,7 @@ <> <> <> - + <> <> <> @@ -616,6 +613,7 @@ <> <> <> + <> <> <> diff --git a/game/base-combat/images.twee b/game/base-combat/images.twee index bf39178f2..7ab151d74 100644 --- a/game/base-combat/images.twee +++ b/game/base-combat/images.twee @@ -23,6 +23,7 @@ <> <
> <
> +
<>
<> @@ -45,6 +46,7 @@
<
> <> +
<> <> diff --git a/game/base-combat/init.twee b/game/base-combat/init.twee index 27531ca51..f1a0fd0db 100644 --- a/game/base-combat/init.twee +++ b/game/base-combat/init.twee @@ -1137,30 +1137,46 @@ <> <> - /* if statements so we can fine tune the percentages for balance */ - <> - <> - 당신의 $_sexToy은 뜻밖인게 확실했지만, 그들은 신경쓰지 않는 듯 하다. - <> - 당신의 $_sexToy을 보고 그들의 숨소리가 빨라진다. - <><> - <> - 그들은 당신의 $_sexToy을 보고 기뻐하는 듯 하다. - <> - <> - 그들은 당신의 $_sexToy을 보고 실망한 듯 하다. 그들은 그게 뜻밖인 것 같다. - <><><> - <> - 그들의 눈은 당신의 $_sexToy을 본 충격으로 커진다. - <><><> - <> - 그들은 당신의 $_sexToy을 보고 혐오감에 움찔한다. - <><><> - <> - 그들은 당신의 $_sexToy을 보고 어리둥절한 듯 하지만, 또한 흥분한 것 같다. - <> - <> - + <> + <> + <> + <> + <> + 당신의 $_sexToy을 보고 그들의 숨소리가 빨라진다. + <> + <> + <> + 그들은 당신의 $_sexToy을 보고 기뻐하는 듯 하다. + <> + <> + 그들은 당신의 $_sexToy을 보고 어리둥절한 듯 하지만, 또한 흥분한 것 같다. + <> + <> + <> + 당신의 $_sexToy은 뜻밖인게 확실했지만, 그들은 신경쓰지 않는 듯 하다. + <> + <> + <> + 그들은 당신의 $_sexToy을 보고 실망한 듯 하다. 그들은 그게 뜻밖인 것 같다. + <><> + <> + <> + <> + 그들의 눈은 당신의 $_sexToy을 본 충격으로 커진다. + <><> + <> + <> + <> + 그들은 당신의 $_sexToy을 보고 혐오감에 움찔한다. + <><> + <> + <> + <> + They appear confused. Not sure what to think of your $_sexToy. + You should inform Vrelnir. + <> + <> + <> <> <> <> @@ -1181,28 +1197,46 @@ <> <> <> - <> - <> - 당신의 $_sexToy을 보고 <> 숨소리가 빨라진다. - <><> - <> - <> 당신의 $_sexToy을 보고 기뻐하는 듯 하다. - <> - <> - 당신의 $_sexToy은 뜻밖인게 확실했지만, <> 신경쓰지 않는 듯 하다. - <> - <> 당신의 $_sexToy을 보고 실망한 듯 하다. <> 그게 뜻밖인 것 같다. - <><><> - <> - <> 눈은 당신의 $_sexToy을 본 충격으로 커진다. - <><><> - <> - <> 당신의 $_sexToy을 보고 혐오감에 움찔한다. - <><><> - <> - <> 당신의 $_sexToy을 보고 어리둥절한 듯 하지만, 또한 흥분한 것 같다. - <> - <> + <> + <> + <> + <> + <> + 당신의 $_sexToy을 보고 <> 숨소리가 빨라진다. + <> + <> + <> + <> 당신의 $_sexToy을 보고 기뻐하는 듯 하다. + <> + <> + <> 당신의 $_sexToy을 보고 어리둥절한 듯 하지만, 또한 흥분한 것 같다. + <> + <> + <> + 당신의 $_sexToy은 뜻밖인게 확실했지만, <> 신경쓰지 않는 듯 하다. + <> + <> + <> + <> 당신의 $_sexToy을 보고 실망한 듯 하다. <> 그게 뜻밖인 것 같다. + <><> + <> + <> + <> + <> 눈은 당신의 $_sexToy을 본 충격으로 커진다. + <><> + <> + <> + <> + <> 당신의 $_sexToy을 보고 혐오감에 움찔한다. + <><> + <> + <> + <> + <> appears confused. Not sure what to think of your $_sexToy. + You should inform Vrelnir. + <> + <> + <> <> <> <
> diff --git a/game/base-combat/machine/actions.twee b/game/base-combat/machine/actions.twee index 55ff528c7..b30b874b8 100644 --- a/game/base-combat/machine/actions.twee +++ b/game/base-combat/machine/actions.twee @@ -428,247 +428,242 @@ <> - <> -
- 당신의 왼팔은 자유롭다. -
- <> - <> - <> - | - <> - | + <> +
+ 당신의 왼팔은 자유롭다. +
+ <> + <> + <> + | + <> + | + <> <> <> - <
> - <> - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> <> <> - <> - <> - <> - <> - | - <> - | - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> + <> + | + <> + | + <> <> <> - <> - <> - <> - <> - | - <> - | - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> + <> + | + <> + | + <> <> <> - <> - <> - | - <> - | - <> - <> -
- 당신의 왼팔은 눌려있다. -
- <> - <> - <> - | - <> - | + <> + | + <> + | + <> + <> +
+ 당신의 왼팔은 눌려있다. +
+ <> + <> + <> + | + <> + | + <> <> <> + <> + | + <> + | + <> + <> +
+ 당신의 왼팔은 묶여있다. <
> - <> - | - <> - | - <> - <> -
- 당신의 왼팔은 묶여있다. -
- <
> - <> -
- 당신의 오른팔은 자유롭다. -
- <> - <> - <> - | - <> - | + <> +
+ 당신의 오른팔은 자유롭다. +
+ <> + <> + <> + | + <> + | + <> <> <> - <
> - <> - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> <> <> - <> - <> - <> - <> - | - <> - | - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> + <> + | + <> + | + <> <> <> - <> - <> - <> - <> - | - <> - | - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> + <> + | + <> + | + <> <> <> - <> - <> - | - <> - | - <> - <> -
- 당신의 오른팔은 체인에 묶여있다. -
- <> - <> - <> - | - <> - | + <> + | + <> + | + <> + <> +
+ 당신의 오른팔은 체인에 묶여있다. +
+ <> + <> + <> + | + <> + | + <> <> <> + <> + | + <> + | + <> + <> +
+ 당신의 오른팔은 묶여있다. <
> - <> - | - <> - | - <> - <> -
- 당신의 오른팔은 묶여있다. -
- <
> - <> -
- 당신의 다리는 자유롭다. -
- <> - <> - <> - | - <> - | + <> +
+ 당신의 다리는 자유롭다. +
+ <> + <> + <> + | + <> + | + <> <> <> - <
> - <> - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> <> <> - <> - <> - <> - <> - | - <> - | - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> + <> + | + <> + | + <> <> <> - <> - <> - <> - <> - | - <> - | - <> - <> - | - <> - | + <> + <> + <> + | + <> + | + <> + <> + | + <> + | + <> <> <> - <> - <> - | - <> - | - <> - <> -
- 당신의 다리는 체인에 묶여있다. -
- <> - <> - <> - | - <> - | + <> + | + <> + | + <> + <> +
+ 당신의 다리는 체인에 묶여있다. +
+ <> + <> + <> + | + <> + | + <> <> <> - <
> - <> - | - <> - | + <> + | + <> + | + <> <> <
> -
- <
> <> -

<> <> -
<
> <
> diff --git a/game/base-combat/man-combat.twee b/game/base-combat/man-combat.twee index 1794c2dc3..ce1964019 100644 --- a/game/base-combat/man-combat.twee +++ b/game/base-combat/man-combat.twee @@ -1607,6 +1607,9 @@ Hands are 'inverted': NPC holds PC's left hand with their right one, and vice ve <><><> <
> <> + <> + <> gently holds onto your $worn.genitals.name before letting it go. + <><><> <> <> 당신의 <> 느슨하게 하려고 시도한다. <> 거친 손놀림으로 인해 당신은 더욱 불만족스럽게 느끼며 흥분한다. <> 몇 분 후 쓸모없이 잡아당기는 것을 포기하였고, 실망한 듯 보인다. @@ -2340,6 +2343,9 @@ Hands are 'inverted': NPC holds PC's left hand with their right one, and vice ve <> 당신의 얼굴 위에 침을 뱉는다. <> <> + <> + <> + <> <> <> 당신의 다리를 묶고 있는 구속구를 확인해본다. <> @@ -2347,7 +2353,8 @@ Hands are 'inverted': NPC holds PC's left hand with their right one, and vice ve <> 당신의 <> 잡는다. <> <> - <> + <> <> 위협적인 금속 족쇄를 꺼낸다. <> <> diff --git a/game/base-combat/npc-generation.twee b/game/base-combat/npc-generation.twee index e5b1e97ca..515491bf1 100644 --- a/game/base-combat/npc-generation.twee +++ b/game/base-combat/npc-generation.twee @@ -177,30 +177,6 @@ <> <> -<> - <> -<> - -<> - <> -<> - -<> - <> -<> - -<> - <> -<> - -<> - <> -<> - -<> - <> -<> - <> <> <> diff --git a/game/base-combat/speech-sydney.twee b/game/base-combat/speech-sydney.twee index 4aaa3975a..8a0f7941b 100644 --- a/game/base-combat/speech-sydney.twee +++ b/game/base-combat/speech-sydney.twee @@ -17,29 +17,29 @@ <> <> <> - <> 순수한 황홀감으로 당신을 보며 신음한다. "네가 나를 타락시키는 느낌이 매번 더 좋아지고 있어. 우리는 서로에게 속해 있으니까, 지금부터 영원토록 말이지!" <> 당신을 꽉 껴안는다. <><><>`>> + <> moans, looking to you with pure ecstasy. "The feeling of you ruining me gets better each time. We belong to each other, now and forever!" <> holds on to you tight. <><><>`>> <> - <> 순수한 황홀감으로 당신을 보며 신음한다. "네가 해버렸어. 네가 드디어 내 순결을 범해 버렸어. 우리는 지금부터 영원토록 서로에게 속해 있는거야!" <> 당신을 꽉 껴안는다. <><><>`>> + <> moans, looking to you with pure ecstasy. "You did it. You defiled me at last. We belong to each other forever now!" <> holds on to you tight. <><><>`>> <> <> <> - <> 순수한 황홀감으로 당신을 보며 헉 하는 소리를 낸다. "우리... 우리가 해버렸어... 나는 너무 행복해..." <> 당신을 꽉 껴안는다. "이건 우리가 영원해 서로에게 속한다는 뜻이지... 그렇지?" <><>`>> + <> gasps, looking to you with pure ecstasy. "We... we did it... I'm so happy..." <> holds on to you tightly. "This means we're bound together forever... right?" <><>`>> <> - <> 순수한 황홀감으로 당신을 보며 헉 하는 소리를 낸다. "우리는 이러지 말았어야 했어... 그런데 왜 나는 이렇게 행복하지...?" <> 당신을 꽉 껴안는다. "이건 우리가 영원해 서로에게 속한다는 뜻이지... 그렇지?" <><><>`>> + <> gasps, looking to you with pure ecstasy. "We shouldn't be doing this... so why do I feel so happy...?" <> holds on to you tightly. "This means we're bound together forever... right?" <><><>`>> <> <> <> <> <> - <> 순수한 황홀감으로 당신을 보며 신음한다. "네가 나를 타락시키는 느낌이 매번 더 좋아지고 있어! 나는 네게 속해 있으니까, 지금부터 영원토록 말이지!" <> 당신을 꽉 껴안는다. <><><>`>> + <> moans, looking to you with pure ecstasy. "The feeling of you ruining me gets better every time. I belong to you, now and forever!" <> holds on to you tightly. <><><>`>> <> - <> 순수한 황홀감으로 당신을 보며 신음한다. "네가 해버렸어. 네가 드디어 내 순결을 범해 버렸어. 우리는 지금부터 영원토록 서로에게 속해 있는거야!" <> 당신을 꽉 껴안는다. <><><>`>> + <> moans, looking to you with pure ecstasy. "You did it. You defiled me at last. We belong to each other forever now!" <> holds on to you tight. <><><>`>> <> <> <> - <> 순수한 황홀감으로 당신을 보며 헉 하는 소리를 낸다. "우리... 우리가 해버렸어... 나는 너무 행복해..." <> 당신을 꽉 껴안는다. "내가 너의 첫번째 상대가 아니었다고 해도... 이건 우리가 서로에게 영원히 묶여 있다는 뜻이지... 그렇지?" <><>`>> + <> gasps, looking to you with pure ecstasy. "We... we did it... I'm so happy..." <> holds on to you tightly. "Even if I wasn't your first... this means we're bound together forever... right?" <><>`>> <> - <> 순수한 황홀감으로 당신을 보며 헉 하는 소리를 낸다. "우리는 이러지 말았어야 했어... 그런데 왜 나는 이렇게 행복하지...?" <> 당신을 꽉 껴안는다. "내가 너의 첫번째 상대가 아니었다고 해도 괜찮아, 내가 네 마지막 상대인 한 말이야!" <><><>`>> + <> gasps, looking to you with pure ecstasy. "We shouldn't be doing this... so why do I feel so happy...?" <> holds on to you tightly. "It's okay that I wasn't your first, as long as I'm your last!" <><><>`>> <> <> <> @@ -48,18 +48,18 @@ <> <> 신음한다. "너도 이번이 처음으로 이렇게 하는 거지, 그렇지 않아? 속도를 줄이지 말아 줘. 제발!" <> <> 다리로 당신을 감싼다.`>> <> - <> 헉 하는 소리를 낸다. "우리... 우리가 아직 순결한 게 맞아...? 이 느낌... 너무 좋아..." <> 당신을 꽉 껴안는다.`>> + <> gasps. "Are... are you sure we're still pure...? It feels... too good..." <> holds on to you tightly.`>> <> <> <> - <> 신음한다. "와아, 그런 식으로 느낀다는 것은 나는 절대로 기대하지 못했어... 계속해 줘!" <> <> 다리로 당신을 감싼다.`>> + <> moans. "Woah, I never expected it to feel like that... keep going!" <> wraps <> legs around you.`>> <> - <> 헉 하는 소리를 낸다. "내... 내가 아직 순결한 게 맞아...? 이 느낌... 너무 좋아..." <> 당신을 꽉 껴안는다.`>> + <> gasps. "Are... are you sure I'm still pure...? It feels... too good..." <> holds on to you tightly.`>> <> <> <> <> - <> 신음하며 한 손을 당신의 머리에 가져다 댄다. "이번이 네가 처음으로 이렇게 하는 거지, 그렇지 않아? 너 정말 잘 하는데."`>> + <> moans and places a hand on your head. "This is your first time doing this, isn't it? You're doing great."`>> <> <> 헉 하는 소리를 낸다. "이... 이번이 네가 처음으로 이렇게 하는-" <> <> 양손을 <> 입에 대며 더 나오려는 음란한 소리를 억누른다.`>> <> @@ -112,11 +112,11 @@ <> <> <> - <> 당혹감에 차서 당신을 내려다본다. "이건 아파야만 하는 건데, 이... 이건 뭐지?" <><>`>> + <> looks down at you with confusion. "This is supposed to be painful, what... what is this?" <><>`>> <> - <> 당신을 보면서 미소지으며 킥킥 웃는다. "네가 몸부림치며 온몸을 비틀어 대는 것을 보는 것은 더 기분을 좋아지게 할 뿐이야." <><>`>> + <> smiles at you and giggles. "Seeing you struggle and writhe just makes it better." <><>`>> <> - <> 당신이 절정에 이르는 것을 당혹감과 공포심이 섞인 눈으로 쳐다본다. <><>`>> + <> looks to you with a mixture of panic and confusion as you climax. <><>`>> <> <> <> @@ -124,12 +124,12 @@ <> <> 당신에게 미소 짓는다. "내... 내가 했어! 내가 너를..." <> 킥킥 웃는다. <><>`>> <> - <> 눈이 커진다. "괘-괜찮아? 너 지금 경련하고 있어!" 당신은 대답을 하기 위해 어떻게든 끄덕였고, <> 마음을 진정시킨다. <><>`>> + <> eyes widen. "A-are you okay? You're shaking!" You manage to nod in response, and <> calms down. <><>`>> <> <> - <> 당신을 보며 미소지으면서 킥킥 웃는다. "이제, 내 차례야." <><>`>> + <> smiles at you and giggles. "My turn, now." <><>`>> <> - <> 당신이 몸을 떨자 사랑스러운 듯 당신을 꼭 붙들고 있다. <><>`>> + <> lovingly clings to you as you tremble. <><>`>> <> <> <> @@ -142,7 +142,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "만약... 만약 네가... 나는 물론..."`>> + <> barely manages to speak. "If... if you... I'm going to..."`>> <> <> 말한다. "사-사원에서는 분명 이것 때문에 나를 처벌할 거야... 하지만 나는 더 이상 신경쓰지 않아!"`>> <> @@ -151,22 +151,22 @@ <> <> <> - <> 당신의 머리를 잡고 당신의 얼굴을 <>에 가까이 끌어당긴다. "나는 네가 내 안에 아이를 만들어 주기를 원해. 당장."`>> + <> grabs you by the head and pulls your face close to <>. "I want you to put a child in me. Now."`>> <> <> 신음한다. "세게. 더 세게! 나는 거의 다 갔어!"`>> <> <> - <> 말한다. "이건 사원에서 내게 줄 어떤 처벌보다도 가치가 있어."`>> + <> speaks. "This is worth any punishment the temple will do to me."`>> <> <> 말한다. "나는 네가 내 안에 들어와 있는 느낌을 사랑해."`>> <> <> <> - <> 간신히 어떻게든 말한다. "아마 나는... 이제 거의...!"`>> + <> barely manages to speak. "I think I'm... getting close...!"`>> <> <> 말한다. "이제 우리는 둘 다 죄인이야, 그렇지 않아?"`>> <> - <> 말한다. "내 생각에 내가 이런 것에 점점 익숙해지는 것 같아."`>> + <> speaks. "I think I'm getting used to this."`>> <> <> <> @@ -174,7 +174,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "만약... 만약 네가... 나는 물론..."`>> + <> barely manages to speak. "If... if you... I'm going to..."`>> <> <> 말한다. "이건... 아직 너무 잘못되었다는 느낌인데... 하지만..."`>> <> @@ -190,11 +190,11 @@ <> <> <> - <> 간신히 어떻게든 말한다. "아마 나는... 이제 거의...!"`>> + <> barely manages to speak. "I think I'm... getting close...!"`>> <> <> 말한다. "우리는 아직 순결해, 그렇지? 이건 치지 않는 거지?"`>> <> - <> 말한다. "내 생각에 내가 이런 것에 점점 익숙해지는 것 같아."`>> + <> speaks. "I think I'm getting used to this."`>> <> <> <> @@ -202,7 +202,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "내... 내가... 다시 빼야 할까?"`>> + <> barely manages to speak. "Sh... should I... pull out?"`>> <> <> 말한다. "사-사원에서는 분명 이것 때문에 나를 처벌할 거야... 하지만 나는 더 이상 신경쓰지 않아!"`>> <> @@ -211,22 +211,22 @@ <> <> <> - <> 당신의 머리를 잡고 당신의 얼굴을 <>에 가까이 끌어당긴다. "나는 네 안에 아이를 만들기를 원해."`>> + <> grabs you by the head and pulls your face close to <>. "I want to put a child in you."`>> <> <> 신음한다. "나는 거의 가버리려고 해! 너는 준비 됐어?"`>> <> <> - <> 말한다. "이건 사원에서 내게 줄 어떤 처벌보다도 가치가 있어."`>> + <> speaks. "This is worth any punishment the temple will do to me."`>> <> <> 말한다. "나는 네 안에 들어가 있는 느낌을 사랑해."`>> <> <> <> - <> 간신히 어떻게든 말한다. "아마 나는... 이제 거의...!"`>> + <> barely manages to speak. "I think I'm... getting close...!"`>> <> <> 말한다. "이제 우리는 둘 다 죄인이야, 그렇지 않아?"`>> <> - <> 말한다. "내가 이 느낌에 점점 익숙해지는 것 같아."`>> + <> speaks. "I'm getting used to this feeling."`>> <> <> <> @@ -235,9 +235,9 @@ <> <> <> - <> 간신히 어떻게든 말한다. "내... 내가... 다시 빼야 할까?"`>> + <> barely manages to speak. "Sh... should I... pull out?"`>> <> - <> 간신히 어떻게든 말한다. "만약... 만약 네가... 나는 물론..."`>> + <> barely manages to speak. "If... if you... I'm going to..."`>> <> <> <> 말한다. "이건... 아직 너무 잘못되었다는 느낌인데... 하지만..."`>> @@ -247,7 +247,7 @@ <> <> <> - <> 당신의 머리를 잡고 당신의 얼굴을 <>에 가까이 끌어당긴다. "나는 네 안에 아이를 만들기를 원해."`>> + <> grabs you by the head and pulls your face close to <>. "I want to put a child in you."`>> <> <> 신음한다. "나는 거의 가버리려고 해! 너는 준비 됐어?"`>> <> @@ -258,11 +258,11 @@ <> <> <> - <> 간신히 어떻게든 말한다. "아마 나는... 이제 거의...!"`>> + <> barely manages to speak. "I think I'm... getting close...!"`>> <> <> 말한다. "우리는 아직 순결해, 그렇지? 이건 치지 않는 거지?"`>> <> - <> 말한다. "내 생각에 내가 이런 것에 점점 익숙해지는 것 같아."`>> + <> speaks. "I'm getting used to this feeling."`>> <> <> <> @@ -270,9 +270,9 @@ <> <> <> - <> 간신히 어떻게든 말한다. "아... 무언가가 오고 있어..."`>> + <> barely manages to speak. "Ah... something's coming..."`>> <> - <> 말한다. "점점 이상한 느낌이 들기 시작하는데..."`>> + <> speaks. "I'm starting to feel weird..."`>> <> <> 킥킥 웃는다. "이거 간지러워!"`>> <> @@ -286,7 +286,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "아마 나는... 이제 거의...!"`>> + <> barely manages to speak. "I think I'm... getting close...!"`>> <> <> 말한다. "너 숨 쉴 수 있지, 그렇지? 내가 너를 아프게 하는 건 아니지?"`>> <> @@ -298,9 +298,9 @@ <> <> <> - <> 간신히 어떻게든 말한다. "아... 무언가가 오고 있어..."`>> + <> barely manages to speak. "Ah... something's coming..."`>> <> - <> 말한다. "점점 이상한 느낌이 들기 시작하는데..."`>> + <> speaks. "I'm starting to feel weird..."`>> <> <> 킥킥 웃는다. "이거 간지러워!"`>> <> @@ -314,7 +314,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "아마 나는... 이제 거의...!"`>> + <> barely manages to speak. "I think I'm... getting close...!"`>> <> <> 말한다. "너 숨 쉴 수 있지, 그렇지? 내가 너를 아프게 하는 건 아니지?"`>> <> @@ -326,7 +326,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "만약... 만약 네가... 나는 물론..."`>> + <> barely manages to speak. "If... if you... I'm going to..."`>> <> <> 말한다. "이건... 아직 너무 잘못되었다는 느낌인데... 하지만..."`>> <> @@ -342,11 +342,11 @@ <> <> <> - <> 간신히 어떻게든 말한다. "아마 나는... 이제 거의...!"`>> + <> barely manages to speak. "I think I'm... getting close...!"`>> <> <> 말한다. "우리는 아직 순결해, 그렇지? 이건 치지 않는 거지?"`>> <> - <> 말한다. "내 생각에 내가 이런 것에 점점 익숙해지는 것 같아."`>> + <> speaks. "I'm getting used to this feeling."`>> <> <> <> @@ -355,7 +355,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "그...그걸... 집어 넣지 마..."`>> + <> barely manages to speak. "D...don't... put it in..."`>> <> <> 말한다. "끄... 끝만 대는 거지, 그렇지?"`>> <> @@ -363,9 +363,9 @@ <> <> <> - <> 헐떡이며 신음하는 사이사이로 말한다. "그...그냥 그걸 빨리 넣어버려줘. 나는 더이상 버틸 수가 없어!"`>> + <> speaks through gasps and moans. "J...just put it in already. I can't take it anymore!"`>> <> - <> 애원하는 눈으로 웃으며, <> 다리를 더욱 벌린다. "무... 무엇을 기다리고 있어...?"`>> + <> smiles at you with pleading eyes, and spreads <> legs further. "Wh... what are you waiting for...?"`>> <> <> 킥킥 웃는다. "하... 하고 싶은 대로 해. 부드럽게만 해 줘, 제발."`>> <> @@ -378,12 +378,12 @@ <> 신음한다. "나를 희롱하기만 하는 것은 그만둬!"`>> <> <> - <> 말한다. "나는 네 안에 들어가기를 원해."`>> + <> speaks. "I want to be inside you."`>> <> <> 말한다. "나는 네가 내 안에 들어오기를 원해."`>> <> <> - <> 말한다. "나는 그것을 집어넣기를 원해."`>> + <> speaks. "I want to put it in."`>> <> <> <> @@ -393,7 +393,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "내... 생각에 나는 준비 된 것 같아..."`>> + <> barely manages to speak. "I... think I'm ready..."`>> <> <> 말한다. "부드럽게 해 줘, 제발..."`>> <> @@ -401,9 +401,9 @@ <> <> <> - <> 헐떡이며 신음하는 사이사이로 말한다. "그...그냥 그걸 빨리 넣어버려줘. 나는 더이상 버틸 수가 없어!"`>> + <> speaks through gasps and moans. "J...just put it in already. I can't take it anymore!"`>> <> - <> 애원하는 눈으로 웃으며, <> 다리를 더욱 벌린다. "무... 무엇을 기다리고 있어...?"`>> + <> smiles at you with pleading eyes, and spreads <> legs further. "Wh... what are you waiting for...?"`>> <> <> 킥킥 웃는다. "최소한 이쪽으로는 순결은 지킬 수 있겠지."`>> <> @@ -414,7 +414,7 @@ <> <> 말한다. "나는 네가 내 안에 들어오기를 원해."`>> <> - <> 말한다. "나는 그것을 집어넣기를 원해."`>> + <> speaks. "I want to put it in."`>> <> <> <> @@ -423,7 +423,7 @@ <> <> <> - <> 간신히 어떻게든 말한다. "그...그걸... 집어 넣지 마..."`>> + <> barely manages to speak. "D...don't... put it in..."`>> <> <> 말한다. "끄... 끝만 대는 거지, 그렇지?"`>> <> @@ -431,7 +431,7 @@ <> <> <> - <> 헐떡이며 신음하는 사이사이로 말한다. "그...그냥 내가 그걸 넣도록 해 줘! 나는 더이상 버틸 수가 없어!"`>> + <> speaks through gasps and moans. "J...just let me put it in already! I can't take it anymore!"`>> <> <> 애원하듯 당신을 보며 웃는다. "무... 무엇을 기다리고 있어...?"`>> <> @@ -442,9 +442,9 @@ <> <> 신음한다. "나를 희롱하기만 하는 것은 그만둬!"`>> <> - <> 말한다. "나는 네 안에 들어가기를 원해."`>> + <> speaks. "I want to be inside you."`>> <> - <> 말한다. "나는 그것을 집어넣기를 원해."`>> + <> speaks. "I want to put it in."`>> <> <> <> @@ -452,21 +452,21 @@ <> <> <> 얼굴을 붉힌다. "이-이건 처벌이어야만 하는 거라고!"`, + `<> blushes. "Th-this is supposed to be a punishment!"`, `<> 얼굴을 붉힌다. "이해할 수가 없어, 내가 너를 충분히 세게 처벌하고 있지 않는 거야?"`, `<> 얼굴을 붉힌다. "너는... 이걸 즐기고 있는 거야?" <><>` )>> <> <> 히죽 웃는다. "네가 이것을 좋아하는 것을 알고 있었지." <><>`, - `<> 히죽 웃는다. "음란한 <> 처벌받아야지, 그리고 나는 네가 음란한 것을 사랑한다니까." <><>`, - `<> 히죽 웃는다. "나는 네 신음소리를 만끽하기를 원해. 내가 네 엉덩이를 더 자주 때려줘야 겠어." <><>` + `<> smirks. "Naughty <> get punished, and I love it when you're naughty." <><>`, + `<> smirks. "I want to savour your moans. I should spank you more often." <><>` )>> <> <> 입술을 불룩 내민다. "이건 훈육이어야만 하는 거라고, 너도 알겠지만."`, + `<> pouts. "This is supposed to be discipline, you know."`, `<> 얼굴을 붉힌다. "너는... 이걸 즐기고 있어?" <><>`, - `<> 얼굴을 붉힌다. "이건 처벌이어야만 하는 거라고! 그런 식으로 신음하지 마!" <><>` + `<> blushes. "This is supposed to be a punishment! Stop moaning like that!" <><>` )>> <> <> @@ -479,7 +479,7 @@ <> <> 말한다. "누구도 훈육을 피해갈 수는 없어!"`>> <> - <> 말한다. "가만히 있어! 너는 교훈을 얻을 필요가 있다고!"`>> + <> speaks. "Hold still! You need to learn your lesson!"`>> <> <> 말한다. "아픔이 네가 규칙을 기억하는 데 도움을 줄 거야! 처벌을 받아들여!"`>> <> @@ -491,7 +491,7 @@ <> <> 말한다. "네가 이걸 즐기고 있을 거라고 장담해."`>> <> - <> 말한다. "이게 너를 화나게 하니? 어쩌면 네가 나에게 똑같은 것을 하도록 해 줘야 할지도 모르겠네."`>> + <> speaks. "Does this make you angry? Maybe I'll let you do the same to me."`>> <> <> 말한다. "네가 아픔을 즐기기 전까지는 나는 멈추지 않을 거야."`>> <> @@ -503,9 +503,9 @@ <> <> 말한다. "이상한 생각은 하지 마, 이건 온전히 훈육을 위한 거라고."`>> <> - <> 말한다. "나는 네가 그냥 예의바르게 행동해서 내가 이렇게 해야만 하지 않기를 바라, 나는 너를 아프게 하기 싫거든."`>> + <> speaks. "I wish you would just behave so I wouldn't have to do this, I don't like hurting you."`>> <> - <> 말한다. "조던이 내게 훈육에 대해 한두가지 가르쳐 주었지, 이제 네가 그것들을 배울 차례야."`>> + <> speaks. "Jordan's taught me a thing or two about discipline, it's time for you to learn too."`>> <
> <> <
> @@ -514,7 +514,7 @@ <> <> 혼란스러운 듯 보인다. "왜 네가 미안하다고 하는 거야? 모든 것이 괜찮아, 약속할게."`>> <> - <> 말한다. "나는 네가 어떻게 내게 보상해야 하는지 알고 있지."`>> + <> speaks. "I know how you can make it up to me."`>> <> <> <> @@ -527,7 +527,7 @@ <> <> <> - <> 기쁜 얼굴로 미소 짓는다. "어쨌든, 우리는 둘 다 순결해야만 하니까." <><>`>> + <> smiles with glee. "We both have to stay pure, after all." <><>`>> <> <> 과장된 한숨을 쉬었지만, 미소 짓는다. "너는 가끔 정말 씨팔 성희롱꾼이 된다니까!" <><>`>> <> @@ -536,9 +536,9 @@ <> <> <> 말한다. "이 벨트에 대해서는 미안하지만, 나는 정말 안전하게 있을 필요가 있어서..."`, - `<> 말한다. "제발 내 벨트를 어떻게 하려고 너 자신을 상처입히지는 말아줘."`, - `<> 말한다. "이... 이 벨트 때문에 네가 나를 사랑하는 것을 그만두지는 않겠지, 그렇지?"` + `<> speaks. "Sorry about the belt, but I really need to stay safe..."`, + `<> speaks. "Please don't hurt yourself trying to mess with my belt."`, + `<> speaks. "Th... the belt's not going to make you stop loving me, is it?"` )>> <> <> 당신의 <> 응시한다. "우리 커플룩이네!"`, `<> 말한다. "우리가 둘 다 정조대를 차고 있다는 것은 우리가 아직 둘 다 순결하다는 뜻이지, 그렇지?"`, - `<> 당신의 <> 응시한다. "최소한 우리가 둘 다 보호되고 있는 동안에는 우리가 걱정할 필요가 없겠지."` + `<> stares at your <>. "At least we don't have to worry since we're both protected."` )>> <> <> 당신의 <> 응시한다. "네가 나에게서 내 것을 벗겨냈는데, 이제 네가 그걸 하고 있다고?"`, + `<> stares at your <>. "You got me to remove mine, but now you have one?"`, `<> 당신의 <> 응시한다. "그래서 이게 이런 느낌이로군."`, `<> 당신의 <> 응시한다. "이런."` )>> @@ -572,14 +572,14 @@ <> <> 말한다. "좋아, 들어봐, 만약 우리가 함께 정조대를 정말 세게 치면..."`, - `<> 말한다. "우리는 우리 정조대 열쇠를 빨리 찾아내야만 해."`, - `<> 당신의 <> 응시한다. "내 생각에 우리는 둘 다 그냥 음란한 죄인이고, 그것에 대해서 아무것도 할 수 없을 것 같아."` + `<> speaks. "We need to find the keys for our chastity devices soon."`, + `<> stares at your <>. "I guess we're both just lusty sinners, and can't do anything about it."` )>> <> <> 당신의 <> 응시한다. "날 놀리는 거지, 그렇지?"`, `<> 당신의 <> 응시한다. "이거 농담이야?"`, - `<> 당신의 <> 응시한다. "이런 순 성희롱꾼 같으니, 네가 나에게서 내 것을 벗긴 이유가 단지-"` + `<> stares at your <>. "Oh you absolute sextease, you got me to remove mine just to-"` )>> <
> <> @@ -605,7 +605,7 @@ <> <> 움찔한다. "날 또 때려 줘! 나는 그게 좋아!"`>> <> - <> 웃는다. "네가 난폭하게 하는 것을 즐긴다면, 나도 그걸 즐겨야만 하겠지."`>> + <> laughs. "If you enjoy being rough, I guess I have to enjoy it too."`>> <> <> 움찔한다. "아야! 그건 진짜 아팠어!"`>> <> @@ -614,16 +614,16 @@ <> <> <> - <> 당신이 당신 자신을 가리려고 하는 것을 알아챈다. "너-너 불안하니? 괜찮아, 나도 그렇거든."`>> + <> notices your attempts to cover yourself. "A-are you nervous? That's okay, I am too."`>> <> - <> 당신이 당신 자신을 가리려고 하는 것을 알아챈다. "내가 불안해 했을 때를 기억해? 어떻게 이렇게 바뀌었는지."`>> + <> notices your attempts to cover yourself. "Remember when I was the apprehensive one? How things have changed."`>> <> - <> 당신이 당신 자신을 가리려고 하는 것을 알아챈다. "괜찮아, 나는 네가 이런 모습인 것을 보는 것을 더이상 신경쓰지 않아. 우리는 서로를 믿을 수 있으니까, 그렇지?"`>> + <> notices your attempts to cover yourself. "It's okay, I don't mind seeing you like this anymore. We can both trust each other, right?"`>> <> <> <> <> - <> 말한다. "나는 항상 네 머리카락을 좋아했어. 그게 내가 내 모습에 대한 네 조언을 받아들이는 데 망설이지 않았던 이유야."`>> + <> speaks. "I've always loved your hair. That's why I didn't hesitate to take your advice on my appearance."`>> <> <> 말한다. "나는 항상 네 머리카락을 좋아했어."`>> <> @@ -640,7 +640,7 @@ <
> <> <> - <> 헐떡이며 신음하는 사이사이로 말한다. "조금만 더!"`>> + <> speaks through gasps and moans. "Just a little more!"`>> <> <> 말한다. "너 이거를 정말 잘 하는구나."`>> <> @@ -648,11 +648,11 @@ <> <> <> - <> 헐떡이며 신음하는 사이사이로 말한다. "나는 점점 가까워지고 있어..."`>> + <> speaks through gasps and moans. "I'm getting close..."`>> <> <> 말한다. "조금 더 참을 만 할 것 같아."`>> <> - <> 킥킥 웃는다. "다른 누군가가 나를 이런 식으로 만지는 것을 원하지는 않을 거야."`>> + <> giggles. "I wouldn't want anyone else to touch me like this."`>> <> <> @@ -698,7 +698,7 @@ <> <> 말한다. "너는 정말 특별해. 나는 과학 수업에서 이런 것을 전혀 배우지 못했어."`, - `<> 말한다. "내가 점점 양쪽을 동시에 애무하는 데에 익숙해지는 것 같아."`, + `<> speaks. "I think I'm getting used to working with both."`, `<> 말한다. "나는 거의 질투하는 것 같아. 양쪽으로 느끼는 것이 어떤지 알았으면 좋겠거든."` )>> <> @@ -715,8 +715,8 @@ <> <> 말한다. "이것 다음에 내 손가락을 네 입 안에 넣어서, 네 자신을 맛 볼 수 있게 해 줄게."`, - `<> 말한다. "그냥 긴장 풀고, 내가 여기를 만지도록 해 줘."`, - `<> 킥킥 웃는다. "가만히 있어야만 해!"` + `<> speaks. "Just relax, and let me take care of this."`, + `<> giggles. "Make sure to keep it down!"` )>> <> <> <> <> 말한다. "내가 너를 바싹 마를 때까지 짜 줄게."`, - `<> 말한다. "그냥 긴장 풀고, 내가 여기를 만지도록 해 줘."`, - `<> 킥킥 웃는다. "가만히 있어야만 해!"` + `<> speaks. "I'm going to wring you dry."`, + `<> speaks. "Just relax, and let me take care of this."`, + `<> giggles. "Make sure to keep it down!"` )>> <> <> <> 말한다. "아마도 우리는 거기에 무언가 더 커다란 것을 집어넣는 것이 좋겠는걸."`, - `<> 말한다. "그냥 긴장 풀고, 내가 여기를 만지도록 해 줘."`, - `<> 킥킥 웃는다. "가만히 있어야만 해!"` + `<> speaks. "Just relax, and let me take care of this."`, + `<> giggles. "Make sure to keep it down!"` )>> <> <> <> <> 두 손가락을 사용해 <> 아랫입술을 벌린다. "경치를 즐기고 있어?"`, + `<> uses two fingers to spread <> lips. "Enjoying the view?"`, `<> 말한다. "그래, 알아, 완벽해 보이지, 그렇지 않아?"`, `<> 킥킥 웃는다. "여기는 전부 네 거야."` )>> <> <> 말한다. "너는 쳐다보고 있기만 하는구나. 최소한 나도 네 것을 볼 수 있도록 해 줘..."`, - `<> 말한다. "무엇을 하려고 하는 거야?"`, + `<> speaks. "What are you going to do?"`, `<> 말한다. "어...어때? 그냥 거기에 서 있지만 말고..."` )>> <> @@ -825,15 +825,15 @@ <> <> <> - <> 당신의 <> 응시한다. <> 잠시동안 그것에 빠져 있는 듯이 보인다. <><>`>> + <> stares at your ` + $worn.neck.name + `. <> seems to get lost in it for a moment. <><>`>> <> <> 당신의 <> 살펴본다. "그건... 아주 예쁘지만, 조금 마음이 어지러워지는 것 같기도 한데..." <><>`>> <> <> <> - <> 당신의 옷을 살펴보고, 킥킥 웃는다. "그건 적절한 학교 유니폼이 아니야. 걱정 마, 내가 네가 그것을 벗는 것을 도와줄게." <>`>> + <> looks over your clothes, and giggles. "That's not proper school uniform. Don't worry, I can help you take it off." <>`>> <> - <> 당신의 옷을 살펴본다. "너-너는 복장 규칙을 정말로 지켜야 해, 문제가 생기기 전에 말야..."`>> + <> looks over your clothes. "Y-you should really adhere to the dress code, before you get in trouble..."`>> <> <> <> @@ -843,7 +843,7 @@ <> <> <> - <> 당신의 몸을 살펴보고, 킥킥 웃는다. "너는 이런 식으로 전부 노출하는게 훨씬 나아 보여." <><>`>> + <> looks over your body, and giggles. "You look even better all exposed like this." <><>`>> <> <> 부끄러운 눈으로 당신의 노출된 몸을 살펴본다. "너-너는... 보기에... 으음..." <> 잠시동안 시선을 피한다. "너는 보기 좋아." <><><><>`>> <> @@ -856,9 +856,9 @@ <> <> 소곤거린다. "미안, 내가 무엇을 하고 있는지 정말 모르겠어..."`>> <> - <> 말한다. "우리는 즐기기 위해 무엇을 해야 하는지 확실히 알고 있지."`>> + <> speaks. "We sure know how to have fun."`>> <> - <> + <> <> <> <> @@ -890,11 +890,11 @@ <> <> <> - <> 크게 숨을 들이마신다. "우리... 조용히 해야 할 필요가 있어..."`>> + <> takes a deep breath. "We... need to stay quiet..."`>> <> <> 킥킥 웃는다. "쉬잇!"`>> <> - <> 말한다. "들키지 않게 조심하자..."`>> + <> speaks. "Let's try not to get caught..."`>> <> <> <> @@ -913,7 +913,7 @@ <> <> 킥킥 웃는다. "이 장소를 더럽혀 버리자고."`>> <> - <> 킥킥 웃는다. "우...우리가 사원에서 이런 짓을 하고, 어떤 것도 우리를 막지 않아..."`>> + <> giggles. "W...we're doing this in the temple, and nothing is stopping us..."`>> <> <> <> diff --git a/game/base-combat/speech.twee b/game/base-combat/speech.twee index a35321dbc..14072c360 100644 --- a/game/base-combat/speech.twee +++ b/game/base-combat/speech.twee @@ -158,10 +158,15 @@ `"둘 다? 네게 더 이상한 것은 없었으면 좋겠네."`, `"왜 나는 평범한 사람을 만나질 못하는 거야?"`, ][random(0,4)]>> - <> - <> - <> - <> 당신의 $_sexToy 굶주린 눈으로 응시한다. + <> + <> + <> + <> + <> + <> + <> + <> + <> 당신의 $_sexToy 굶주린 눈으로 응시한다. <>. 그걸로 너와 무엇을 할 지 알겠어."`, ][random(0,4)]>> - <> - <> - <> + <> + <> + <> + <> + <> + <> + <> + <> <> 당신의 $_sexToy 충격받은 눈으로 쳐다본다. <> - <> - <> - <> + <> + <> + <> + <> + <> + <> + <> + <> <> 당신의 $_sexToy 쳐다보며 한숨을 쉰다. <> - <> - <> - <> + <> + <> + <> + <> + <> + <> + <> + <> <> 당신의 $_sexToy 노려본다. <> <> 싱긋 웃는다. "네 눈을 보면 알 수 있어. 너는 이것을 갖고 싶어 죽을 지경이지, 그렇지?" <> 당신의 귀에 속삭이기 위해 앞으로 숙인다. "나 같은, 네 모든 구멍을 내 줄기로 채울 수 있는 누군가 말야. <> - 내가 네 소원을 이뤄주기로 하지, 조그마한 요부여." + Let me fulfil that wish, little witch." <> 너를 내 제물로 삼기로 하지." <> @@ -4664,9 +4684,9 @@ <> <> <> - <> + <> <> 당신의 $_sexToy 보며 킥킥 웃는다. "` + [`한동안 너랑 이걸 하고 싶었어.`, `이건 정말 추잡해... 계속 해.`][random(0,1)] + `"`>> - <> + <> <> 당신의 $_sexToy 보며 얼굴을 붉힌다. "` + [`우와! 부끄럽지 않아?`, `천천히 해줘, 알았지?`][random(0,1)] + `"`>> <> <> 당신의 $_sexToy 보며 킥킥 웃는다. "` + [`네가 그걸 만지고 있다는 게 믿어지지가 않아.`, `이건 정말 추잡해... 계속 해.`][random(0,1)] + `"`>> @@ -4846,6 +4866,28 @@ <> <> <> 당신의 $_sexToy 보고 싱긋 웃는다. "` + [`착한 <>.`, `네가 추잡한 <>는 걸 알았다니까.`, `그것보다 더 좋은 생각은 없을 거야.`, `뭐야, 네 <> 입을 쓰는 게 싫증난 거야?`][random(0,3)] + `"`>> + <> + <> + <> laughs. "You always get a slutty look on your face when I squeeze this."`, `<> raises an eyebrow. "That good?"`, `<> raises an eyebrow. "Horny slut."`][random(0, 2)]>> + <> + <> + <> smirks. "You look so cute with your tiny${$player.virginity.penile is true ? " virgin" : " useless"} penis all locked up." <><>`>> + <> + <> + <> smirks. "You clearly don't need to be unlocked when you've obviously been having a lot of fun without it."`>> + <> + <> smirks. "Don't worry, you'll still be my slut even when you get ${$pregnancyspeechdisable is "f" ? "knocked" : "fucked"} up."`>> + <> + <> smirks. "Maybe I'll unlock you in the future, whenever I'm ready to have my own."`>> + <> + <> smirks. "Not like you need that thing to have fun."`>> + <> + <> + <> smirks. "${ + ["Can't trust a slut like you to run around without being locked up.", "I hope it's getting you all hot and bothered.", "If only these things were indestructible."][random(0,2)] + }"`>> + <> + <> <> <> <> @@ -4869,7 +4911,7 @@ <> <> <> - <> + <> <> 당신의 $_sexToy 보며 말한다. "` + [`뭐, 이건 또 멋진 기분전환 방법인데.`, `누구든 각각 자신의 장사도구들을 가지고 있게 마련이지.`][random(0,1)] + `"`>> <> <> 당신의 $_sexToy 보며 말한다. "` + [`오, 안돼. 이것 때문에 네게 돈을 너무 많이 썼나 봐.`, `당장 그것 다시 넣어 둬.`][random(0,1)] + `"`>> @@ -4907,10 +4949,10 @@ <> <> <> - <> + <> <> 당신의 $_sexToy 보고 싱긋 웃는다. "` + [`이제 우리 사이가 조금 더 진전되겠네.`, `귀여운 장난감이네. 그걸 어떻게 사용하는 지 알고 있는 게 좋을 거야.`, `그게 도시에서 구할 수 있는 가장 좋은 거야?`][random(0,2)] + `"`>> - <> - <> 당신의 $_sexToy 보고 망설인다. "` + [`알았어. 네 판단을 믿겠어.`, `네가 그 물건을 어떻게 사용하는 지 알고 있기를 바라.`, `너는 항상 어떻게 좋은 시간을 보낼 지 알고 있으니까.`][random(0,2)] + `"`>> + <> + <> looks at your $_sexToy and hesitates. "` + [`All right. I trust your judgement.`, `Hope you know how to use that thing.`, `You always know just how to have a good time.`][random(0,2)] + `"`>> <> <> 당신의 $_sexToy 보고 미소 짓는다. "` + [`네 사고방식이 맘에 들어.`, `우리가 거친 시간을 보낼 것 같은데.`, `그래서 이게 도시에서 파는 물건이구나.`][random(0,2)] + `"`>> <> diff --git a/game/base-combat/stalk/stalk.twee b/game/base-combat/stalk/stalk.twee index 3f2c52132..3fb6d1f5d 100644 --- a/game/base-combat/stalk/stalk.twee +++ b/game/base-combat/stalk/stalk.twee @@ -910,7 +910,7 @@ <> <> <> - <> + <> <> <><> 당신을 위아래로 쳐다보더니, <> 눈을 굴린다. "네게 쓸 시간이 없다." <><> <> 눈을 가늘게 뜨고 보다가 조소하면서 몸을 돌린다. "수고할 가치가 없어." diff --git a/game/base-combat/struggle.twee b/game/base-combat/struggle.twee index a72b5ef33..b1c067a6f 100644 --- a/game/base-combat/struggle.twee +++ b/game/base-combat/struggle.twee @@ -150,12 +150,12 @@ 당신은 당신의 <> <><잡고 있지 않다`>> - <><허술하게 잡고 있다`>> - <><약하게 잡고 있다`>> - <><애매하게 잡고 있다`>> - <><적절하게 잡고 있다`>> - <><강하게 잡고 있다`>> - <><굳건하게 잡고 있다`>> + <>a fragile + <>a weak + <>a tentative + <>a decent + <>a strong + <>an iron <> <> <> @@ -2191,7 +2191,7 @@ <> 그 <> 휙 하고 움직이며 그것을 당신의 몸에서 뜯어내어, 당신의 <> 노출시킨다. <> - 그것의 <> 그 소재를 뚫고, 잡아당긴다. 그 소재가 + Its appendage pierces the material, and pulls. The material <> 팍 하는 소리를 내더니 <> diff --git a/game/base-combat/swarm-effects.twee b/game/base-combat/swarm-effects.twee index 6d269af1f..f23be5e70 100644 --- a/game/base-combat/swarm-effects.twee +++ b/game/base-combat/swarm-effects.twee @@ -426,7 +426,6 @@ <> <> <> -
<
> <
> <
> diff --git a/game/base-combat/tentacles/abomination.twee b/game/base-combat/tentacles/abomination.twee index bbedf3a1c..f9cd495fc 100644 --- a/game/base-combat/tentacles/abomination.twee +++ b/game/base-combat/tentacles/abomination.twee @@ -82,7 +82,6 @@ <> <> <> -
<
> <
> <
> diff --git a/game/base-combat/tentacles/tentacleActionsGeneration.twee b/game/base-combat/tentacles/tentacleActionsGeneration.twee index 6474439aa..da720b014 100644 --- a/game/base-combat/tentacles/tentacleActionsGeneration.twee +++ b/game/base-combat/tentacles/tentacleActionsGeneration.twee @@ -1510,7 +1510,6 @@ <> <> <> -
<
> <
> <> diff --git a/game/base-combat/tentacles/tentacles.twee b/game/base-combat/tentacles/tentacles.twee index 9fd229acc..9938bf76c 100644 --- a/game/base-combat/tentacles/tentacles.twee +++ b/game/base-combat/tentacles/tentacles.twee @@ -209,9 +209,7 @@ <> <> <> -
<> -
<
> <
> <
> diff --git a/game/base-combat/vore.twee b/game/base-combat/vore.twee index 098008d72..722045c1e 100644 --- a/game/base-combat/vore.twee +++ b/game/base-combat/vore.twee @@ -255,6 +255,11 @@ <><><> <> 당신은 그 <> 위장 안에 있고, 당신의 머리를 미끈미끈한 액체 위로 내어 놓는 것만도 힘들다. + <> + You feel something round and smooth pressing against your body. You grab it and investigate it. You found a rare pearl. It's going to fetch a tidy sum. + <> + <> + <> <><><> <
> <> @@ -638,7 +643,6 @@ <> <> <> -
<
> <
> <> @@ -882,3 +886,4 @@ <
>
<> + diff --git a/game/base-debug/clothesTesting.twee b/game/base-debug/clothes-testing.twee similarity index 83% rename from game/base-debug/clothesTesting.twee rename to game/base-debug/clothes-testing.twee index e8b74fb36..ae314bd8b 100644 --- a/game/base-debug/clothesTesting.twee +++ b/game/base-debug/clothes-testing.twee @@ -16,6 +16,7 @@ face:0, neck:0, hands:0, + handheld:0, upper:0, lower:0, under_upper:0, @@ -29,6 +30,7 @@ face:"full", neck:"full", hands:"full", + handheld:"full", upper:"full", lower:"full", under_upper:"full", @@ -40,10 +42,14 @@ <> <> <> + <> + <> + <> + <> + <> + <> <> <> <> @@ -159,6 +176,7 @@ <> <> <> + <> <> <> <> @@ -167,6 +185,16 @@ <> <> <> + <> + <> + <> + <> + <> | + <> + <> + <> + <> + <> <> <> <> @@ -175,6 +203,7 @@ <> <> <> + <> <> <> <> @@ -183,6 +212,7 @@ <> <> <> + <> <> <> <> @@ -191,6 +221,7 @@ <> <> <> + <> <> <> <> @@ -205,6 +236,9 @@
<>
+
+ <> +
<>
@@ -271,7 +305,7 @@ <> <> - <> + <> <> <> <> @@ -284,12 +318,12 @@ <>

Body

- - <> - Body Type: - <> + Body Type
- + | + | + | +

Skin Colour
| @@ -377,7 +411,6 @@ $('#numberslider-value-characterviewerbodystate' + className).html(" "+labels[e.currentTarget.value])); $('#numberslider-value-characterviewerbodystate' + className).html(" "+labels[$characterViewer.bodyState[name]]); } - setupListener("bodytype", _bodytypename); setupListener("breastSize", _breastNames); setupListener("vaginaCum", _liquidNames); setupListener("anusCum", _liquidNames); @@ -397,6 +430,31 @@ |

+ Brow
+ | + | + | + | + | +

+ + Blush level
+ | + | + | + | + | +

+ + Mouth
+ | + | + | + | + | +

+ +

Eyes

Show Trauma Eyes
| | @@ -417,6 +475,14 @@ |

+ Tears level
+ | + | + | + | + | +

+ Left Eye Colour
| @@ -444,20 +510,10 @@ | |

+<
> - Hair Colour -
- <> - <> - <> - | - <> -

+<> +

Hair

Hair Length
@@ -476,29 +532,56 @@ < { if (!V.shopClothingFilter || !V.shopClothingFilter.active || !V.shopClothingFilter.traits.length || style.type.find(type => V.shopClothingFilter.traits.includes(type))) T.hairTypeByName[style.name_cap] = style.variable; })>> - - <> - <> - | - <> + <> + <> + <>

- - Fringe Colour -
- <> - <> - <> - | - <> -

+
+ <> + <> + <> + <> + <> +

+ Hair Colour +
+ <> + <> + <> +

+
+
+ <> + <> + <> + <> + <> +

+ Style: + <> + <> +

+ First Colour: +
+ <> + <> + <> +

+ Second Colour: +
+ <> + <> + <> +

+
Fringe Length
@@ -518,14 +601,57 @@ < { if (!V.shopClothingFilter || !V.shopClothingFilter.active || !V.shopClothingFilter.traits.length || style.type.find(type => V.shopClothingFilter.traits.includes(type))) T.fringeTypeByName[style.name_cap] = style.variable; })>> - <> - <> - | - <> + + <> + <> + <>

+ + <> +
+ <> + <> + <> + <> + <> +

+ Fringe Colour +
+ <> + <> + <> +

+
+
+ <> + <> + <> + <> + <> +

+ Style: + <> + <> +

+ First Colour: +
+ <> + <> + <> +

+ Second Colour: +
+ <> + <> + <> +

+
<
> <> @@ -873,21 +999,29 @@ <> /*Hair*/ + <> <> + <> <> <> <> + <> <> + <> <> <> <> /*Face*/ <> + <> + <> <> <> <> <> + <> + <> <> <> @@ -912,6 +1046,7 @@ <> <> <> + <> <> <> <> @@ -920,6 +1055,12 @@ <> <> + <> + <> + <> + <> + <> + /*tf*/ <> <> @@ -978,7 +1119,7 @@ <>

Clothes

- <> + <> <> <> <> diff --git a/game/base-debug/customTestroom.twee b/game/base-debug/custom-testroom.twee similarity index 100% rename from game/base-debug/customTestroom.twee rename to game/base-debug/custom-testroom.twee diff --git a/game/base-debug/debug.twee-config.yml b/game/base-debug/debug.twee-config.yml new file mode 100644 index 000000000..16314a599 --- /dev/null +++ b/game/base-debug/debug.twee-config.yml @@ -0,0 +1,120 @@ +aliases: + unused: &unused + border: 0px +sugarcube-2: + macros: + characterViewerModel: + name: characterViewerModel + characterViewerModelClothes: + name: characterViewerModelClothes + characterViewerModelClothesControls: + name: characterViewerModelClothesControls + characterViewerModelExportControls: + name: characterViewerModelExportControls + checkEventNPC: + description: |- + Checks that npc at given slot was properly closed + + `<>` + - **slot**: `number` - slot to check (zero-based) + parameters: + - 'number' + clothesTestingBodyControls: + name: clothesTestingBodyControls + clothesTestingFaceControls: + name: clothesTestingFaceControls + clothesTestingGenerateClothes: + name: clothesTestingGenerateClothes + clothesTestingHairControls: + name: clothesTestingHairControls + clothesTestingImageGenerateAll: + name: clothesTestingImageGenerateAll + clothesTestingImageUpdate: + name: clothesTestingImageUpdate + clothesTestingTransformationsControls: + name: clothesTestingTransformationsControls + debug: + name: debug + debugactionsman: + name: debugactionsman + debugAdd: + name: debugAdd + debugCharacter: + name: debugCharacter + debugContents: + name: debugContents + debugEvents: + name: debugEvents + debugFavourites: + name: debugFavourites + debugGenerateDivs: + name: debugGenerateDivs + debugMain: + name: debugMain + ejaculation-demon: + name: ejaculation-demon + eventExtraInfo: + name: eventExtraInfo + freezePlayerStats: + description: |- + Clones current story variables into the `$frozenValues` object, as well as stores all current story variable names in the `$frozenKeys` array + + Some variables, like ones with names starting with `real_` are ignored + + Also sets `$statFreeze` to `true`, which other widgets can use to determine if they should be active or not + + To restore the variables, use `<>` + G: + name: G + GL: + name: GL + L: + name: L + rangeScopedTest: + name: rangeScopedTest + rangeScopedTestInner: + name: rangeScopedTestInner + resetDebugLinks: + decoration: *unused + name: resetDebugLinks + saveCompareTool: + decoration: *unused + name: saveCompareTool + saveCompareUI: + name: saveCompareUI + saveLoadCache: + name: saveLoadCache + scenes: + name: scenes + scenesSearch: + name: scenesSearch + scenesViewer: + name: scenesViewer + scenesViewerCalc: + name: scenesViewerCalc + scenesViewerControls: + name: scenesViewerControls + seenPassage: + decoration: *unused + name: seenPassage + seenPassageChecks: + name: seenPassageChecks + setupReplayScene: + name: setupReplayScene + straponGeneratorOptions: + name: straponGeneratorOptions + testing-skinColorWidget: + name: testing-skinColorWidget + testScopedVar: + name: testScopedVar + testScopedVar2: + name: testScopedVar2 + unfreezePlayerStats: + description: |- + Restores the story variables saved by `<>` and unsets `$statFreeze` + + Any new story variables not present in `$frozenKeys` also get removed + + After restoring the values, it runs `<>` in case the game was updated while values were frozen + updateSceneViewer: + name: updateSceneViewer diff --git a/game/base-debug/sceneViewer.twee b/game/base-debug/scene-viewer.twee similarity index 90% rename from game/base-debug/sceneViewer.twee rename to game/base-debug/scene-viewer.twee index 9744cab6c..892bffa1f 100644 --- a/game/base-debug/sceneViewer.twee +++ b/game/base-debug/scene-viewer.twee @@ -84,7 +84,7 @@ End of '<>' scene. <> <> <> - <> + <> <> <> <> @@ -171,6 +171,27 @@ End of '<>' scene. <> <> <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <
> <> @@ -196,28 +217,28 @@ End of '<>' scene. name:"Avery Park Introduction", startPassage:"Park Lichen", passages: ["Park Lichen","Park Lichen Climb","Park Lichen Help","Park Lichen Explain","Park Lichen Run","Park Lichen Honest","Park Lichen Evasive","Park Lichen Accept","Park Lichen Refuse","Park Lichen Smooch","Park Lichen Still","Park Lichen Move"], - manualVariableChanges: "<>", + manualVariableChanges: "<>", unlocked: $scenePassages.includes("Park Lichen") }, "RobinIntro1":{ name:"Robin Intro 1", startPassage:"Orphanage", passages:[], - manualVariableChanges:"<><><><><><><>", + manualVariableChanges:"<><><><><><><>", unlocked: $scenePassages.includes("Robin Intro") }, "RobinIntro2":{ name:"Robin Intro 2", startPassage:"Shopping Centre", passages:[], - manualVariableChanges:"<><><><><><><>", + manualVariableChanges:"<><><><><><><>", unlocked: $scenePassages.includes("Robin Intro") }, "RobinIntro3":{ name:"Robin Intro 3", startPassage:"Canteen", passages:[], - manualVariableChanges:"<><><><><><><>", + manualVariableChanges:"<><><><><><><>", unlocked: $scenePassages.includes("Robin Intro") }, "ChefOpeningPhotograph":{ @@ -447,6 +468,48 @@ End of '<>' scene. manualVariableChanges:"<><>", unlocked: $scenePassages.includes("Schism") }, + "Pinch":{ + name:"Pinch", + startPassage: "Olive Book 1", + passages:["Olive Book 1", "Olive Book 2", "Olive Book 3", "Olive Book 4", "Olive Book Full", "Olive Book Full 2", "Olive Book Full 3", "Olive Book Full 4", "Olive Book Full 5", "Olive Book Full 6", "Olive Book Full 7", "Olive Book Lew", "Olive Book Lew Molestation", "Olive Book Lew Finish", "Olive Book Full 8", "Olive Book Full 9", "Olive Book Full 10", "Olive Book Full 11", "Olive Book Pinch", "Olive Book Pinch 2", "Olive Book Pinch Gangbang", "Olive Book Pinch Finish", "Olive Book End"], + manualVariableChanges:"<>", + unlocked: $scenePassages.includes("Pinch") + }, + "RaulAndJanet":{ + name:"Raul and Janet", + startPassage: "ScarletBook1", + passages:["ScarletBook1", "ScarletBook2", "ScarletBook3", "ScarletBook4", "ScarletBook5", "ScarletBook6", "ScarletBook7aye", "ScarletBook7nay", "ScarletBook7nay_page2", "ScarletBook8aye", "ScarletBook8nay", "ScarletBook9", "ScarletBook10", "Loveth", "Loveth Finish", "Drama", "Drama Finish"], + manualVariableChanges:"<><><>", + unlocked: $scenePassages.includes("RaulAndJanet") + }, + "NightmareEden":{ + name:"Nightmare Eden", + startPassage:"Nightmare Eden", + passages:["Nightmare Eden","Nightmare Eden Wake","Nightmare Eden 2","Nightmare Eden 3","Nightmare Eden Molestation","Nightmare Eden Molestation Finish","Nightmare Eden End","Nightmare Eden Wake 2","Nightmare Eden Console","Nightmare Eden Insist"], + manualVariableChanges:"<><>", + unlocked: $scenePassages.includes("NightmareEden") + }, + "NightmareWraith":{ + name:"Nightmare Wraith", + startPassage:"Nightmare Wraith", + passages:["Nightmare Wraith","Nightmare Wraith Wake","Nightmare Wraith 2","Nightmare Wraith 3","Nightmare Wraith 4","Nightmare Wraith 5","Nightmare Wraith Rape","Nightmare Wraith Rape Finish","Nightmare Wraith 6","Nightmare Wraith End"], + manualVariableChanges:"<><>", + unlocked: $scenePassages.includes("NightmareWraith") + }, + "NightmareBird":{ + name:"Nightmare Bird", + startPassage:"Nightmare Bird", + passages:["Nightmare Bird","Nightmare Bird Wake","Nightmare Bird 2","Nightmare Bird Rape","Nightmare Bird Rape Finish","Nightmare Bird End"], + manualVariableChanges:"<><>", + unlocked: $scenePassages.includes("NightmareBird") + }, + "NightmareHarper":{ + name:"Nightmare Harper", + startPassage:"Nightmare Harper", + passages:["Nightmare Harper","Nightmare Harper Wake","Nightmare Harper 2","Nightmare Harper 3","Nightmare Harper 4","Nightmare Harper 5","Nightmare Harper Machine","Nightmare Harper Machine Finish","Nightmare Harper 6","Nightmare Harper End"], + manualVariableChanges:"<><>", + unlocked: $scenePassages.includes("startPassage") + }, }>> <
> <
> @@ -623,6 +686,8 @@ End of '<>' scene. <
> <> <> + <> + <> <> diff --git a/game/base-debug/test demon.twee b/game/base-debug/test demon.twee index c4c94adde..a7142784e 100644 --- a/game/base-debug/test demon.twee +++ b/game/base-debug/test demon.twee @@ -99,10 +99,8 @@ You consider your options... <> You pass out from exertion, completely spent, while the <> roars and continues to work away on your limp body. -

<> <> -
<
> <> diff --git a/game/base-debug/test encounters.twee b/game/base-debug/test encounters.twee index 98cad89c5..e98f60184 100644 --- a/game/base-debug/test encounters.twee +++ b/game/base-debug/test encounters.twee @@ -1423,7 +1423,7 @@ A terminal is attached to the tube. <> although fully nude and holding a <> in <> hand.

- <> + <> "This thing is cute and all, but I've already got one", <> says, throwing it away and pulling <> favourite <> out of nowhere. <> @@ -1790,12 +1790,10 @@ Unset V.bellySizeDebug to fix. <
> <> <> -

<> <> <> <> -
<
> <
> <
> diff --git a/game/base-debug/testing-encountersUI.twee b/game/base-debug/testing-encounters-ui.twee similarity index 100% rename from game/base-debug/testing-encountersUI.twee rename to game/base-debug/testing-encounters-ui.twee diff --git a/game/base-debug/testing-skinColor.twee b/game/base-debug/testing-skin-colour.twee similarity index 94% rename from game/base-debug/testing-skinColor.twee rename to game/base-debug/testing-skin-colour.twee index eece36070..c8a38aeed 100644 --- a/game/base-debug/testing-skinColor.twee +++ b/game/base-debug/testing-skin-colour.twee @@ -5,7 +5,7 @@ Skin Range: <> -<> +<> <> <><><> <> | @@ -22,40 +22,40 @@ Skin Range: <> <><><> <> | -<> +<> <> <> <><><> <> | -<> +<> <> <><><> <> | -<> +<> <> <><><> <> | -<> +<> <> <><><> <> | -<> +<> <> <><><> <> | -<> +<> <> <><><> <> | -<> +<> <> <><><> <> | -<> +<> <> <><><> <> | -<> +<> <> <><><> <> @@ -94,7 +94,7 @@ Manual Skin Filter:
<