diff --git a/classes/classes/CoC.as b/classes/classes/CoC.as index aa6d81a750..bd66d4494e 100644 --- a/classes/classes/CoC.as +++ b/classes/classes/CoC.as @@ -396,7 +396,7 @@ the text from being too boring. //model.debug = debug; // TODO: Set on model? //Version NUMBER - ver = "1.0.2_mod_1.3.15"; + ver = "1.0.2_mod_1.3.16"; version = ver + " (Nostalgia Ahoy)"; //Indicates if building for mobile? diff --git a/classes/classes/Scenes/Areas/Forest.as b/classes/classes/Scenes/Areas/Forest.as index cafb262a33..1d9785ee26 100644 --- a/classes/classes/Scenes/Areas/Forest.as +++ b/classes/classes/Scenes/Areas/Forest.as @@ -288,7 +288,7 @@ package classes.Scenes.Areas break; case 1: case 2: - outputText("You find a pill stamped with the letter 'H' discarded on the ground."); + outputText("You find a pill stamped with the letter 'H' discarded on the ground. "); inventory.takeItem(consumables.H_PILL, camp.returnToCampUseOneHour); break; case 3: diff --git a/classes/classes/Scenes/Areas/Forest/Essrayle.as b/classes/classes/Scenes/Areas/Forest/Essrayle.as index 0f937a1dca..f4dd804e04 100644 --- a/classes/classes/Scenes/Areas/Forest/Essrayle.as +++ b/classes/classes/Scenes/Areas/Forest/Essrayle.as @@ -62,10 +62,7 @@ public function essrayleMeetingI():void { } flags[kFLAGS.MET_ESSY]++; //[Yes] [No] - menu(); - addButton(1,"Yes",plantsForMe); - addButton(2,"No",noPlantsForMe); - + doYesNo(plantsForMe, noPlantsForMe); } //>If No diff --git a/classes/classes/Scenes/Areas/Mountain.as b/classes/classes/Scenes/Areas/Mountain.as index b578389fdf..43d6a51164 100644 --- a/classes/classes/Scenes/Areas/Mountain.as +++ b/classes/classes/Scenes/Areas/Mountain.as @@ -29,6 +29,8 @@ package classes.Scenes.Areas { player.exploredMountain++; var chooser:Number = rand(5); + if (chooser == 5 && player.level < 3 && model.time.days < 20) //Disable mimic if requirements not met (Can still be encountered in level 1 run) + chooser = rand(4); //Helia monogamy fucks if (flags[kFLAGS.PC_PROMISED_HEL_MONOGAMY_FUCKS] == 1 && flags[kFLAGS.HEL_RAPED_TODAY] == 0 && rand(10) == 0 && player.gender > 0 && !kGAMECLASS.helScene.followerHel()) { kGAMECLASS.helScene.helSexualAmbush(); @@ -83,7 +85,7 @@ package classes.Scenes.Areas return; } //10% chance of hairdresser encounter if not found yet - if (rand(10) == 0 && player.findStatusEffect(StatusEffects.HairdresserMeeting) < 0) chooser = 4; + if (rand(10) == 0 && player.findStatusEffect(StatusEffects.HairdresserMeeting) < 0) chooser = 5; if ((rand(8) == 0 && flags[kFLAGS.MARAE_QUEST_START] >= 1) && flags[kFLAGS.FACTORY_FOUND] <= 0) { trace("Dungeon start!") kGAMECLASS.dungeons.enterFactory(); @@ -108,10 +110,6 @@ package classes.Scenes.Areas minotaurScene.minoAddictionBadEndEncounter(); return; } - //Disable mimic if requirements not met (Can still be encountered in level 1 run) - if (chooser == 5 && player.level < 3 && model.time.days < 20) { - chooser = rand(4); - } //Generic Goblin/Imp encounter if (chooser == 0) { kGAMECLASS.exploration.genericGobImpEncounters(); @@ -261,13 +259,13 @@ package classes.Scenes.Areas } hellHoundScene.hellhoundEncounter(); } - //Hairdresser + //Mimic if (chooser == 4) { - salon.hairDresser(); + getGame().mimicScene.mimicTentacleStart(2); } - //Mimic + //Hairdresser if (chooser == 5) { - getGame().mimicScene.mimicTentacleStart(2); + salon.hairDresser(); } } diff --git a/classes/classes/Scenes/Camp.as b/classes/classes/Scenes/Camp.as index 8a3abbdde8..43ba022d9c 100644 --- a/classes/classes/Scenes/Camp.as +++ b/classes/classes/Scenes/Camp.as @@ -1865,10 +1865,14 @@ public function badEndGIANTBALLZ():void { outputText("\n\nFortunately, you have some Reducto. You can shrink your balls and get back to your adventures!", false) addButton(1, "Reducto", applyReductoAndEscapeBadEnd); } - else if (player.findStatusEffect(StatusEffects.CampRathazul) >= 0) { + if (player.findStatusEffect(StatusEffects.CampRathazul) >= 0) { outputText("\n\nYou could call for Rathazul to help you.", false) addButton(2, "Rathazul", callRathazulAndEscapeBadEnd); } + if (shouldraFollower.followerShouldra()) { + outputText("\n\nYou could call for Shouldra to shrink your monstrous balls.", false) + addButton(3, "Shouldra", shouldraFollower.shouldraReductosYourBallsUpInsideYa, true); + } else getGame().gameOver(); } private function applyReductoAndEscapeBadEnd():void { @@ -2104,13 +2108,16 @@ public function wakeFromBadEnd():void { outputText("\n\nYou realize the consequences of having oversized balls and you NEED to shrink it right away. Reducto will do."); player.ballSize = (14 + (player.str / 2) + (player.tallness / 4)); } - outputText("\n\nYou get up, still feeling traumatized from the nightmares."); + if (flags[kFLAGS.EASY_MODE_ENABLE_FLAG] > 0 || debug) + outputText("\n\nYou get up, still feeling confused from the nightmares."); + else + outputText("\n\nYou get up, still feeling traumatized from the nightmares."); //Skip time forward model.time.days++; if (flags[kFLAGS.BENOIT_CLOCK_BOUGHT] > 0) model.time.hours = flags[kFLAGS.BENOIT_CLOCK_ALARM]; else model.time.hours = 6; //Set so you're in camp. - kGAMECLASS.inDungeon = false; + inDungeon = false; inRoomedDungeon = false; inRoomedDungeonResume = null; getGame().inCombat = false; @@ -2121,6 +2128,7 @@ public function wakeFromBadEnd():void { //PENALTY! var penaltyMultiplier:int = 1; penaltyMultiplier += flags[kFLAGS.GAME_DIFFICULTY] * 0.5; + if (flags[kFLAGS.EASY_MODE_ENABLE_FLAG] > 0 || debug) penaltyMultiplier = 0; //Deduct XP and gems. player.gems -= int((player.gems / 10) * penaltyMultiplier); player.XP -= int((player.level * 10) * penaltyMultiplier); diff --git a/classes/classes/Scenes/Dungeons/D3/Lethice.as b/classes/classes/Scenes/Dungeons/D3/Lethice.as index adf5257db0..6093fcb3c9 100644 --- a/classes/classes/Scenes/Dungeons/D3/Lethice.as +++ b/classes/classes/Scenes/Dungeons/D3/Lethice.as @@ -942,7 +942,7 @@ package classes.Scenes.Dungeons.D3 private function gropehands():void { - outputText("“Let’s see how you fight while you’re being groped, shall we? A shame Pigby isn’t around to see how I’ve improved his hands,” Lethice murmurs. Cupping her hands into a parody of lecher’s grip, the corruptive Queen squeezes and chants. Immediately, you feel phantasmal hands all over your body, reaching through your armor to fondle your bare [skinFurScalesNoun]. Digits slip into your [butt]. Fingertips brush your [nipples]. Warm palms slide down your quivering belly toward your vulnerable loins."); + outputText("“Let’s see how you fight while you’re being groped, shall we? A shame Pigby isn’t around to see how I’ve improved his hands,” Lethice murmurs. Cupping her hands into a parody of lecher’s grip, the corruptive Queen squeezes and chants. Immediately, you feel phantasmal hands all over your body, reaching through your armor to fondle your bare [skinFurScales]. Digits slip into your [butt]. Fingertips brush your [nipples]. Warm palms slide down your quivering belly toward your vulnerable loins."); outputText("\n\nYou glare daggers at Lethice, but she merely laughs. “A shame I never got to convince him that his hands were so much more effective when used like this.”"); game.dynStats("lus",5); player.createStatusEffect(StatusEffects.PigbysHands,0,0,0,0); diff --git a/classes/classes/Scenes/Dungeons/D3/LethiceScenes.as b/classes/classes/Scenes/Dungeons/D3/LethiceScenes.as index 604ba31bfb..9cb235a31a 100644 --- a/classes/classes/Scenes/Dungeons/D3/LethiceScenes.as +++ b/classes/classes/Scenes/Dungeons/D3/LethiceScenes.as @@ -836,10 +836,10 @@ package classes.Scenes.Dungeons.D3 } else if (method == 1) { outputText("Lethice probably deserves a punishment that would fit her foul deeds indeed. You raise your [weapon] and slice through her neck, causing her head to fall to the floor and demonic blood spills forth. You pick up your prize and show it to the demons to let them know not to mess with you. "); + awardAchievement("Off With Her Head!", kACHIEVEMENTS.GENERAL_OFF_WITH_HER_HEAD, true, true, true); flags[kFLAGS.LETHICE_KILLED] = 2; // 2 indicates Lethice beheaded } outputText("The assembled demons scatter at the sight, fearful they’ll fall next - and rightly so. So long as they remain, there’s always the chance another will take her place."); - if (method == 1) awardAchievement("Off With Her Head!", kACHIEVEMENTS.GENERAL_OFF_WITH_HER_HEAD, true, true, false); postTheChoice(); } @@ -1211,7 +1211,7 @@ package classes.Scenes.Dungeons.D3 outputText("You may have defeated Lethice and completed the main story but the fun isn't over! It's time for you to return to the game and begin a new era of Mareth."); outputText("\n\nYou can now ascend if you like. Search for the book in the ruined cathedral and perform the ritual at your camp."); awardAchievement("Demon Slayer", kACHIEVEMENTS.STORY_FINALBOSS, true, true, false); - if (player.level <= 1) awardAchievement("Ultimate Noob", kACHIEVEMENTS.CHALLENGE_ULTIMATE_NOOB, true, true, false); + if (player.level <= 1) awardAchievement("Ultimate Noob", kACHIEVEMENTS.CHALLENGE_ULTIMATE_NOOB, true, true, false); //Lethice beaten at level 1! inDungeon = false; inRoomedDungeon = false; player.HP = player.maxHP(); diff --git a/classes/classes/Scenes/Monsters/ImpScene.as b/classes/classes/Scenes/Monsters/ImpScene.as index ad2b42856c..b67ac4016b 100644 --- a/classes/classes/Scenes/Monsters/ImpScene.as +++ b/classes/classes/Scenes/Monsters/ImpScene.as @@ -1793,16 +1793,22 @@ package classes.Scenes.Monsters outputText("\n\nThe imp on the other hand looks a little sick to the stomach now, and flops backwards, passing out completely. You look at him for a moment and decide he'll be fine."); } else { - outputText("\n\nYou massage your breasts for the final time, fascinated by the idea of what will become of the imp when he milks you of all your corrupt milk. You feel the fluid flow begin, and the imp keeps on suckling your " + player.breastDescript(0) + ". He nurses passionately at your " + player.nippleDescript(0) + ", slurping down every drop of your milk."); - outputText("\n\nBefore you can even fully begin to enjoy the rest of the milking, it's over. The imp takes one last, long gulp and falls backwards onto the ground. You watch, fascinated as the imp groans loudly in discomfort. His belly gurgles and visibly shifts as if his belly was full of large worms wiggling around. \"Weird.\" The imp begins to desperately claw at his testicles as they shrink so far that they vanish back inside of him. The apparent itching sensation he's experiencing doesn't seem to stop however, as he begins clawing out small patches of fur, until he reveals a new, moist virgin cunt."); - outputText("\n\nThe imp quickly penetrates his new orifice with two clawed fingers, gasping in the foreign ecstasy. As he plays with his new tool, his former cock vanishes inside of his body, just as his testicles did. The imp is crying out in the new found pleasure, and it seems like he's enjoying his new form."); - - outputText("\n\nThe gurgling of his stomach seems to have ceased, and his former muscular torso and abs are revealed again. However his nipples are now drooling an excessive amount of milk. The imp now appears to be a cunt-boy of some sort. You feel yourself grow flush with arousal as the imp experiences his final changes. Mooing loudly, the greater imp's new clit quickly begins to expand, growing larger and fuller the more he fingers his virgin fuck hole."); - - outputText("\n\nIt takes several minutes, but the imp reaches his orgasm. His clit is as large as an average cock (and appears to have stopped growing). He's taken to using one hand to stroke off his clit like a cock, while his other hand fingers his new delicate pussy. He moos loudly as his new fuck hole leaks its girl goo all over the ground and his hand."); - - outputText("\n\nThe imp weakly smiles at you one last time as he passes out, clearly very happy with how the events unfolded. You're very pleased with the event as well. Picking yourself up, you gather your equipment and put your [armor] back on."); - flags[kFLAGS.IMP_LORD_MALEHERM_PROGRESS] = 1; + if (player.cor < 80) { + outputText("\n\nUnfortunately it looks like you won't find out, as the last of your " + player.breastDescript(2) + " runs dry. The imp wobbles and falls over, clearly not used to the added weight. Now that you get a good look at him, you see some subtle changes. He's got a very full belly. It's a shame as your milk is not potent enough."); + outputText("\n\nThe imp on the other hand looks a little sick to the stomach now, and flops backwards, passing out completely. You look at him for a moment and decide he'll be fine."); + } + else { + outputText("\n\nYou massage your breasts for the final time, fascinated by the idea of what will become of the imp when he milks you of all your corrupt milk. You feel the fluid flow begin, and the imp keeps on suckling your " + player.breastDescript(0) + ". He nurses passionately at your " + player.nippleDescript(0) + ", slurping down every drop of your milk."); + outputText("\n\nBefore you can even fully begin to enjoy the rest of the milking, it's over. The imp takes one last, long gulp and falls backwards onto the ground. You watch, fascinated as the imp groans loudly in discomfort. His belly gurgles and visibly shifts as if his belly was full of large worms wiggling around. \"Weird.\" The imp begins to desperately claw at his testicles as they shrink so far that they vanish back inside of him. The apparent itching sensation he's experiencing doesn't seem to stop however, as he begins clawing out small patches of fur, until he reveals a new, moist virgin cunt."); + outputText("\n\nThe imp quickly penetrates his new orifice with two clawed fingers, gasping in the foreign ecstasy. As he plays with his new tool, his former cock vanishes inside of his body, just as his testicles did. The imp is crying out in the new found pleasure, and it seems like he's enjoying his new form."); + + outputText("\n\nThe gurgling of his stomach seems to have ceased, and his former muscular torso and abs are revealed again. However his nipples are now drooling an excessive amount of milk. The imp now appears to be a cunt-boy of some sort. You feel yourself grow flush with arousal as the imp experiences his final changes. Mooing loudly, the greater imp's new clit quickly begins to expand, growing larger and fuller the more he fingers his virgin fuck hole."); + + outputText("\n\nIt takes several minutes, but the imp reaches his orgasm. His clit is as large as an average cock (and appears to have stopped growing). He's taken to using one hand to stroke off his clit like a cock, while his other hand fingers his new delicate pussy. He moos loudly as his new fuck hole leaks its girl goo all over the ground and his hand."); + + outputText("\n\nThe imp weakly smiles at you one last time as he passes out, clearly very happy with how the events unfolded. You're very pleased with the event as well. Picking yourself up, you gather your equipment and put your [armor] back on."); + flags[kFLAGS.IMP_LORD_MALEHERM_PROGRESS] = 1; + } } } else outputText("\n\nAs your milk flow begins to slow, the imp curls up against you contently. You cradle him for a moment, before laying the creature down, where he burps and falls asleep. You chuckle at how cute these creatures are when they're passive."); @@ -1814,9 +1820,9 @@ package classes.Scenes.Monsters outputText("\n\nYou moan softly as the imp continues his work, although you do notice that he's starting to have trouble keeping up with your flow, as a fair amount of your milk has ended up on your chest and the ground, rather than the imp's belly. Giving him a small swat on his bald head, you point to the milk on the ground, which causes him to whimper in apology."); outputText("\n\nNodding your acceptance, he continues his work much more carefully. He's taking his time again instead of just sucking wildly. You reach down curiously, and tug on the imp's " + monster.cockDescriptShort(0) + " but find that it's shrinking. As you hold it, it shrinks more and more. You wonder what will happen to him if he continues to nurse."); //if (player has only 2 rows of breasts) - if (player.bRows() == 2 || player.cor < 80) { - if (player.bRows() > 2 && player.cor < 80) { - outputText("\n\nUnfortunately it looks like you won't find out, as the last of your " + player.breastDescript(2) + " runs dry. The imp wobbles and falls over, clearly not used to the added weight. Now that you get a good look at him, you see some subtle changes. He's got a very full belly. It's a shame as your milk is not potent enough."); + if (player.bRows() == 2) { + if (player.cor < 80) { + outputText("\n\nUnfortunately it looks like you won't find out, as the last of your " + player.breastDescript(1) + " runs dry. The imp wobbles and falls over, clearly not used to the added weight. Now that you get a good look at him, you see some subtle changes. He's got a very full belly. It's a shame as your milk is not potent enough."); outputText("\n\nThe imp on the other hand looks a little sick to the stomach now, and flops backwards, passing out completely. You look at him for a moment and decide he'll be fine."); } else { @@ -1825,16 +1831,22 @@ package classes.Scenes.Monsters } } else { - outputText("\n\nYou begin massaging your lowest row of breasts, fascinated by the idea of what will become of the imp when he milks you of all your corrupt milk. You feel the fluid flow begin, and the imp moves on to your " + player.breastDescript(2) + ". He nurses passionately at your " + player.nippleDescript(2) + ", slurping down every drop of your milk."); - outputText("\n\nBefore you can even fully begin to enjoy the rest of the milking, it's over. The imp takes one last, long gulp and falls backwards onto the ground. You watch, fascinated as the imp groans loudly in discomfort. His belly gurgles and visibly shifts as if his belly was full of large worms wiggling around. \"Weird.\" The imp begins to desperately claw at his testicles as they shrink so far that they vanish back inside of him. The apparent itching sensation he's experiencing doesn't seem to stop however, as he begins clawing out small patches of fur, until he reveals a new, moist virgin cunt."); - outputText("\n\nThe imp quickly penetrates his new orifice with two clawed fingers, gasping in the foreign ecstasy. As he plays with his new tool, his former cock vanishes inside of his body, just as his testicles did. The imp is crying out in the new found pleasure, and it seems like he's enjoying his new form."); - - outputText("\n\nThe gurgling of his stomach seems to have ceased, and his former muscular torso and abs are revealed again. However his nipples are now drooling an excessive amount of milk. The imp now appears to be a cunt-boy of some sort. You feel yourself grow flush with arousal as the imp experiences his final changes. Mooing loudly, the greater imp's new clit quickly begins to expand, growing larger and fuller the more he fingers his virgin fuck hole."); - - outputText("\n\nIt takes several minutes, but the imp reaches his orgasm. His clit is as large as an average cock (and appears to have stopped growing). He's taken to using one hand to stroke off his clit like a cock, while his other hand fingers his new delicate pussy. He moos loudly as his new fuck hole leaks its girl goo all over the ground and his hand."); - - outputText("\n\nThe imp weakly smiles at you one last time as he passes out, clearly very happy with how the events unfolded. You're very pleased with the event as well. Picking yourself up, you gather your equipment and put your [armor] back on."); - flags[kFLAGS.IMP_LORD_MALEHERM_PROGRESS] = 1; + if (player.cor < 80) { + outputText("\n\nUnfortunately it looks like you won't find out, as the last of your " + player.breastDescript(2) + " runs dry. The imp wobbles and falls over, clearly not used to the added weight. Now that you get a good look at him, you see some subtle changes. He's got a very full belly. It's a shame as your milk is not potent enough."); + outputText("\n\nThe imp on the other hand looks a little sick to the stomach now, and flops backwards, passing out completely. You look at him for a moment and decide he'll be fine."); + } + else { + outputText("\n\nYou begin massaging your lowest row of breasts, fascinated by the idea of what will become of the imp when he milks you of all your corrupt milk. You feel the fluid flow begin, and the imp moves on to your " + player.breastDescript(2) + ". He nurses passionately at your " + player.nippleDescript(2) + ", slurping down every drop of your milk."); + outputText("\n\nBefore you can even fully begin to enjoy the rest of the milking, it's over. The imp takes one last, long gulp and falls backwards onto the ground. You watch, fascinated as the imp groans loudly in discomfort. His belly gurgles and visibly shifts as if his belly was full of large worms wiggling around. \"Weird.\" The imp begins to desperately claw at his testicles as they shrink so far that they vanish back inside of him. The apparent itching sensation he's experiencing doesn't seem to stop however, as he begins clawing out small patches of fur, until he reveals a new, moist virgin cunt."); + outputText("\n\nThe imp quickly penetrates his new orifice with two clawed fingers, gasping in the foreign ecstasy. As he plays with his new tool, his former cock vanishes inside of his body, just as his testicles did. The imp is crying out in the new found pleasure, and it seems like he's enjoying his new form."); + + outputText("\n\nThe gurgling of his stomach seems to have ceased, and his former muscular torso and abs are revealed again. However his nipples are now drooling an excessive amount of milk. The imp now appears to be a cunt-boy of some sort. You feel yourself grow flush with arousal as the imp experiences his final changes. Mooing loudly, the greater imp's new clit quickly begins to expand, growing larger and fuller the more he fingers his virgin fuck hole."); + + outputText("\n\nIt takes several minutes, but the imp reaches his orgasm. His clit is as large as an average cock (and appears to have stopped growing). He's taken to using one hand to stroke off his clit like a cock, while his other hand fingers his new delicate pussy. He moos loudly as his new fuck hole leaks its girl goo all over the ground and his hand."); + + outputText("\n\nThe imp weakly smiles at you one last time as he passes out, clearly very happy with how the events unfolded. You're very pleased with the event as well. Picking yourself up, you gather your equipment and put your [armor] back on."); + flags[kFLAGS.IMP_LORD_MALEHERM_PROGRESS] = 1; + } } } dynStats("cor", 1); diff --git a/classes/classes/Scenes/Monsters/Mimic.as b/classes/classes/Scenes/Monsters/Mimic.as index ad8b1b7d18..c5c3e5de3a 100644 --- a/classes/classes/Scenes/Monsters/Mimic.as +++ b/classes/classes/Scenes/Monsters/Mimic.as @@ -58,7 +58,10 @@ package classes.Scenes.Monsters default: this.imageName = "mimic"; } - if (type == 2) this.createCock(80, 16, CockTypesEnum.HUMAN); + if (type == 2) + this.createCock(80, 16, CockTypesEnum.HUMAN); + else + this.initedGenitals = true; this.balls = 0; this.ballSize = 0; if (type == 1) diff --git a/classes/classes/Scenes/Monsters/MimicScene.as b/classes/classes/Scenes/Monsters/MimicScene.as index 81fb231530..dbe3bf38a0 100644 --- a/classes/classes/Scenes/Monsters/MimicScene.as +++ b/classes/classes/Scenes/Monsters/MimicScene.as @@ -56,7 +56,7 @@ package classes.Scenes.Monsters else if (mimicAppearance == 3) outputText("giant dick ", false); else outputText("chest ", false); //d100 roll (0-99) + inte/3 vs 75 - if (rand(100) + Math.floor(player.inte / 1.5) >= 75) { + if (rand(100) + Math.floor(player.inte / 1.5) >= 80) { //another d100 roll + inte/3 vs only 50 this time if (rand(player.inte) + Math.floor(player.inte / 3) >= 50 + player.newGamePlusMod() * 10) { //find a cool item! @@ -107,7 +107,7 @@ package classes.Scenes.Monsters //If you lose to Mimic. public function mimicTentacle2():void { - var tempSize:Number = Math.round((player.breastRows[0].nippleLength + player.cocks[0].cockLength / 2) * 100) / 100; + var tempSize:Number = Math.round((player.averageNippleLength() + player.cocks[0].cockLength / 2) * 100) / 100; var nippleCockDescript:String = player.nippleDescript(0); //If its a box or a rock, it swallows you whole! if (mimicAppearance != 2 && mimicAppearance != 3) outputText("\n\nWith you collapsing from being unable to put up any further fight, you are unable to prevent yourself from being dragged into the thing’s enormous maw. You are pulled deeper and deeper into the monster’s surprisingly vast innards, and the light streaming in through its jaws grows more and more faint, until with a final *snap*, the light is cut off completely. Surrounded by total darkness and gripped by what must be hundreds of tentacles, you let loose an involuntary moan of terror as the finality of your fate sinks home. ", false); @@ -429,14 +429,15 @@ package classes.Scenes.Monsters } public function mimicTentacleEnd():void { - outputText("Much to your surprise, you wake up some time later. You aren’t sure where you are, or how you got there, but you are certainly glad to be alive. You are covered in some kind of slime, and your body seems strangely sensitive. ", true); + clearOutput(); + outputText("Much to your surprise, you wake up some time later. You aren’t sure where you are, or how you got there, but you are certainly glad to be alive. You are covered in some kind of slime, and your body seems strangely sensitive. "); if (player.hasVagina()) { if (mimicAppearance == 2 || mimicAppearance == 3) { if (rand(5) > player.vaginas[0].vaginalWetness) { player.vaginas[0].vaginalWetness++; - outputText("Strangely, even after that ordeal, your vagina seems wetter than usual. ", false); + outputText("Strangely, even after that ordeal, your vagina seems wetter than usual. "); } } else @@ -444,7 +445,7 @@ package classes.Scenes.Monsters if (rand(5) > player.vaginas[0].vaginalLooseness) { player.vaginas[0].vaginalLooseness++; - outputText("Your cunt is painfully stretched from the ordeal, temporarily enlarged. ", false); + outputText("Your cunt is painfully stretched from the ordeal, temporarily enlarged. "); } } } diff --git a/classes/classes/Scenes/Monsters/SuccubusScene.as b/classes/classes/Scenes/Monsters/SuccubusScene.as index 85e2893432..28ca986b56 100644 --- a/classes/classes/Scenes/Monsters/SuccubusScene.as +++ b/classes/classes/Scenes/Monsters/SuccubusScene.as @@ -15,7 +15,7 @@ package classes.Scenes.Monsters if (player.hasCock()) { outputText("She's expressing interest in your [cock] and says, \"Hello dearie. You've got that wonderful cock of yours and I'd like to offer my pussy to you. It'll be very pleasurable. What do you say?\""); menu(); - addButton(0, "Fight", startCombatImmediate, new Succubus()); + addButton(0, "Fight", fightAgainstSuccubus); addButton(1, "Accept", loseToSuccubus, false); addButton(4, "Run", tryToFlee); } @@ -25,6 +25,10 @@ package classes.Scenes.Monsters } } + public function fightAgainstSuccubus():void { + startCombatImmediate(new Succubus()); + } + public function winAgainstSuccubus():void { clearOutput(); outputText("The succubus falls to her knees, too badly " + (monster.HP <= 0 ? "beaten" : "aroused") + "beaten to continue fighting."); diff --git a/classes/classes/Scenes/NPCs/ShouldraFollower.as b/classes/classes/Scenes/NPCs/ShouldraFollower.as index c01da1bdbd..417ac72833 100644 --- a/classes/classes/Scenes/NPCs/ShouldraFollower.as +++ b/classes/classes/Scenes/NPCs/ShouldraFollower.as @@ -1083,11 +1083,16 @@ private function shouldraReductoMenu():void { choices("Balls", balls, "Breasts", breasts, "Clit", clit, "Cock", cock, "Nipples", nipples, "Butt", butt, "", null, "", null, "", null, "Back", shouldraTalkMenu); } //Balls -private function shouldraReductosYourBallsUpInsideYa():void { +public function shouldraReductosYourBallsUpInsideYa(rescue:Boolean = false):void { flags[kFLAGS.SHOULDRA_MAGIC_COOLDOWN] = 72; clearOutput(); spriteSelect(67); - outputText("\"Why would you want to shrivel these delicious cumquats?\" Shouldra's arsenal of puns doesn't affect your request in the slightest. The ghost girl moves into a quick spell, savoring your testicles before finishing."); + if (rescue) { + outputText("\"Gone a bit too far and got those delicious cumquats too big?\" Shouldra's arsenal of puns and teases don't affect your request in the slightest. The ghost girl moves into a quick spell, savoring your testicles before finishing."); + if (player.ballSize > 18 + (player.str / 2) + (player.tallness / 4)) player.ballSize = 16 + (player.str / 2) + (player.tallness / 4); + } + else + outputText("\"Why would you want to shrivel these delicious cumquats?\" Shouldra's arsenal of puns doesn't affect your request in the slightest. The ghost girl moves into a quick spell, savoring your testicles before finishing."); //this.player.ballSize = this.player.ballSize - (2 + this.rand(4)); player.ballSize -= (2 + rand(4)); if (player.ballSize < 1) player.ballSize = 1; diff --git a/classes/showdown/Showdown.as b/classes/showdown/Showdown.as new file mode 100644 index 0000000000..c9c86711d8 --- /dev/null +++ b/classes/showdown/Showdown.as @@ -0,0 +1,1324 @@ +//Copyright (c) 2010, Charles Strahan +// +//All rights reserved. +// +// Original Showdown Copyright (c) 2007, John Fraser +// +//All rights reserved. +// +// Original Markdown copyright (c) 2004, John Gruber +// +//All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are +//met: +// +//* Redistributions of source code must retain the above copyright notice, +//this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +//notice, this list of conditions and the following disclaimer in the +//documentation and/or other materials provided with the distribution. +// +// * Neither the name "Markdown" nor the names of its contributors may +//be used to endorse or promote products derived from this software +//without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as +//is" and any express or implied warranties, including, but not limited +//to, the implied warranties of merchantability and fitness for a +//particular purpose are disclaimed. In no event shall the copyright owner +//or contributors be liable for any direct, indirect, incidental, special, +//exemplary, or consequential damages (including, but not limited to, +// procurement of substitute goods or services; loss of use, data, or +// profits; or business interruption) however caused and on any theory of +//liability, whether in contract, strict liability, or tort (including +// negligence or otherwise) arising in any way out of the use of this +//software, even if advised of the possibility of such damage. + +// +// Showdown.as -- An ActionScript port of showdown.js +// +// Copyright (c) 2010 Charles Strahan. +// +// Original Showdown Copyright (c) 2007 John Fraser. +// +// +// Original Markdown Copyright (c) 2004-2005 John Gruber +// +// +// Redistributable under a BSD-style open source license. +// See license.txt for more information. +// +// Usage: +// +// var text = "Markdown *rocks*."; +// var html = Showdown.makeHtml(text); +// + +package showdown { + + public class Showdown + { + // Global hashes, used by various utility routines + private static var g_urls:Array; + private static var g_titles:Array; + private static var g_html_blocks:Array; + + // Used to track when we're inside an ordered or unordered list + // (see _ProcessListItems() for details): + private static var g_list_level:Number = 0; + + public static function makeHtml(text:String):String + { + // Clear the global hashes. If we don't clear these, you get conflicts + // from other articles when generating a page which contains more than + // one article (e.g. an index page that shows the N most recent + // articles): + g_urls = []; + g_titles = []; + g_html_blocks = []; + + // attacklab: Replace ~ with ~T + // This lets us use tilde as an escape char to avoid md5 hashes + // The choice of character is arbitray; anything that isn't + // magic in Markdown will work. + text = text.replace(/~/g,"~T"); + + // attacklab: Replace $ with ~D + // RegExp interprets $ as a special character + // when it's in a replacement string + text = text.replace(/\$/g,"~D"); + + // Standardize line endings + text = text.replace(/\r\n/g,"\n"); // DOS to Unix + text = text.replace(/\r/g,"\n"); // Mac to Unix + + // Make sure text begins and ends with a couple of newlines: + text = "\n\n" + text + "\n\n"; + + // Convert all tabs to spaces. + text = _Detab(text); + + // Strip any lines consisting only of spaces and tabs. + // This makes subsequent regexen easier to write, because we can + // match consecutive blank lines with /\n+/ instead of something + // contorted like /[ \t]*\n+/ . + text = text.replace(/^[ \t]+$/mg,""); + + // Turn block-level HTML blocks into hash entries + text = _HashHTMLBlocks(text); + + // Strip link definitions, store in hashes. + text = _StripLinkDefinitions(text); + + text = _RunBlockGamut(text); + + text = _UnescapeSpecialChars(text); + + // attacklab: Restore dollar signs + text = text.replace(/~D/g,"$$"); + + // attacklab: Restore tildes + text = text.replace(/~T/g,"~"); + + // unfortunately, because flash is on crack, the "htmlText" window still inserts breaks at "\n" + // characters AS WELL AS

tags. Therefore, we remove all the newline tags after parsing to HTML, and before + // trying to display the contents. + // FLASH: YOU CRAZY + text = text.replace(/\n/g, ""); + + // Finally, add a additional newline after each closing P tag, because flash only + // outputs one newline per

tag, apparently flash again feels the need to be a special snowflake + // text = text.replace(/<\/p>/gi,"

\n"); + // aaaannnnd now it's properly producing two \n's between paragraphs. WTF, Flash? + + + return text; + } + + + private static function _StripLinkDefinitions(text:String):String { + // + // Strips link definitions from text, stores the URLs and titles in + // hash references. + // + + // Link defs are in the form: ^[id]: url "optional title" + + /* + var text = text.replace(/ + ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 + [ \t]* + \n? // maybe *one* newline + [ \t]* + ? // url = $2 + [ \t]* + \n? // maybe one newline + [ \t]* + (?: + (\n*) // any lines skipped = $3 attacklab: lookbehind removed + ["(] + (.+?) // title = $4 + [")] + [ \t]* + )? // title is optional + (?:\n+|$) + /gm, + function(){...}); + */ + text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm, + function (wholeMatch:String,m1:*,m2:*,m3:*,m4:*,...args):String { + m1 = m1.toLowerCase(); + g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive + if (m3) { + // Oops, found blank lines, so it's not a title. + // Put back the parenthetical statement we stole. + return m3+m4; + } else if (m4) { + g_titles[m1] = m4.replace(/"/g,"""); + } + + // Completely remove the definition from the text + return ""; + } + ); + + return text; + } + + + private static function _HashHTMLBlocks(text:String):String { + // attacklab: Double up blank lines to reduce lookaround + text = text.replace(/\n/g,"\n\n"); + + // Hashify HTML blocks: + // We only want to do this for block-level HTML tags, such as headers, + // lists, and tables. That's because we still want to wrap

s around + // "paragraphs" that are wrapped in non-block-level tags, such as anchors, + // phrase emphasis, and spans. The list of tags we're looking for is + // hard-coded: + //noinspection JSUnusedLocalSymbols + var block_tags_a:String = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"; + //noinspection JSUnusedLocalSymbols + var block_tags_b:String = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"; + + // First, look for nested blocks, e.g.: + //

+ //
+ // tags for inner block must be indented. + //
+ //
+ // + // The outermost tags must start at the left margin for this to match, and + // the inner nested divs must be indented. + // We need to do this before the next, more liberal match, because the next + // match will start at the first `
` and stop at the first `
`. + + // attacklab: This regex can be expensive when it fails. + /* + var text = text.replace(/ + ( // save in $1 + ^ // start of line (with /m) + <($block_tags_a) // start tag = $2 + \b // word break + // attacklab: hack around khtml/pcre bug... + [^\r]*?\n // any number of lines, minimally matching + // the matching end tag + [ \t]* // trailing spaces/tabs + (?=\n+) // followed by a newline + ) // attacklab: there are sentinel newlines at end of document + /gm,function(){...}}; + */ + + text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement); + + // + // Now match more liberally, simply from `\n` to `\n` + // + + /* + var text = text.replace(/ + ( // save in $1 + ^ // start of line (with /m) + <($block_tags_b) // start tag = $2 + \b // word break + // attacklab: hack around khtml/pcre bug... + [^\r]*? // any number of lines, minimally matching + .* // the matching end tag + [ \t]* // trailing spaces/tabs + (?=\n+) // followed by a newline + ) // attacklab: there are sentinel newlines at end of document + /gm,function(){...}}; + */ + text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement); + + // Special case just for
. It was easier to make a special case than + // to make the other regex more complicated. + + /* + text = text.replace(/ + ( // save in $1 + \n\n // Starting after a blank line + [ ]{0,3} + (<(hr) // start tag = $2 + \b // word break + ([^<>])*? // + \/?>) // the matching end tag + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement); + + // Special case for standalone HTML comments: + + /* + text = text.replace(/ + ( // save in $1 + \n\n // Starting after a blank line + [ ]{0,3} // attacklab: g_tab_width - 1 + + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement); + + // PHP and ASP-style processor instructions ( and <%...%>) + + /* + text = text.replace(/ + (?: + \n\n // Starting after a blank line + ) + ( // save in $1 + [ ]{0,3} // attacklab: g_tab_width - 1 + (?: + <([?%]) // $2 + [^\r]*? + \2> + ) + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement); + + // attacklab: Undo double lines (see comment at top of this function) + text = text.replace(/\n\n/g,"\n"); + return text; + } + + //noinspection JSUnusedLocalSymbols + private static function hashElement(wholeMatch:String,m1:*,m2:*,m3:*,m4:*,...args):String { + var blockText:String = m1; + + // Undo double lines + blockText = blockText.replace(/\n\n/g,"\n"); + blockText = blockText.replace(/^\n/,""); + + // strip trailing blank lines + blockText = blockText.replace(/\n+$/g,""); + + // Replace the element text with a marker ("~KxK" where x is its key) + blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n"; + + return blockText; + } + + private static function _RunBlockGamut(text:String):String { + // + // These are all the transformations that form block-level + // tags like paragraphs, headers, and list items. + // + text = _DoHeaders(text); + + // Do Horizontal Rules: + var key:String = hashBlock("
"); + text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key); + text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key); + text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key); + + text = _DoTables(text); + text = _DoLists(text); + text = _DoCodeBlocks(text); + text = _DoBlockQuotes(text); + + // We already ran _HashHTMLBlocks() before, in Markdown(), but that + // was to escape raw HTML in the original Markdown source. This time, + // we're escaping the markup we've just created, so that we don't wrap + //

tags around block-level tags. + text = _HashHTMLBlocks(text); + text = _FormParagraphs(text); + + return text; + } + + + private static function _RunSpanGamut(text:String):String { + // + // These are all the transformations that occur *within* block-level + // tags like paragraphs, headers, and list items. + // + + text = _DoCodeSpans(text); + text = _EscapeSpecialCharsWithinTagAttributes(text); + text = _EncodeBackslashEscapes(text); + + // Process anchor and image tags. Images must come first, + // because ![foo][f] looks like an anchor. + text = _DoImages(text); + text = _DoAnchors(text); + + // Make links out of things like `` + // Must come after _DoAnchors(), because you can use < and > + // delimiters in inline links like [this](). + text = _DoAutoLinks(text); + text = _EncodeAmpsAndAngles(text); + text = _DoItalicsAndBold(text); + + // Do hard breaks: + text = text.replace(/ +\n/g,"
\n"); + + return text; + } + + private static function _EscapeSpecialCharsWithinTagAttributes(text:String):String { + // + // Within tags -- meaning between < and > -- encode [\ ` * _] so they + // don't conflict with their use in Markdown for code, italics and strong. + // + + // Build a regex to find HTML tags and comments. See Friedl's + // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. + var regex:RegExp = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; + + text = text.replace(regex, function(wholeMatch:String,...args):String { + var tag:String = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`"); + tag = escapeCharacters(tag,"\\`*_"); + return tag; + }); + + return text; + } + + private static function _DoAnchors(text:String):String { + // + // Turn Markdown link shortcuts into XHTML tags. + // + // + // First, handle reference-style links: [link text] [id] + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[] // or anything else + )* + ) + \] + + [ ]? // one optional space + (?:\n[ ]*)? // one optional newline followed by spaces + + \[ + (.*?) // id = $3 + \] + )()()()() // pad remaining backreferences + /g,_DoAnchors_callback); + */ + text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag); + + // + // Next, inline-style links: [link text](url "optional title") + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[\]] // or anything else + ) + ) + \] + \( // literal paren + [ \t]* + () // no id, so leave $3 empty + ? // href = $4 + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // Title = $7 + \6 // matching quote + [ \t]* // ignore any spaces/tabs between closing quote and ) + )? // title is optional + \) + ) + /g,writeAnchorTag); + */ + text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag); + + // + // Last, handle reference-style shortcuts: [link text] + // These must come last in case you've also got [link test][1] + // or [link test](/foo) + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ([^\[\]]+) // link text = $2; can't contain '[' or ']' + \] + )()()()()() // pad rest of backreferences + /g, writeAnchorTag); + */ + text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); + + return text; + } + + //noinspection JSUnusedLocalSymbols + private static function writeAnchorTag(wholeMatch:String,m1:*,m2:*,m3:*,m4:*,m5:*,m6:*,m7:*,m8:*,m9:*,...args):String { + if (m7 == undefined) m7 = ""; + //noinspection UnnecessaryLocalVariableJS + var whole_match:String = m1; + //noinspection UnnecessaryLocalVariableJS + var link_text:String = m2; + var link_id:String = m3.toLowerCase(); + var url:String = m4; + var title:String = m7; + + if (url == "") { + if (link_id == "") { + // lower-case and turn embedded newlines into spaces + link_id = link_text.toLowerCase().replace(/ ?\n/g," "); + } + url = "#"+link_id; + + if (g_urls[link_id] != undefined) { + url = g_urls[link_id]; + if (g_titles[link_id] != undefined) { + title = g_titles[link_id]; + } + } + else { + if (whole_match.search(/\(\s*\)$/m)>-1) { + // Special case for explicit empty url + url = ""; + } else { + return whole_match; + } + } + } + + url = escapeCharacters(url,"*_"); + var result:String = ""; + + return result; + } + + + private static function _DoImages(text:String):String { + // + // Turn Markdown image shortcuts into tags. + // + + // + // First, handle reference-style labeled images: ![alt text][id] + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + !\[ + (.*?) // alt text = $2 + \] + + [ ]? // one optional space + (?:\n[ ]*)? // one optional newline followed by spaces + + \[ + (.*?) // id = $3 + \] + )()()()() // pad rest of backreferences + /g,writeImageTag); + */ + text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag); + + // + // Next, handle inline images: ![alt text](url "optional title") + // Don't forget: encode * and _ + + /* + text = text.replace(/ + ( // wrap whole match in $1 + !\[ + (.*?) // alt text = $2 + \] + \s? // One optional whitespace character + \( // literal paren + [ \t]* + () // no id, so leave $3 empty + ? // src url = $4 + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // title = $7 + \6 // matching quote + [ \t]* + )? // title is optional + \) + ) + /g,writeImageTag); + */ + text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag); + + return text; + } + + //noinspection JSUnusedLocalSymbols + private static function writeImageTag(wholeMatch:String,m1:*,m2:*,m3:*,m4:*,m5:*,m6:*,m7:*,m8:*,m9:*):String { + //noinspection UnnecessaryLocalVariableJS + var whole_match:String = m1; + var alt_text:String = m2; + var link_id:String = m3.toLowerCase(); + var url:String = m4; + var title:String = m7; + + if (!title) title = ""; + + if (url == "") { + if (link_id == "") { + // lower-case and turn embedded newlines into spaces + link_id = alt_text.toLowerCase().replace(/ ?\n/g," "); + } + url = "#"+link_id; + + if (g_urls[link_id] != undefined) { + url = g_urls[link_id]; + if (g_titles[link_id] != undefined) { + title = g_titles[link_id]; + } + } + else { + return whole_match; + } + } + + alt_text = alt_text.replace(/"/g,"""); + url = escapeCharacters(url,"*_"); + var result:String = "\""" + _RunSpanGamut(m1) + "");}); + + text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, + function(matchFound:String,m1:String,...args):String{return hashBlock("

" + _RunSpanGamut(m1) + "

");}); + + // atx-style headers: + // # Header 1 + // ## Header 2 + // ## Header 2 with closing hashes ## + // ... + // ###### Header 6 + // + + /* + text = text.replace(/ + ^(\#{1,6}) // $1 = string of #'s + [ \t]* + (.+?) // $2 = Header text + [ \t]* + \#* // optional closing #'s (not counted) + \n+ + /gm, function() {...}); + */ + + text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, + function(wholeMatch:String,m1:String,m2:String,...args):String { + var h_level:int = m1.length; + return hashBlock("" + _RunSpanGamut(m2) + ""); + }); + + return text; + } + + private static function _DoLists(text:String):String { + // + // Form HTML ordered (numbered) and unordered (bulleted) lists. + // + + // attacklab: add sentinel to hack around khtml/safari bug: + // http://bugs.webkit.org/show_bug.cgi?id=11231 + text += "~0"; + + // Re-usable pattern to match any entirel ul or ol list: + + /* + var whole_list = / + ( // $1 = whole list + ( // $2 + [ ]{0,3} // attacklab: g_tab_width - 1 + ([*+-]|\d+[.]) // $3 = first list item marker + [ \t]+ + ) + [^\r]+? + ( // $4 + ~0 // sentinel for workaround; should be $ + | + \n{2,} + (?=\S) + (?! // Negative lookahead for another list item marker + [ \t]* + (?:[*+-]|\d+[.])[ \t]+ + ) + ) + )/g + */ + var whole_list:RegExp = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; + + if (g_list_level) { + text = text.replace(whole_list,function(wholeMatch:String,m1:String,m2:String,...args):String { + var list:String = m1; + var list_type:String = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; + + // Turn double returns into triple returns, so that we can make a + // paragraph for the last item in a list, if necessary: + list = list.replace(/\n{2,}/g,"\n\n\n"); + var result:String = _ProcessListItems(list); + + // Trim any trailing whitespace, to put the closing `` + // up on the preceding line, to get it past the current stupid + // HTML block parser. This is a hack to work around the terrible + // hack that is the HTML block parser. + result = result.replace(/\s+$/,""); + result = "<"+list_type+">" + result + "\n"; + return result; + }); + } else { + whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; + text = text.replace(whole_list,function(wholeMatch:String,m1:String,m2:String,m3:String,...args):String { + //noinspection UnnecessaryLocalVariableJS + var runup:String = m1; + var list:String = m2; + + var list_type:String = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; + // Turn double returns into triple returns, so that we can make a + // paragraph for the last item in a list, if necessary: + list = list.replace(/\n{2,}/g,"\n\n\n"); + var result:String = _ProcessListItems(list); + result = runup + "<"+list_type+">\n" + result + "\n"; + return result; + }); + } + + // attacklab: strip sentinel + text = text.replace(/~0/,""); + + return text; + } + + private static function _DoTables(text:String):String { + text = text.replace(/(?:\|(?:[^\|\r\n]+\|)+\n)+/gm, + function(wholeMatch:String,...args):String { + return "" + _DoTableRows(wholeMatch) + "
"; + }); + return text + } + + private static function _DoTableRows(text:String):String { + text = text.replace(/\|((?:[^\|\r\n]+\|)+)\n/gm, + function(wholeMatch:String, g1:String,...args):String { + return "" + _DoTableCells(g1) + ""; + }); + return text; + } + + private static function _DoTableCells(text:String):String { + text = text.replace(/([^\|\r\n]+)\|/gm, + function(wholeMatch:String, g1:String,...args):String { + return "" + _RunSpanGamut(g1) + ""; + }); + return text; + } + + private static function _ProcessListItems(list_str:String):String { + // + // Process the contents of a single ordered or unordered list, splitting it + // into individual list items. + // + // The $g_list_level global keeps track of when we're inside a list. + // Each time we enter a list, we increment it; when we leave a list, + // we decrement. If it's zero, we're not in a list anymore. + // + // We do this because when we're not inside a list, we want to treat + // something like this: + // + // I recommend upgrading to version + // 8. Oops, now this line is treated + // as a sub-list. + // + // As a single paragraph, despite the fact that the second line starts + // with a digit-period-space sequence. + // + // Whereas when we're inside a list (or sub-list), that line will be + // treated as the start of a sub-list. What a kludge, huh? This is + // an aspect of Markdown's syntax that's hard to parse perfectly + // without resorting to mind-reading. Perhaps the solution is to + // change the syntax rules such that sub-lists must start with a + // starting cardinal number; e.g. "1." or "a.". + + g_list_level++; + + // trim trailing blank lines: + list_str = list_str.replace(/\n{2,}$/,"\n"); + + // attacklab: add sentinel to emulate \z + list_str += "~0"; + + /* + list_str = list_str.replace(/ + (\n)? // leading line = $1 + (^[ \t]*) // leading whitespace = $2 + ([*+-]|\d+[.]) [ \t]+ // list marker = $3 + ([^\r]+? // list item text = $4 + (\n{1,2})) + (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) + /gm, function(){...}); + */ + list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, + function(wholeMatch:String,m1:String,m2:String,m3:String,m4:String,...args):String{ + var item:String = m4; + //noinspection UnnecessaryLocalVariableJS + var leading_line:String = m1; + //noinspection JSUnusedLocalSymbols + var leading_space:String = m2; + + if (leading_line || (item.search(/\n{2,}/)>-1)) { + item = _RunBlockGamut(_Outdent(item)); + } + else { + // Recursion for sub-lists: + item = _DoLists(_Outdent(item)); + item = item.replace(/\n$/,""); // chomp(item) + item = _RunSpanGamut(item); + } + + return "
  • " + item + "
  • \n"; + } + ); + + // attacklab: strip sentinel + list_str = list_str.replace(/~0/g,""); + + g_list_level--; + return list_str; + } + + + private static function _DoCodeBlocks(text:String):String { + // + // Process Markdown `
    ` blocks.
    +			//  
    +			
    +			/*
    +			text = text.replace(text,
    +			/(?:\n\n|^)
    +			(								// $1 = the code block -- one or more lines, starting with a space/tab
    +			(?:
    +			(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    +			.*\n+
    +			)+
    +			)
    +			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
    +			/g,function(){...});
    +			*/
    +			
    +			// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    +			text += "~0";
    +			
    +			text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
    +				function(wholeMatch:String,m1:String,m2:String,...args):String {
    +					var codeblock:String = m1;
    +					//noinspection UnnecessaryLocalVariableJS
    +					var nextChar:String = m2;
    +					
    +					codeblock = _EncodeCode( _Outdent(codeblock));
    +					codeblock = _Detab(codeblock);
    +					codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
    +					codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
    +					
    +					// disabling codeblocks, because they're not needed in CoC.
    +					codeblock = "

    " + codeblock + "\n

    "; + // codeblock = "
    " + codeblock + "\n
    "; + + return hashBlock(codeblock) + nextChar; + } + ); + + // attacklab: strip sentinel + text = text.replace(/~0/,""); + + return text; + } + + //noinspection JSUnusedLocalSymbols + private static function hashBlock(text:String,...args):String { + text = text.replace(/(^\n+|\n+$)/g,""); + return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n"; + } + + + private static function _DoCodeSpans(text:String):String { + // + // * Backtick quotes are used for spans. + // + // * You can use multiple backticks as the delimiters if you want to + // include literal backticks in the code span. So, this input: + // + // Just type ``foo `bar` baz`` at the prompt. + // + // Will translate to: + // + //

    Just type foo `bar` baz at the prompt.

    + // + // There's no arbitrary limit to the number of backticks you + // can use as delimters. If you need three consecutive backticks + // in your code, use four for delimiters, etc. + // + // * You can use spaces to get literal backticks at the edges: + // + // ... type `` `bar` `` ... + // + // Turns to: + // + // ... type `bar` ... + // + + /* + text = text.replace(/ + (^|[^\\]) // Character before opening ` can't be a backslash + (`+) // $2 = Opening run of ` + ( // $3 = The code block + [^\r]*? + [^`] // attacklab: work around lack of lookbehind + ) + \2 // Matching closer + (?!`) + /gm, function(){...}); + */ + + text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, + function(wholeMatch:String,m1:*,m2:*,m3:*,m4:*,...args):String { + var c:String = m3; + c = c.replace(/^([ \t]*)/g,""); // leading whitespace + c = c.replace(/[ \t]*$/g,""); // trailing whitespace + c = _EncodeCode(c); + return m1+""+c+""; + }); + + return text; + } + + + private static function _EncodeCode(text:String):String { + // + // Encode/escape certain characters inside Markdown code runs. + // The point is that in code, these characters are literals, + // and lose their special Markdown meanings. + // + // Encode all ampersands; HTML entities are not + // entities within a Markdown code span. + text = text.replace(/&/g,"&"); + + // Do the angle bracket song and dance: + text = text.replace(//g,">"); + + // Now, escape characters that are magic in Markdown: + text = escapeCharacters(text,"*_{}[]\\",false); + + // jj the line above breaks this: + //--- + + //* Item + + // 1. Subitem + + // special char: * + //--- + + return text; + } + + + private static function _DoItalicsAndBold(text:String):String { + + // must go first: + text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, + "$2"); + + text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, + "$2"); + + return text; + } + + + private static function _DoBlockQuotes(text:String):String { + + /* + text = text.replace(/ + ( // Wrap whole match in $1 + ( + ^[ \t]*>[ \t]? // '>' at the start of a line + .+\n // rest of the first line + (.+\n)* // subsequent consecutive lines + \n* // blanks + )+ + ) + /gm, function(){...}); + */ + + text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, + function(wholeMatch:String,m1:*,...args):String { + var bq:String = m1; + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + + bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting + + // attacklab: clean up hack + bq = bq.replace(/~0/g,""); + + bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines + bq = _RunBlockGamut(bq); // recurse + + bq = bq.replace(/(^|\n)/g,"$1 "); + // These leading spaces screw with
     content, so we need to fix that:
    +					bq = bq.replace(
    +						/(\s*
    [^\r]+?<\/pre>)/gm,
    +						function(wholeMatch:String,m1:String,...args):String {
    +							var pre:String = m1;
    +							// attacklab: hack around Konqueror 3.5.4 bug:
    +							pre = pre.replace(/^  /mg,"~0");
    +							pre = pre.replace(/~0/g,"");
    +							return pre;
    +						});
    +					
    +					return hashBlock("
    \n" + bq + "\n
    "); + }); + return text; + } + + + private static function _FormParagraphs(text:String):String { + // + // Params: + // $text - string to process with html

    tags + // + + // Strip leading and trailing lines: + text = text.replace(/^\n+/g,""); + text = text.replace(/\n+$/g,""); + + var grafs:Array = text.split(/\n{2,}/g); + var grafsOut:Array = []; + + // + // Wrap

    tags. + // + var i:Number; + var end:int = grafs.length; + for (i=0; i= 0) { + grafsOut.push(str); + } + else if (str.search(/\S/) >= 0) { + str = _RunSpanGamut(str); + str = str.replace(/^([ \t]*)/g,"

    "); + str += "

    "; + grafsOut.push(str); + } + + } + + // + // Unhashify HTML blocks + // + end = grafsOut.length; + for (i=0; i= 0) { + var firstGroup:String = (/~K(\d+)K/).exec(grafsOut[i])[1]; + var blockText:String = g_html_blocks[firstGroup]; + blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs + grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText); + } + } + + return grafsOut.join("\n\n"); + } + + + private static function _EncodeAmpsAndAngles(text:String):String { + // Smart processing for ampersands and angle brackets that need to be encoded. + + // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + // http://bumppo.net/projects/amputator/ + text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"); + + // Encode naked <'s + text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); + + return text; + } + + + private static function _EncodeBackslashEscapes(text:String):String { + // + // Parameter: String. + // Returns: The string, with after processing the following backslash + // escape sequences. + // + + // attacklab: The polite way to do this is with the new + // escapeCharacters() function: + // + // text = escapeCharacters(text,"\\",true); + // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); + // + // ...but we're sidestepping its use of the (slow) RegExp constructor + // as an optimization for Firefox. This function gets called a LOT. + + text = text.replace(/\\(\\)/g,escapeCharacters_callback); + text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback); + return text; + } + + + private static function _DoAutoLinks(text:String):String { + + text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"
    $1"); + + // Email addresses: + + /* + text = text.replace(/ + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > + /gi, _DoAutoLinks_callback()); + */ + text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, + function(wholeMatch:String,m1:*,...args):String { + return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); + } + ); + + return text; + } + + + private static function _EncodeEmailAddress(addr:String):String { + // + // Input: an email address, e.g. "foo@example.com" + // + // Output: the email address as a mailto link, with each character + // of the address encoded as either a decimal or hex entity, in + // the hopes of foiling most address harvesting spam bots. E.g.: + // + // foo + // @example.com + // + // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk + // mailing list: + // + + // attacklab: why can't javascript speak hex? + function char2hex(ch:String):String { + var hexDigits:String = '0123456789ABCDEF'; + var dec:int = ch.charCodeAt(0); + return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15)); + } + + var encode:Array = [ + function(ch:String):String{return "&#"+ch.charCodeAt(0)+";";}, + function(ch:String):String{return "&#x"+char2hex(ch)+";";}, + function(ch:String):String{return ch;} + ]; + + addr = "mailto:" + addr; + + addr = addr.replace(/./g, function(ch:String,...args):String { + if (ch == "@") { + // this *must* be encoded. I insist. + ch = encode[Math.floor(Math.random()*2)](ch); + } else if (ch !=":") { + // leave ':' alone (to spot mailto: later) + var r:Number = Math.random(); + // roughly 10% raw, 45% hex, 45% dec + ch = ( + r > .9 ? encode[2](ch) : + r > .45 ? encode[1](ch) : + encode[0](ch) + ); + } + return ch; + }); + + addr = "" + addr + ""; + addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part + + return addr; + } + + + private static function _UnescapeSpecialChars(text:String):String { + // + // Swap back in all the special characters we've hidden. + // + text = text.replace(/~E(\d+)E/g, + function(wholeMatch:String,m1:String,...args):String { + var charCodeToReplace:int = parseInt(m1); + return String.fromCharCode(charCodeToReplace); + } + ); + return text; + } + + + private static function _Outdent(text:String):String { + // + // Remove one level of line-leading tabs or spaces + // + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + + text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width + + // attacklab: clean up hack + text = text.replace(/~0/g,""); + + return text; + } + + private static function _Detab(text:String):String { + // attacklab: Detab's completely rewritten for speed. + // In perl we could fix it by anchoring the regexp with \G. + // In javascript we're less fortunate. + + // expand first n-1 tabs + text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width + + // replace the nth with two sentinels + text = text.replace(/\t/g,"~A~B"); + + // use the sentinel to anchor our regex so it doesn't explode + text = text.replace(/~B(.+?)~A/g, + function(wholeMatch:String,m1:String,m2:*,...args):String { + var leadingText:String = m1; + var numSpaces:int = 4 - leadingText.length % 4; // attacklab: g_tab_width + + // there *must* be a better way to do this: + for (var i:int=0; iLucida Sans Typewriter\n"); } else - outputText("Font: Georgia\n"); + outputText("Font: Palatino Linotype\n"); outputText("\n\n"); diff --git a/lib/bin/MainView.swc b/lib/bin/MainView.swc index 818fcad383..bf586e4afe 100644 Binary files a/lib/bin/MainView.swc and b/lib/bin/MainView.swc differ diff --git a/lib/fla/MainView.fla b/lib/fla/MainView.fla index ece226c503..a2524d0d36 100644 Binary files a/lib/fla/MainView.fla and b/lib/fla/MainView.fla differ