JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
<<print sX.s('title');>>
<<script>> if(!window.SwRxHL) {window.SwRxHL={};} SwRxHL.DB={ 'buildLv':'public', }; window.db=SwRxHL.DB; SwRxHL.DB.ABILITIES={ "Crushing Blows" : {"name":"Crushing Blows","base":"STR","prereq":{ STR:5, }}, "Favor" : {"name":"Favor","base":"LCK","prereq":{ LCK:5, }}, "Feline Grace" : {"name":"Feline Grace","base":"AGI","prereq":{ AGI:5, }}, "Perfect Health" : {"name":"Perfect Health","base":"VIT","prereq":{ VIT:5, }}, "Zeal" : {"name":"Zeal","base":"WIL","prereq":{ WIL:5, }}, }; SwRxHL.DB.ACTIONS={ "Craft" : {"name":"Craft","type":"map"}, "Drink" : {"name":"Drink","type":"map"}, "Eat" : {"name":"Eat","type":"map"}, "Explore" : {"name":"Explore","type":"map","do":function(P={}) { let PC=sX.ent('PC'); sX.goto(PC.loc); },"can":true}, "Practice" : {"name":"Practice","type":"map"}, "Read" : {"name":"Read","type":"map"}, "Rest" : {"name":"Rest","type":"map"}, "Return" : {"name":"Return","type":"map"}, "Sleep" : {"name":"Sleep","type":"map"}, "Train" : {"name":"Train","type":"map"}, "Wank" : {"name":"Wank","type":"map","do":function() { return `while nobody\u2019s looking, you have at it...`; },"can":function() { return db.TESTS.isPrivate.f(); },"cant":function() { return `ye can\u2019t do that here...`; }}, "Cast" : {"name":"Cast","type":"vs"}, "Flash" : {"name":"Flash","type":"vs"}, "Flirt" : {"name":"Flirt","type":"vs"}, "Kick" : {"name":"Kick","type":"vs"}, "Punch" : {"name":"Punch","type":"vs","do":function() { return `you try to punch it...`; },"can":true}, "Stab" : {"name":"Stab","type":"vs"}, "Swing" : {"name":"Swing","type":"vs"}, "Talk" : {"name":"Talk","type":"vs"}, "Taunt" : {"name":"Taunt","type":"vs"}, "Throw" : {"name":"Throw","type":"vs"}, }; SwRxHL.DB.BUILDS={ "Attitude" : {"name":"Attitude","type":"basic","desc":"those built for Attitude are Short & Chubby... (WIL+1; ht-6%; fit:-40%; bust:+80%; gen:-30%)","base":"WIL","bfpm":"15","attr":{ sc:0.94, fit:0.60, brSc:1.80, genL:0.60, genG:0.80, }}, "Endurance" : {"name":"Endurance","type":"basic","desc":"those built for Endurance are Fit & Healthy... (VIT+1; fit:+40%; bust:-40% gen:+35%)","base":"VIT","bfpm":"0","attr":{ sc:1.00, fit:1.40, brSc:0.60, genL:1.20, genG:1.50, }}, "Power" : {"name":"Power","type":"basic","desc":"those built for Power are Big & Strong... (STR+1; ht+6%; fit:-20%; bust:+35%; gen:+25%)","base":"STR","bfpm":"10","attr":{ sc:1.06, fit:0.80, brSc:1.35, genL:1.10, genG:1.40, }}, "Speed" : {"name":"Speed","type":"basic","desc":"those built for Speed are Tall & Lanky... (AGI+1; ht+16%; fit:+20%; bust:-30%; gen:+15%)","base":"AGI","bfpm":"-10","attr":{ sc:1.16, fit:1.20, brSc:0.70, genL:1.20, genG:1.10, }}, "Survival" : {"name":"Survival","type":"basic","desc":"those built for Survival are Short & Small, making it easier to evade predators... (LCK+1; ht-16%; fit:-10%; bust:+10%; gen:-15%)","base":"LCK","bfpm":"-5","attr":{ sc:0.84, fit:0.90, brSc:1.10, genL:0.90, genG:0.80, }}, }; SwRxHL.DB.CLASSES={ "Monk" : {"name":"Monk","type":"basic","desc":"a Monk pushes their body to the limits, remaining ever devout and disciplined... (VIT+3; fit:+60%; bust:-70%; gen:+40%)","base":"VIT","races":"*.*","attr":{ sc:1.00, fit:1.60, brSc:0.30, genL:1.20, genG:1.60, }}, "Mystic" : {"name":"Mystic","type":"basic","desc":"a Mystic attunes to the universe around them, becoming one with all... (LCK+3; ht-16%; fit:-40%; bust:+20%; gen:-20%)","base":"LCK","races":"*.*","attr":{ sc:0.84, fit:0.60, brSc:1.20, genL:0.90, genG:0.70, }}, "Rogue" : {"name":"Rogue","type":"basic","desc":"a Rogue uses their speed and dexterity to outmaneuver their pursuers... (AGI+3; ht+16%; fit:+20%; bust:-30%; gen:+20%)","base":"AGI","races":"*.*","attr":{ sc:1.16, fit:1.20, brSc:0.70, genL:1.30, genG:1.10, }}, "Scholar" : {"name":"Scholar","type":"basic","desc":"a Scholar seeks to understand and manipulate the world around them... (WIL+3; ht-6%; fit:-30%; bust:-15%; gen:-15%)","base":"WIL","races":"*.*","attr":{ sc:0.94, fit:0.70, brSc:0.85, genL:0.80, genG:0.90, }}, "Warrior" : {"name":"Warrior","type":"basic","desc":"a Warrior\u2019s focus is on physical Strength and crushing their foes... (STR+3; ht+6%; fit:+30; bust:+30; gen:+15%)","base":"STR","races":"*.*","attr":{ sc:1.06, fit:1.30, brSc:1.30, genL:1.10, genG:1.20, }}, }; SwRxHL.DB.ENC={ "*.*" : {"name":"*.*","enc":function(P={}){ let encName='ENCOUNTER', PC=sX.ent('PC'); switch(P.encRoll) { case '': encName=''; break; } PC.enc=null; /* encounter over... */ return `<p>[${encName}: \"${P.this?.name??'?name'}\"\u00bb\"${P.encRoll??'-'}\"]</p>`; }}, "Oakford.Town.*" : {"name":"Oakford.Town.*","enc":function(P={}){ let encName='ENCOUNTER', PC=sX.ent('PC'); switch(P.encRoll) { case '': encName=''; break; } PC.enc=null; /* encounter over... */ return `<p>[${encName}: \"${P.this?.name??'?name'}\"\u00bb\"${P.encRoll??'-'}\"]</p>`; }}, "Oakford.Woods.*" : {"name":"Oakford.Woods.*","enc":function(P={}){ let encName='ENCOUNTER', PC=sX.ent('PC'); switch(P.encRoll) { case '': encName=''; break; case 'd4h' : encName='thieving raven'; break; case 'd4n' : encName='pesky squirrel'; break; case 'd4f' : encName='brownie adventurer'; break; case 'd4e' : encName='?weather shifts|strange footprints'; break; case 'd4r' : encName='?minor herb (numbing agent?)'; break; case 'd6h' : encName='lone goblin'; break; case 'd6n' : encName='curious badger'; break; case 'd6f' : encName='shy gnome'; break; case 'd6e' : encName='?missing trinkets'; break; case 'd6r' : encName='?firewood|berries'; break; case 'd8h' : encName='vengeful kobold'; break; case 'd8n' : encName='starving hound'; break; case 'd8f' : encName='young buck'; break; case 'd8e' : encName='?strange writings'; break; case 'd8r' : encName='?antivenom herb'; break; case 'd10h': encName='bold bandit'; break; case 'd10n': encName='young adventurer'; break; case 'd10f': encName='local/traveler'; break; case 'd10e': encName='?distant noises'; break; case 'd10r': encName='?fruit|water'; break; case 'd12h': encName='murderous orc'; break; case 'd12n': encName='lost bear'; break; case 'd12f': encName='grizzled hero'; break; case 'd12e': encName='?fire|storm|screams'; break; case 'd12r': encName='?rare herb|special item'; break; default: encName='(nothing)'; break; } PC.enc=null; /* encounter over... */ return `<p>[${encName}: \"${P.this?.name??'?name'}\"\u00bb\"${P.encRoll??'-'}\"]</p>`; }}, "Oakford.Woods.d10h" : {"name":"Bold Bandit","enc":function(P={}) { let PC=sX.ent('PC'), BRANCHES={ talk: `<p>You talk... the bandit gets bored and wanders off...</p>`, fight: `<p>You fight... and nothing happens.</p>`, flee: `<p>You flee... you\u2019re a bit winded, but you manage to escape...</p>`, '!': ` <p>\u201cWell, well, well... what do we have here?\u201d, the bandit asks as (s)he begins circling.</p> <p><a onClick=\"sX.encBranch('talk');\">Talk</a></p> <p><a onClick=\"sX.encBranch('fight');\">Fight</a></p> <p><a onClick=\"sX.encBranch('flee');\">Flee</a></p> <p> . . . </p> `, }, branch=PC.dec; /* retain branch-name past encOver() */ switch(branch) { case 'talk': sX.encOver(); return BRANCHES[branch]; break; case 'fight': return BRANCHES[branch]+BRANCHES['!']; break; case 'flee': sX.encOver(); return BRANCHES[branch]; break; default: return BRANCHES['!']; break; } }}, }; SwRxHL.DB.FUNC={ "test" : {"name":"test","func":function() { return 'this was a test of the CMS function system, this was only a test, had this been an actual function it would be followed by a useful return value.'; }}, }; SwRxHL.DB.ITEMS={ }; SwRxHL.DB.NPCS={ "oakT.Audræ" : {"name":"Audr\u00e6","type":"artisan","ent":function(P={}) { P.days=Math.max(0,P.days??10); P.dc??=3; P.ds??=12; P.sc??=1.0; P.ht??=2.34; P.fit??=0.69; let wk=(P.days/13), se=(P.days/104), yr=(P.days/1347), clv=null, E={ ATTR:{ d: P.ds+(sX.unstack(yr,true)*2), sc: P.sc*(0.69+0.06*yr), ht: P.ht+(0.16*yr), fit: P.fit+(0.34*yr), }, Build: 'Power', CLASS:{ Monk: { xp: P.days*100, lv: Math.round(30+wk), spec: 'Wrestler', }, Mystic: { xp: P.days* 10, lv: Math.round(10+se), spec: 'Bodybuilder', }, Rogue: { xp: P.days* 10, lv: Math.round(10+se), spec: 'Wildling', }, Scholar: { xp: P.days* 10, lv: Math.round(10+se), spec: 'Dominatrix', }, Warrior: { xp: P.days*100, lv: Math.round(30+wk), spec: 'Gladiator', }, }, Class: 'Warrior', EQUIP:{}, INV:{}, Name: 'Audr\u00e6', PARTY:{}, RES:{}, Race: 'Human', /* ? */ STATS:{}, Sex: ('Alpha Herm'??'Alpha Female'), }; ['STR','AGI','VIT','WIL','LCK'].forEach(function(k) { P.ds=E.ATTR.d; if(['STR','VIT'].includes(k)) { P.dc=Math.round(9+se); clv=Math.round(30+wk); P.ds+=2; } else { P.dc=Math.round(5+yr); clv=Math.round(10+se); } E.STATS[k]={ dc:P.dc, wn:0, rk:(P.ds+P.dc), st:Math.round(P.dc*P.ds*(0.34+yr)), }; E.STATS[k].mxe=Math.round(E.STATS[k].st*E.STATS[k].rk/10+clv); E.STATS[k].en=Math.round(E.STATS[k].mxe*0.69); }); return E; },"art":function(P={}) { let IMGS={ 'Audr\u00e6 - R00 - blacksmith.webp' : 999, 'Audr\u00e6 - R00 - warrior.webp' : 333, 'Audr\u00e6 - R00 - athlete.webp' : 111, 'Audr\u00e6 - R00 - hot forge.webp' : 333, 'Audr\u00e6 - R00 - comfortable.webp' : 99, 'Audr\u00e6 - R00 - naked.webp' : 66, 'Audr\u00e6 - R00 - secret.webp' : 11, 'Audr\u00e6 - R00 - big secret.webp' : 11, 'Audr\u00e6 - R00 - stallion.webp' : 33, 'Audr\u00e6 - R00 - siegebreaker.webp' : 11, 'Audr\u00e6 - R00 - heavy artillery.webp' : 1, }; return sX.choose(IMGS); }}, }; SwRxHL.DB.PLACES={ }; SwRxHL.DB.PROFS={ "trades.Abjuration (anti-)" : {"name":"Abjuration (anti-)","type":"trades"}, "trades.Accounting" : {"name":"Accounting","type":"trades"}, "trades.Acoustics" : {"name":"Acoustics","type":"trades"}, "trades.Acting" : {"name":"Acting","type":"trades"}, "trades.Adventuring" : {"name":"Adventuring","type":"trades"}, "trades.Alchemy" : {"name":"Alchemy","type":"trades"}, "trades.Anatomy" : {"name":"Anatomy","type":"trades"}, "trades.Animal Husbandry" : {"name":"Animal Husbandry","type":"trades"}, "trades.Apothecary" : {"name":"Apothecary","type":"trades"}, "trades.Arcanery" : {"name":"Arcanery","type":"trades"}, "trades.Archeology" : {"name":"Archeology","type":"trades"}, "trades.Artistry" : {"name":"Artistry","type":"trades"}, "trades.Astrology" : {"name":"Astrology","type":"trades"}, "trades.Astronomy" : {"name":"Astronomy","type":"trades"}, "trades.Beseigement" : {"name":"Beseigement","type":"trades"}, "trades.Biology" : {"name":"Biology","type":"trades"}, "trades.Botony" : {"name":"Botony","type":"trades"}, "trades.Brewing" : {"name":"Brewing","type":"trades"}, "trades.Butchery" : {"name":"Butchery","type":"trades"}, "trades.Carpentry" : {"name":"Carpentry","type":"trades"}, "trades.Cartography" : {"name":"Cartography","type":"trades"}, "trades.Casting" : {"name":"Casting","type":"trades"}, "trades.Chemistry" : {"name":"Chemistry","type":"trades"}, "trades.Choreography" : {"name":"Choreography","type":"trades"}, "trades.Civility" : {"name":"Civility","type":"trades"}, "trades.Civilization" : {"name":"Civilization","type":"trades"}, "trades.Cleaning" : {"name":"Cleaning","type":"trades"}, "trades.Cobbling" : {"name":"Cobbling","type":"trades"}, "trades.Codifying" : {"name":"Codifying","type":"trades"}, "trades.Coercion" : {"name":"Coercion","type":"trades"}, "trades.Colonization" : {"name":"Colonization","type":"trades"}, "trades.Companionship" : {"name":"Companionship","type":"trades"}, "trades.Conjuration" : {"name":"Conjuration","type":"trades"}, "trades.Cooking" : {"name":"Cooking","type":"trades"}, "trades.Corruption" : {"name":"Corruption","type":"trades"}, "trades.Cosmetics" : {"name":"Cosmetics","type":"trades"}, "trades.Crafting" : {"name":"Crafting","type":"trades"}, "trades.Cryptography" : {"name":"Cryptography","type":"trades"}, "trades.Cultivation" : {"name":"Cultivation","type":"trades"}, "trades.Dancing" : {"name":"Dancing","type":"trades"}, "trades.Deception" : {"name":"Deception","type":"trades"}, "trades.Defense" : {"name":"Defense","type":"trades"}, "trades.Demography" : {"name":"Demography","type":"trades"}, "trades.Demolition" : {"name":"Demolition","type":"trades"}, "trades.Diplomacy" : {"name":"Diplomacy","type":"trades"}, "trades.Discipline" : {"name":"Discipline","type":"trades"}, "trades.Disguise" : {"name":"Disguise","type":"trades"}, "trades.Distilation" : {"name":"Distilation","type":"trades"}, "trades.Divination" : {"name":"Divination","type":"trades"}, "trades.Domination" : {"name":"Domination","type":"trades"}, "trades.Duplicity" : {"name":"Duplicity","type":"trades"}, "trades.Engineering" : {"name":"Engineering","type":"trades"}, "trades.Entertaining" : {"name":"Entertaining","type":"trades"}, "trades.Espionage" : {"name":"Espionage","type":"trades"}, "trades.Ethics" : {"name":"Ethics","type":"trades"}, "trades.Etiquette" : {"name":"Etiquette","type":"trades"}, "trades.Evocation" : {"name":"Evocation","type":"trades"}, "trades.Experimentation" : {"name":"Experimentation","type":"trades"}, "trades.Exploring" : {"name":"Exploring","type":"trades"}, "trades.Farming" : {"name":"Farming","type":"trades"}, "trades.Fertility" : {"name":"Fertility","type":"trades"}, "trades.Fighting" : {"name":"Fighting","type":"trades"}, "trades.Fishing" : {"name":"Fishing","type":"trades"}, "trades.Forestry" : {"name":"Forestry","type":"trades"}, "trades.Forgery" : {"name":"Forgery","type":"trades"}, "trades.Gaffery (Glassblowing)" : {"name":"Gaffery (Glassblowing)","type":"trades"}, "trades.Genealogy" : {"name":"Genealogy","type":"trades"}, "trades.Geography" : {"name":"Geography","type":"trades"}, "trades.Geology" : {"name":"Geology","type":"trades"}, "trades.Geometry" : {"name":"Geometry","type":"trades"}, "trades.Herbalism" : {"name":"Herbalism","type":"trades"}, "trades.History" : {"name":"History","type":"trades"}, "trades.Hospitality" : {"name":"Hospitality","type":"trades"}, "trades.Hunting" : {"name":"Hunting","type":"trades"}, "trades.Illustrating" : {"name":"Illustrating","type":"trades"}, "trades.Impersonation" : {"name":"Impersonation","type":"trades"}, "trades.Incarceration" : {"name":"Incarceration","type":"trades"}, "trades.Intimidation" : {"name":"Intimidation","type":"trades"}, "trades.Invocation" : {"name":"Invocation","type":"trades"}, "trades.Irrigating" : {"name":"Irrigating","type":"trades"}, "trades.Jewelcrafting" : {"name":"Jewelcrafting","type":"trades"}, "trades.Juristry" : {"name":"Juristry","type":"trades"}, "trades.Knitting" : {"name":"Knitting","type":"trades"}, "trades.Laboring" : {"name":"Laboring","type":"trades"}, "trades.Leadership" : {"name":"Leadership","type":"trades"}, "trades.Leatherworking" : {"name":"Leatherworking","type":"trades"}, "trades.Linguistics" : {"name":"Linguistics","type":"trades"}, "trades.Logging" : {"name":"Logging","type":"trades"}, "trades.Logic" : {"name":"Logic","type":"trades"}, "trades.Logistics" : {"name":"Logistics","type":"trades"}, "trades.Marketing" : {"name":"Marketing","type":"trades"}, "trades.Marksmanship" : {"name":"Marksmanship","type":"trades"}, "trades.Martial Discipline" : {"name":"Martial Discipline","type":"trades"}, "trades.Masonry" : {"name":"Masonry","type":"trades"}, "trades.Mathematics" : {"name":"Mathematics","type":"trades"}, "trades.Mechanics" : {"name":"Mechanics","type":"trades"}, "trades.Metallurgy" : {"name":"Metallurgy","type":"trades"}, "trades.Metaphysics" : {"name":"Metaphysics","type":"trades"}, "trades.Mining" : {"name":"Mining","type":"trades"}, "trades.Mixology" : {"name":"Mixology","type":"trades"}, "trades.Morality" : {"name":"Morality","type":"trades"}, "trades.Mountaineering" : {"name":"Mountaineering","type":"trades"}, "trades.Navigating" : {"name":"Navigating","type":"trades"}, "trades.Notary" : {"name":"Notary","type":"trades"}, "trades.Novelism" : {"name":"Novelism","type":"trades"}, "trades.Numerology" : {"name":"Numerology","type":"trades"}, "trades.Nursing" : {"name":"Nursing","type":"trades"}, "trades.Obfuscation" : {"name":"Obfuscation","type":"trades"}, "trades.Observation" : {"name":"Observation","type":"trades"}, "trades.Optics" : {"name":"Optics","type":"trades"}, "trades.Painting" : {"name":"Painting","type":"trades"}, "trades.Pathfinding" : {"name":"Pathfinding","type":"trades"}, "trades.Pathology" : {"name":"Pathology","type":"trades"}, "trades.Philosophy" : {"name":"Philosophy","type":"trades"}, "trades.Physics" : {"name":"Physics","type":"trades"}, "trades.Physiology" : {"name":"Physiology","type":"trades"}, "trades.Potting" : {"name":"Potting","type":"trades"}, "trades.Pyrotechnics" : {"name":"Pyrotechnics","type":"trades"}, "trades.Riding" : {"name":"Riding","type":"trades"}, "trades.Ropework" : {"name":"Ropework","type":"trades"}, "trades.Sabotage" : {"name":"Sabotage","type":"trades"}, "trades.Scribing" : {"name":"Scribing","type":"trades"}, "trades.Scrying" : {"name":"Scrying","type":"trades"}, "trades.Sculpting" : {"name":"Sculpting","type":"trades"}, "trades.Seafaring" : {"name":"Seafaring","type":"trades"}, "trades.Security" : {"name":"Security","type":"trades"}, "trades.Sensuality" : {"name":"Sensuality","type":"trades"}, "trades.Sewing" : {"name":"Sewing","type":"trades"}, "trades.Shipwright" : {"name":"Shipwright","type":"trades"}, "trades.Singing" : {"name":"Singing","type":"trades"}, "trades.Skinning" : {"name":"Skinning","type":"trades"}, "trades.Smithing" : {"name":"Smithing","type":"trades"}, "trades.Spellcraft" : {"name":"Spellcraft","type":"trades"}, "trades.Spirituality" : {"name":"Spirituality","type":"trades"}, "trades.Staining" : {"name":"Staining","type":"trades"}, "trades.Storytelling" : {"name":"Storytelling","type":"trades"}, "trades.Subversion" : {"name":"Subversion","type":"trades"}, "trades.Surgery" : {"name":"Surgery","type":"trades"}, "trades.Survival" : {"name":"Survival","type":"trades"}, "trades.Tailoring" : {"name":"Tailoring","type":"trades"}, "trades.Taxidermy" : {"name":"Taxidermy","type":"trades"}, "trades.Theatrics" : {"name":"Theatrics","type":"trades"}, "trades.Theology" : {"name":"Theology","type":"trades"}, "trades.Tinkering" : {"name":"Tinkering","type":"trades"}, "trades.Trading" : {"name":"Trading","type":"trades"}, "trades.Transmutation" : {"name":"Transmutation","type":"trades"}, "trades.Trapping" : {"name":"Trapping","type":"trades"}, "trades.Tutorage" : {"name":"Tutorage","type":"trades"}, "trades.Weaving" : {"name":"Weaving","type":"trades"}, "trades.Zoology" : {"name":"Zoology","type":"trades"}, }; SwRxHL.DB.RACES={ "Dwarf" : {"name":"Dwarf","type":"basic","desc":"Stout and hardy folk, Dwarves can survive injuries most other races can\u2019t- a good thing too, considering the perils of the deep mountain caverns which they make their abode... (VIT+3; ht: ~4\u2019/120\u00b130cm; fit:-40%; bust:+40%; gen:+30%)","ds":"10","classes":"*","sexes":{m:49,f:50,h:1,},"base":"VIT","attr":{ ht:1.2, fit:0.60, brSc:1.40, genL:0.6, genG:2.0, }}, "Elf" : {"name":"Elf","type":"basic","desc":"Tall and graceful, the Elves have an almost majestic quality to them. Able to outmaneuver other races, they are fearsome adversaries on the battlefield, though they\u2019d prefer a life of peace and tranquility... (AGI+3; ht: ~7\u2019/210\u00b130cm; fit:+10%; bust:-50%; gen:+20%)","ds":"10","classes":"*","sexes":{m:49,f:50,h:1,},"base":"AGI","attr":{ ht:2.1, fit:1.10, brSc:0.50, genL:1.3, genG:1.10, }}, "Human" : {"name":"Human","type":"basic","desc":"Perhaps the most Versatile of races, humans stand at a fairly average height, and solve most problems through hard-won lessons... (WIL+3; ht: ~5\u2019/150\u00b130cm; fit:-10%; bust:-10%; gen:-10%)","ds":"10","classes":"*","sexes":{m:49,f:50,h:1,},"base":"WIL","attr":{ ht:1.5, fit:0.90, brSc:0.90, genL:0.95, genG:0.85, }}, "Oni" : {"name":"Oni","type":"basic","desc":"Tiny tricksters, most Oni only grow to be about one meter, though there is rumor of some towering over even the Elves... (LCK+3; ht: ~3\u2019/90\u00b130cm +0.3cm/lv; fit:-20%; bust:+50%; gen:+75%)","ds":"10","classes":"*","sexes":{m:49,f:50,h:1,},"base":"LCK","attr":{ ht:0.9, fit:0.80, brSc:1.50, genL:1.50, genG:2.00, }}, "Orc" : {"name":"Orc","type":"basic","desc":"Large & Powerful, Orcs use their brute Strength to crush those who stand in their way. Taller than most races, they are as intimidating as they are deadly... (STR+3; ht: ~6\u2019/180\u00b130cm; fit:+20%; bust:+20%; gen:+37%)","ds":"10","classes":"*","sexes":{m:49,f:50,h:1,},"base":"STR","attr":{ ht:1.8, fit:1.20, brSc:1.20, genL:1.25, genG:1.50, }}, }; SwRxHL.DB.SCENES={ "explore" : {"name":"explore","type":"action","scene":function(P={}) { return `<p>[do stuff here]</p>`; }}, "combat" : {"name":"combat","type":"encounter","scene":function(P={}) { return `<p>[do stuff here]</p>`; }}, "encounter" : {"name":"encounter","type":"encounter","scene":function(P={}) { /* d4~d12 random encounter table... */ /* hostile|neutral|friendly|resource|event -type roll */ /* load encounter table for current zone... */ /* sample: {'oakwood.d4h': 'thieving raven','oakwood.d12e': 'fire|storm|screams',} */ return `<p>[do stuff here]</p>`; }}, "npc" : {"name":"npc","type":"encounter","scene":function(P={}) { return `<p>[do stuff here]</p>`; }}, "Oakford.Town" : {"name":"Oakford","type":"locale","class":"town","scene":function(P={}) { /* do oakford-specific setup shit here... */ sX.setLocalShops({ 'Blacksmith' :{}, 'General Store' :{}, 'Inn' :{}, 'Leatherworker' :{}, 'Tavern' :{}, /* 'Apothecary' :{}, 'Arcanery' :{}, 'Carpenter' :{}, 'Constable' :{}, 'Curio' :{}, 'Healer' :{}, 'Lender' :{}, 'Livery' :{}, 'Mason' :{}, 'Open Market' :{}, 'Scribe' :{}, 'Tailor' :{}, 'Wellhouse' :{}, */ }); return sX.s('inTown');; }}, "Oakford.Woods" : {"name":"Oakford Woods","type":"locale","class":"wilds","scene":function(P={}) { return ` <h2>in the woods...</h2> <p>[do stuff here]</p> <p>${sX.act_tryLink('Explore')}</p> <p><a onClick=\"sX.goto('Oakford.Town', {});\">return to town</a></p> `; }}, "atShop" : {"name":"atShop","type":"screen","scene":function(P={}) { let PC=sX.ent('PC'), ctx=db.SCENES[PC.shop], shopScene=sX.get('scene',{ctx:ctx, this:ctx}); return ` <p>at the ${PC.shop}...</p> <p>${shopScene}</p> <p><a onClick=\"sX.ent('PC').shop=null; sX.goto('inTown', {});\">leave shop</a></p> `; }}, "bonusContent" : {"name":"bonusContent","type":"screen","scene":function(P={}) { return ` <h2>bonus content...</h2> <p><a onClick=\"sX.goto('title', {});\">exit</a></p> `; }}, "cgAbilities" : {"name":"Select Abilities","type":"screen","scene":function(P={}) { let selAbil=sX.abilSelUI(); return ` <h2>Select Abilities:</h2> <p>${selAbil}</p> <p><a onClick=\"sX.goto('cgRoll', {});\">Roll Attributes</a></p> <p><a onClick=\"sX.goto('cgCore', {});\">back to Core</a></p> <div id=\"statPreview\"><<print sX.cgPreview();>></div> `; }}, "cgCore" : {"name":"Character Generation Core","type":"screen","scene":function(P={}) { let FIELDS={ 'name' : sX.cgTxt('Name'), 'race' : sX.cgSel('Race', db.RACES), 'class' : sX.cgSel('Class', db.CLASSES), 'sex' : sX.cgSel('Sex', db.SEXES), 'build' : sX.cgSel('Build', db.BUILDS), }; return ` <h2>select Core Character Traits:</h2> <p>${FIELDS.name}</p> <p>${FIELDS.race}</p> <p>${FIELDS.class}</p> <p>${FIELDS.sex}</p> <p>${FIELDS.build}</p> <p><a onClick=\"sX.goto('cgAbilities', {});\">select Abilities</a></p> <p><a onClick=\"sX.goto('title', {});\">quit</a></p> <div id=\"statPreview\"><<print sX.cgPreview();>></div> `; }}, "cgRoll" : {"name":"Roll Attributes","type":"screen","scene":function(P={}) { return ` <h2>Roll Attributes:</h2> <div><<print sX.rollStats(sX.ent('PC'));>></div> <p><a onClick=\"sX.goto('gameIntro', {});\">start game</a></p> <p><a onClick=\"sX.goto('cgAbilities', {});\">back to Abilities</a></p> <div id=\"statPreview\"><<print sX.cgPreview();>></div> `; }}, "exitTown" : {"name":"Leaving Town","type":"screen","scene":function(P={}) { let PC=sX.ent('PC'), exitTo={ 'Oakford.Town':'Oakford.Woods', }[PC.loc]; return ` <p>now leaving the town of ${db.SCENES[PC.loc].name} ...</p> <p><a onClick=\"sX.goto('${exitTo}', {});\">continue...</a></p> `; }}, "gameIntro" : {"name":"gameIntro","type":"screen","scene":function(P={}) { let PC=sX.ent('PC'); PC.CLASS[PC.Class].lv=1; return ` <p>intro scene...<p> <p><a onClick=\"sX.goto('Oakford.Town', {});\">continue</a></p> `; }}, "gameOptions" : {"name":"gameOptions","type":"screen","scene":function(P={}) { return ` <h2>game options...</h2> <h3>Difficulty:</h3> <p>Easy (d12)</p> <p>Normal (d10)</p> <p>Hard (d8)</p> <p><a onClick=\"sX.goto('title', {});\">return</a></p> `; }}, "inTown" : {"name":"inTown","type":"screen","scene":function(P={}) { let PC=sX.ent('PC'), shopList=sX.listShops(); return ` <p>in the town of ${db.SCENES[PC.loc].name} ...</p> <p>${shopList}</p> <p>${sX.act_tryLink('Explore')}</p> <p><a onClick=\"sX.goto('exitTown', {});\">leave town</a></p> `; }}, "title" : {"name":"title","type":"screen","scene":function(P={}) { return `<h1>Alpha (Primal)</h1> <ul> <li><a onClick=\"sX.goto('cgCore', {});\">New Game</a></li> <li><a onClick=\"SugarCube.UI.saves();\">Continue</a></li> <li><a onClick=\"sX.goto('gameOptions', {});\">Options</a></li> <li><a onClick=\"sX.goto('bonusContent', {});\">Extras</a></li> </ul>`; }}, "Apothecary" : {"name":"Apothecary","type":"shop"}, "Arcanery" : {"name":"Arcanery","type":"shop"}, "Blacksmith" : {"name":"Blacksmith","type":"shop","scene":function(P={}) { return ` <p>${sX.npcArt('oakT.Audr\u00e6')}</p> <p>Audr\u00e6 greets you warmly, \u201cWhat troubles you?\u201d</p> `; }}, "Carpenter" : {"name":"Carpenter","type":"shop"}, "Constable" : {"name":"Constable","type":"shop"}, "Curio" : {"name":"Curio","type":"shop"}, "General Store" : {"name":"General Store","type":"shop"}, "Healer" : {"name":"Healer","type":"shop"}, "Inn" : {"name":"Inn","type":"shop"}, "Leatherworker" : {"name":"Leatherworker","type":"shop"}, "Lender" : {"name":"Lender","type":"shop"}, "Livery" : {"name":"Livery","type":"shop"}, "Mason" : {"name":"Mason","type":"shop"}, "Open Market" : {"name":"Open Market","type":"shop"}, "Tailor" : {"name":"Tailor","type":"shop"}, "Tavern" : {"name":"Tavern","type":"shop"}, "Wellhouse" : {"name":"Wellhouse","type":"shop"}, }; SwRxHL.DB.SEXES={ "Alpha Female" : {"name":"Alpha Female","desc":"an Alpha Female is athletically gifted and very Healthy... (VIT+1; bust-15%; fit+20%; ht+6%; gen+75%)","base":"VIT","attr":{ sc:1.06, fit:1.20, brSc:0.85, genL:1.5, genG:2.0, }}, "Alpha Herm" : {"name":"Alpha Herm","desc":"an Alpha Herm is genitally gifted, but lacking a bit in the milk department... (LCK+1; gen+100%; bust-60%; fit+34%; ht+16%)","base":"LCK","attr":{ sc:1.16, fit:1.34, brSc:0.4, genL:1.69, genG:2.34, }}, "Alpha Male" : {"name":"Alpha Male","desc":"an Alpha Male is large and Powerful... (STR+1; gen+30%; fit+40%; ht+10%)","base":"STR","attr":{ sc:1.10, fit:1.40, genL:1.4, genG:1.2, }}, "Beta Female" : {"name":"Beta Female","desc":"a Beta Female is small but curvy and Feisty... (WIL+1; bust+60%; fit-20%; ht-20%; gen-30%)","base":"WIL","attr":{ sc:0.80, fit:0.80, brSc:1.6, genL:0.8, genG:0.6, }}, "Beta Herm" : {"name":"Beta Herm","desc":"a Beta Herm has somewhat muted genital characteristics, but makes up for this with larger mammaries... (LCK+1; gen+50%; bust+100%; fit-10%; ht-16%)","base":"LCK","attr":{ sc:0.84, fit:0.90, brSc:2.0, genL:1.34, genG:1.69, }}, "Beta Male" : {"name":"Beta Male","desc":"a Beta Male is small but Quick... (AGI+1; gen-30%; fit-30%; ht-10%)","base":"AGI","attr":{ sc:0.90, fit:0.70, genL:0.8, genG:0.6, }}, }; SwRxHL.DB.SKILLS={ }; SwRxHL.DB.SPECS={ "Athlete" : {"name":"Athlete","type":"Monk","core":"Monk","pri":"VIT","sec":"AGI"}, "Beast" : {"name":"Beast","type":"Monk","core":"Monk","pri":"VIT","sec":"VIT"}, "Cleric" : {"name":"Cleric","type":"Monk","core":"Monk","pri":"VIT","sec":"WIL"}, "Warlock" : {"name":"Warlock","type":"Monk","core":"Monk","pri":"VIT","sec":"LCK"}, "Wrestler" : {"name":"Wrestler","type":"Monk","core":"Monk","pri":"VIT","sec":"STR"}, "Bard" : {"name":"Bard","type":"Mystic","core":"Mystic","pri":"LCK","sec":"WIL"}, "Bodybuilder" : {"name":"Bodybuilder","type":"Mystic","core":"Mystic","pri":"LCK","sec":"STR"}, "Dancer" : {"name":"Dancer","type":"Mystic","core":"Mystic","pri":"LCK","sec":"AGI"}, "Shaman" : {"name":"Shaman","type":"Mystic","core":"Mystic","pri":"LCK","sec":"LCK"}, "Sorcerer" : {"name":"Sorcerer","type":"Mystic","core":"Mystic","pri":"LCK","sec":"VIT"}, "Assassin" : {"name":"Assassin","type":"Rogue","core":"Rogue","pri":"AGI","sec":"LCK"}, "Fighter" : {"name":"Fighter","type":"Rogue","core":"Rogue","pri":"AGI","sec":"STR"}, "Ninja" : {"name":"Ninja","type":"Rogue","core":"Rogue","pri":"AGI","sec":"AGI"}, "Ranger" : {"name":"Ranger","type":"Rogue","core":"Rogue","pri":"AGI","sec":"WIL"}, "Wildling" : {"name":"Wildling","type":"Rogue","core":"Rogue","pri":"AGI","sec":"VIT"}, "Alchemist" : {"name":"Alchemist","type":"Scholar","core":"Scholar","pri":"WIL","sec":"VIT"}, "Hunter" : {"name":"Hunter","type":"Scholar","core":"Scholar","pri":"WIL","sec":"AGI"}, "Psi" : {"name":"Psi","type":"Scholar","core":"Scholar","pri":"WIL","sec":"WIL"}, "Warlord" : {"name":"Warlord","type":"Scholar","core":"Scholar","pri":"WIL","sec":"STR"}, "Wizard" : {"name":"Wizard","type":"Scholar","core":"Scholar","pri":"WIL","sec":"LCK"}, "Barbarian" : {"name":"Barbarian","type":"Warrior","core":"Warrior","pri":"STR","sec":"STR"}, "Champion" : {"name":"Champion","type":"Warrior","core":"Warrior","pri":"STR","sec":"LCK"}, "Gladiator" : {"name":"Gladiator","type":"Warrior","core":"Warrior","pri":"STR","sec":"VIT"}, "Knight" : {"name":"Knight","type":"Warrior","core":"Warrior","pri":"STR","sec":"WIL"}, "Soldier" : {"name":"Soldier","type":"Warrior","core":"Warrior","pri":"STR","sec":"AGI"}, }; SwRxHL.DB.SPELLS={ }; SwRxHL.DB.TESTS={ "canGrasp" : {"name":"canGrasp","f":function(E, P={}) { return (Math.random()<0.5); }}, "canSit" : {"name":"canSit","f":function(E, P={}) { return (Math.random()<0.5); }}, "canStand" : {"name":"canStand","f":function(E, P={}) { return (Math.random()<0.5); }}, "canWalk" : {"name":"canWalk","f":function(E, P={}) { return (Math.random()<0.5); }}, "inBarrier" : {"name":"inBarrier","f":function(E, P={}) { return (Math.random()<0.5); }}, "inShelter" : {"name":"inShelter","f":function(E, P={}) { return (Math.random()<0.5); }}, "isAlone" : {"name":"isAlone","f":function(E, P={}) { return (Math.random()<0.5); }}, "isDark" : {"name":"isDark","f":function(E, P={}) { return (Math.random()<0.5); }}, "isLight" : {"name":"isLight","f":function(E, P={}) { return (Math.random()<0.5); }}, "isPrivate" : {"name":"isPrivate","f":function(E, P={}) { return (Math.random()<0.5); }}, "isReady" : {"name":"isReady","f":function(E, P={}) { return (Math.random()<0.8); }}, "isSafe" : {"name":"isSafe","f":function(E, P={}) { return (Math.random()<0.5); }}, "isSitting" : {"name":"isSitting","f":function(E, P={}) { return (Math.random()<0.5); }}, "isStanding" : {"name":"isStanding","f":function(E, P={}) { return (Math.random()<0.5); }}, }; SwRxHL.DB.WORLDS={ "w1" : {"name":"World1 (?Jelsar)","type":"medieval"}, }; SwRxHL.DB.ZONES={ "w1.Oakford" : {"name":"Oakford","type":"frontier"}, }; <</script>>
<<set setup.SYS={}; >> <<set setup.SYS.MAIN={ '' : function() {}, /* 2022 JS-SAFE VALUE ACCESS { */ /* USAGE: sX.get(propertyName|variable, PARAMETERS|{}); PARAMETERS: { A:functionArguments|[], ctx:objectContext|sX, df:defaultValue|undefined } */ 'get': function(v,P={}) { P.ctx??=null; P.df=P.df; let ctx=(P.ctx||sX), O=( ((typeof v)==='string') ?ctx[v] :v ), rslt=( ((typeof O)==='function') ?(P.A ?O(...P.A) :O(P)) :O ); return rslt??P.df; }, 'arr': function(v,P={}) { v=sX.get(v,P); return ( (isArray(v)) ?v :[v] )??[]; }, 'str': function(v,P={}) { /* upgrade: add localization/translation support here? request stringKey from langContext... */ return sX.get(v,P)??''; }, 'fin': function(v,P={}) { return v /* &&!NaN &&!infinity */ ; }, 'num': function(v,P={}) { P.rd??=null; v=sX.get(v,P); let f=Math[P.rd]; return sX.fin(((f) ?f(v) :v),P); }, 'int': function(v,P={}) { P.rd??='floor'; return sX.num(v,P); }, 'met': function(v,P={}) { return sX.num(v,P); /* convert default units to display format */ }, 'val': function(v,P={}) { /* get PROPERTY value whether scalar variable or functional return */ return ((typeof v==='function') ?v(P) :v); }, /* } 2022 JS-SAFE VALUE ACCESS */ /* 2022 CMS Formulas & Scenes w/ full ARGUMENT support { */ 'f': function(func,A=[]) { let f=((db.FUNC[func] ||{}).func ||(sX )[func] ||func ); return f(...A); }, /* 's': function(scene,A=[]) { let s=((db.SCENES[scene] ||{}).scene ||(sDS||{})[scene] ||scene); return ((typeof s === 'function') ? s(...A) : s); }, */ 's': function(scene,P={}) { P.this??=db.SCENES[scene]; return sX.get((db.SCENES[scene]??db.SCENES['gameIntro']).scene, P); }, /* } 2022 CMS Formulas & Scenes w/ full ARGUMENT support */ 'cls' : function(targetWindow) { $(targetWindow||'#passages').html(''); }, 'append' : function(sceneText, targetWindow) { $(targetWindow||'#passages').wiki(sceneText); }, 'replace' : function(sceneText, targetWindow) { sX.cls(targetWindow); sX.append(sceneText, targetWindow); }, 'goto' : function(scene, P={}) { if(db.SCENES[scene]) { switch(db.SCENES[scene].type) { case 'screen': $AP.G.screen=scene; break; case 'locale': $AP.E.PC.loc=scene; $AP.G.screen=null; break; case 'encounter': $AP.E.PC.enc=scene; break; case 'action': $AP.E.PC.act=scene; break; default: break; } sX.loadScene(scene); } else { alert(`SCENE: "${scene}" does not exist!`); } }, 'loadScene' : function(scene, P={}) { sX[(P.append ?'append' :'replace')]( (('locale'===db.SCENES[scene].type) ?sX.enc(P)+(!$AP.E.PC.enc /* "encounter-over" */ ?sX.s(scene, {this:db.SCENES[scene]}) :`<p>(scene cannot continue until encounter completes...)</p>` ) :sX.s(scene, {this:db.SCENES[scene]}) ) ); }, 'run' : function(sceneText) { $('').wiki(sceneText); /* DOESNT ***EXECUTE*** SCRIPT WITHOUT VALID TARGET ELEMENT! */ }, 'cgSel' : function(Field, SRC) { let field=Field.toLowerCase(), opts='', noOpt=`<option disabled selected value> -- select ${Field} -- </option>`; Object.keys(SRC).forEach(function(k) { opts+=`<option value="${k}" desc="${SRC[k].desc}">${SRC[k].name}</option>`; }); return `<label>${Field}: <select id="sel${Field}" onchange="$('#desc${Field}').html(this.options[this.selectedIndex].getAttribute('desc')); sX.cgSetAttr('${Field}', this.value); sX.cgUpdate();">${noOpt}${opts}${noOpt}</select></label><p id="desc${Field}"></p>`; }, 'cgTxt' : function(Field) { let field=Field.toLowerCase(), fDefault=$AP.E.PC[Field]||''; return `<label>${Field}: <input type="text" id="txt${Field}" value="${fDefault}" onchange="sX.cgSetAttr('${Field}', this.value); sX.cgUpdate();" /></label>`; }, 'cgPreview' : function() { let attrPrev='',statPrev='', PC=$AP.E.PC; Object.keys(PC).forEach(function(k) { if(typeof PC[k]!='object') { attrPrev+=`<tr><td>${k}: </td><td> ${PC[k]}</td></tr>`; } }); sX.cgRecalcStats(); let sysSTATS=sX.sys_STATS(); Object.keys(sysSTATS).forEach(function(k) { statPrev+=`<tr class="roll"><th class="lbl r">${k}</th><td>:</td><td class="val"> ${PC.STATS[k].st||'...'} </td> ${'<td class="die" roll="*"></td>'.repeat(PC.STATS[k].dc)} </tr>`; }); return `<table class='attr'>${attrPrev}</table><table class='stats'>${statPrev}</table>`; }, 'cgRecalcStats' : function() { let PC=$AP.E.PC, sysSTATS=sX.sys_STATS(), sysATTR=sX.sys_ATTR(); let RACE=db.RACES[PC.Race], CLASS=db.CLASSES[PC.Class], SEX=db.SEXES[PC.Sex], BUILD=db.BUILDS[PC.Build]; Object.keys(sysSTATS).forEach(function(k) { PC.STATS[k].dc=PC.ATTR.m; }); Object.keys(sysATTR).forEach(function(k) { PC.ATTR[k]=sysATTR[k]; }); if(PC.Race) { (PC.STATS[RACE.base]??=sX.initStat()).dc+=3; Object.keys(RACE.attr).forEach(function(k) { PC.ATTR[k]=(PC.ATTR[k]??1.0)*RACE.attr[k]; }); PC.ATTR.ht=RACE.attr.ht; } if(PC.Class) { (PC.STATS[CLASS.base]??=sX.initStat()).dc+=3; Object.keys(CLASS.attr).forEach(function(k) { PC.ATTR[k]=(PC.ATTR[k]??1.0)*CLASS.attr[k]; }); } if(PC.Sex) { (PC.STATS[SEX.base]??=sX.initStat()).dc+=1; Object.keys(SEX.attr).forEach(function(k) { PC.ATTR[k]=(PC.ATTR[k]??1.0)*SEX.attr[k]; }); } if(PC.Build) { (PC.STATS[BUILD.base]??=sX.initStat()).dc+=1; Object.keys(BUILD.attr).forEach(function(k) { PC.ATTR[k]=(PC.ATTR[k]??1.0)*BUILD.attr[k]; }); } Object.keys(sysSTATS).forEach(function(k) { PC.STATS[k].rk=(PC.ATTR.d||0)+(PC.STATS[k].dc||0)-(PC.STATS[k].wn||0); PC.STATS[k].mxe=Math.round((PC.STATS[k].st||0)*(PC.STATS[k].rk||0)/10); PC.STATS[k].en=Math.round(PC.STATS[k].mxe*0.5); }); }, 'cgUpdate' : function() { $('#statPreview').html(sX.cgPreview()); }, 'cgSetAttr' : function(attr, val) { $AP.E.PC[attr]=val; }, 'pcStatus' : function() { let basicInfo='', stats='', energy='', mxe=0, res='', PC=$AP.E.PC, cLv=(PC.CLASS[PC.Class]||{}).lv||0; let sysSTATS=sX.sys_STATS(); basicInfo=`<h2>${PC.Name}<br/><small><small>(Lv.${cLv} ${PC.Race} ${PC.Class})</small></small></h2>`; sX.cgRecalcStats(); Object.keys(sysSTATS).forEach(function(k) { stats+=`<tr><th class="lbl r">${k}</th><td>:</td><td class="val"> ${PC.STATS[k].st||0} </td> </tr>`; energy+=`<tr><th class="lbl r">${sysSTATS[k].en}</th><td>:</td><td class="val"> ${PC.STATS[k].en} / ${PC.STATS[k].mxe} </td> </tr>`; }); Object.keys(PC.RES).forEach(function(k) { res+=`<tr><th class="lbl r">${k}</th><td>:</td><td class="val"> ${PC.RES[k]} </td> </tr>`; }); sX.dialog_showAs(`<p>${basicInfo}</p><hr/><flex><table class='stats ib'>${stats}</table><table class='stats ib'>${energy}</table><table class='stats ib'>${res}</table></flex>`); }, 'sys_ATTR' : function() { return { sc:1.0, fit:1.0, brSc:1.0, genL:1.0, genG:1.0, }; }, 'sys_STATS' : function() { return { STR:{name: 'Strength', en: 'Stamina', }, AGI:{name: 'Agility', en: 'Focus', }, VIT:{name: 'Vitality', en: 'Health', }, WIL:{name: 'Willpower', en: 'Mana', }, LCK:{name: 'Luck', en: 'Lust', }, }; }, 'dialog_showAs' : function(modalText) { /* let us try the native functionality before rolling our own solution... */ Dialog.setup(); Dialog.wiki(modalText); Dialog.open(); }, 'gameInit' : function() { $AP.E.PC={}; }, 'gameUpdate' : function() { }, 'enc' : function(P={}) { if(!$AP.E.PC.enc) { /* NEW ENCOUNTER... */ P.varRoll??=sX.roll('1d20'); P.loc=$AP.E.PC.loc??'*'; P.scRoll??=sX.roll('1d20'); P.encScale??=sX.xlRNGEncSc(P.scRoll); P.tyRoll??=sX.roll('1d20'); P.encType??=sX.xlRNGEncTy(P.tyRoll); P.encRoll=`d${P.encScale}${P.encType.enc}`; $AP.E.PC.enc=P.rolledEncID=`${P.loc}.${P.encRoll}`; } else { /* CONTINUE ENCOUNTER... */ P.rolledEncID=$AP.E.PC.enc; } P.foundEncID=(db.ENC[P.rolledEncID] ?P.rolledEncID :`${P.loc}.*`); P.ctx=P.this??=db.ENC[P.foundEncID]; return sX.get('enc', P); /* return sX.get(db.ENC[P.foundEncID].enc, P); */ }, 'xlRNGEncSc' : function(d20, P={}) { switch(true) { case (d20<=1): return 4; break; case (d20<=3): return 6; break; case (d20<=12): return 8; break; case (d20<=19): return 10; break; case (d20<=20): return 12; break; } }, 'xlRNGEncTy' : function(d20, P={}) { switch(true) { case (d20<=1): return {enc:'h', code:'v', lbl:'violent'}; break; case (d20<=4): return {enc:'h', code:'h', lbl:'hostile'}; break; case (d20<=10): return {enc:'n', code:'n', lbl:'neutral'}; break; case (d20<=13): return {enc:'f', code:'f', lbl:'friendly'}; break; case (d20<=16): return {enc:'e', code:'e', lbl:'event'}; break; case (d20<=19): return {enc:'r', code:'r', lbl:'resource'}; break; case (d20<=20): return {enc:'r', code:'t', lbl:'treasure'}; break; } }, 'encOver' : function(P={}) { $AP.E.PC.enc=null; $AP.E.PC.dec=null; /* called within encounter branches, cannot output scene text! */ }, 'encBranch' : function(pChoice, P={}) { $AP.E.PC.dec=pChoice; sX.replace(sX.enc(P)); let scene=$AP.E.PC.loc; /* if encounter-over, continue SCENE with NO encounter */ if(!$AP.E.PC.enc) { sX.append(sX.s(scene, {this:db.SCENES[scene]})); } }, 'npcArt' : function(npcID) { let ctx=db.NPCS[npcID], art=sX.get('art', {ctx:ctx, this:ctx}); return `<img src="§GA/¡AP/npcs/${npcID}/${art}" style="height: 69vh; float:left; margin-left:-10em;" />`; }, 'ent' : function(entity) { return $AP.E[entity]; }, 'initEnt' : function(P={}) { P.cat??=10; P.mat??=3; let ENT={ ATTR:{ d: P.cat, m: P.mat, ht: 1.5, sc: 1.0, }, CLASS:{}, EQUIP:{}, INV:{}, PARTY:{}, RES:{ coin: 0, food: 0, water: 0, rest: 0, }, STATS:{}, }; ['STR','AGI','VIT','WIL','LCK'].forEach(function(k) { ENT.STATS[k]=sX.initStat(); }); return ENT; }, 'initPC' : function(P={}) { P.cat??=($AP.G.diff??10); P.mat=3; let PC=sX.initEnt(); Object.keys(db.CLASSES).forEach(function(k) { PC.CLASS[k]={ xp:0, lv:0, spec:null, }; }); return PC; }, 'initStat' : function() { return { dc:0, st:0, en:0, wn:0, mxe:0, }; }, 'setLocalShops' : function(SHOPS) { setup.TEMP.G.SHOPS[$AP.E.PC.loc] = SHOPS; }, 'getLocalShops' : function(town) { return setup.TEMP.G.SHOPS[(town||$AP.E.PC.loc)]; }, 'listShops' : function(town) { let SHOPS=sX.getLocalShops(town), shopList=''; Object.keys(SHOPS).forEach(function(k) { shopList+=`<li><a onClick="sX.ent('PC').shop='${k}'; sX.goto('atShop', {});">${k}</a></li>`; }); return `<ul>${shopList}</ul>`; }, 'abilSelUI' : function(ENT) { let ABILS=db.ABILITIES, abilList=''; Object.keys(ABILS).forEach(function(k) { abilList+=`<li><a onClick="">${k}</a> cost: ${ABILS[k].base} | requires: ${JSON.stringify(ABILS[k].prereq)}</li>`; }); return `<ul>${abilList}</ul>`; }, 'act_try' : function(action, ENT, P={}) { let ACTS=db.ACTIONS; if(!db.TESTS.isReady.f(ENT, P)) return `~not ready~`; if(!ACTS[action]) return `~undefined action~`; return ( sX.get(ACTS[action].can, {df:true, this:ACTS[action]}) ?sX.get(ACTS[action]['do'], {df:`~done~`, this:ACTS[action]}) :sX.get(ACTS[action].cant, {df:`~failed~`, this:ACTS[action]}) ); }, 'act_do' : function(action, ENT, P={}) { let ACTS=db.ACTIONS; if(!ACTS[action]) return `~undefined action~`; return sX.get(ACTS[action]['do'], {df:`~done~`, this:ACTS[action]}); }, 'act_fail' : function(action, ENT, P={}) { let ACTS=db.ACTIONS; if(!ACTS[action]) return `~undefined action~`; return sX.get(ACTS[action].cant, {df:`~failed~`, this:ACTS[action]}); }, 'act_testLink' : function(action, ENT, P={}) { let ACTS=db.ACTIONS; /* test action available... */ if(!ACTS[action]) return `~undefined action~`; return ( sX.get(ACTS[action].can, {df:true, this:ACTS[action]}) ?sX.act_doLink(action, ENT, P) :sX.act_cantLink(action, ENT, P) ); }, 'act_tryLink' : function(action, ENT, P={}) { let ACTS=db.ACTIONS; /* no pre-test, just try when clicked... */ if(!ACTS[action]) return `~undefined action~`; return sX.XML.a(action, {onClick:`(sX.act_try('${action}'));`, 'class':`ready`}); }, 'act_doLink' : function(action, ENT, P={}) { let ACTS=db.ACTIONS; /* no test, just works... */ if(!ACTS[action]) return `~undefined action~`; return sX.XML.a(action, {onClick:`(sX.act_do('${action}'));`, 'class':`ready`}); }, 'act_cantLink' : function(action, ENT, P={}) { let ACTS=db.ACTIONS; /* no test, just blocked... */ P.cant??=`!${action}`; if(!ACTS[action]) return `~undefined action~`; return sX.XML.a(P.cant, {'class':`disabled`}); }, 'sys_onKeyDown' : function(e) { /* allow typing... */ if(sX.sys_inputHasFocus()) { return true; } let ctx=sX.sys_modalContext(); switch(true) { case (e.code==='Numpad1'): case (e.code==='Digit1'): $(ctx+'#button1').toggleClass('lit', true); break; case (e.code==='Numpad2'): case (e.code==='Digit2'): $(ctx+'#button2').toggleClass('lit', true); break; case (e.code==='Numpad3'): case (e.code==='Digit3'): $(ctx+'#button3').toggleClass('lit', true); break; case (e.code==='Numpad4'): case (e.code==='Digit4'): $(ctx+'#button4').toggleClass('lit', true); break; case (e.code==='Numpad5'): case (e.code==='Digit5'): $(ctx+'#button5').toggleClass('lit', true); break; case (e.code==='Numpad6'): case (e.code==='Digit6'): $(ctx+'#button6').toggleClass('lit', true); break; case (e.code==='Numpad7'): case (e.code==='Digit7'): $(ctx+'#button7').toggleClass('lit', true); break; case (e.code==='Numpad8'): case (e.code==='Digit8'): $(ctx+'#button8').toggleClass('lit', true); break; case (e.code==='Numpad9'): case (e.code==='Digit9'): $(ctx+'#button9').toggleClass('lit', true); break; case (e.code==='Numpad0'): case (e.code==='Digit0'): $(ctx+'#button0').toggleClass('lit', true); break; case (e.code==='NumpadDecimal'): case (e.code==='Period'): $('#buttonDel').toggleClass('lit', true); break; case (e.code==='NumpadDivide'): case (e.code==='Slash'): $('#buttonFS').toggleClass('lit', true); break; } e.preventDefault(); return true; }, 'sys_onKeyPress' : function(e) {}, 'sys_onKeyUp' : function(e) { /* allow typing... */ if(sX.sys_inputHasFocus() && !(e.code==='Escape')) { return true; } let ctx=sX.sys_modalContext(); switch(true) { /* case (e.code==='Escape'): { if(document.activeElement.tagName != 'BODY') { document.activeElement.blur(); } else if($('#ui-dialog').is(":visible")) { Dialog.close(); } else if($('#gameMenu').is(":visible")) { if($('#gameMenu').attr('data-menu')!='') { switch($('#gameMenu').attr('data-root')) { case 'debug': sX.ui_debugMenu(''); break; case 'main': sX.ui_gameMenu(''); break; default: sX.ui_gameMenu('x'); break; } } else { $('#gameMenu').hide(); } } else if($('#modalScreen').is(":visible")) { $('#modalScreen').hide(); } else { sX.ui_gameMenu(); } } break; */ case (e.code==='Comma'): { /* $('#sceneView').toggle(); sX.sceneView_drawCanvas(new Date()); */ } break; case (e.code==='Semicolon'): { /* $('#roomView').toggle(); */ } break; case (e.code==='Quote'): { /* $('#landscape').toggle(); */ } break; /* case (e.code==='Backquote'): { if($('#gameMenu').is(":visible") && $('#gameMenu').attr('data-root')==='debug' && $('#gameMenu').attr('data-menu')==='') { $('#gameMenu').hide(); } else { sX.ui_debugMenu(); } } break; */ /* case (e.code==='Slash'): case (e.code==='NumpadDivide'): { if($('#gameMenu').is(":visible") && $('#gameMenu').attr('data-root')==='main' && $('#gameMenu').attr('data-menu')==='') { $('#gameMenu').hide(); } else { sX.ui_gameMenu(); } } break; */ case (e.code==='Period'): case (e.code==='NumpadDecimal'): { /* ACTION-MENU toggle/home */ $('#buttonDel').toggleClass('lit', false); $('#actionMenu').toggle(); } break; /* 0~9 top & numpad */ case (e.which>47 && e.which<58): case (e.which>95 && e.which<106): { /* $(ctx+'#button'+e.key).toggleClass('lit', false); $(ctx+'#button'+e.key).click(); */ } break; case (e.code==='ArrowUp'): { } break; case (e.code==='ArrowDown'): { } break; case (e.code==='ArrowLeft'): { } break; case (e.code==='ArrowRight'): { } break; case (e.code==='Tab'): { /* go backward */ if(e.shiftKey) {} } break; case (e.code==='Space'): { /* pause or repeat last action? */ } break; case (e.code==='Enter'): case (e.code==='NumpadEnter'): { /* perhaps this should be repeat? since the focus will already be on the item... */ } break; /* A~Z */ case (e.which>64 && e.which<91): { /* allow custom binding */ } break; /* F1~F12 */ case (e.which>111 && e.which<124): { /* allow custom binding */ if(e.which === 122) { sX.sys_requestFullscreen(); } } break; default: { sX.WTF(null, { 'e.key' : e.key, 'e.code' : e.code, 'e.which' : e.which, 'e.altKey' : e.altKey, 'e.ctrlKey' : e.ctrlKey, 'e.metaKey' : e.metaKey, 'e.shiftKey' : e.shiftKey, }); } break; } e.preventDefault(); return false; }, 'sys_onLoad' : function(save) { /* reinitialize state... */ /* sX.game_start(); */ /* if no game-state, then go to title screen instead... */ if($AP.E.PC.loc) { sX.loadScene($AP.E.PC.loc); } if($AP.G.screen && !$AP.E.PC.enc) { sX.loadScene($AP.G.screen); } /* if($AP.E.PC.loc) { sX.cls(); sX.replace(sX.s($AP.E.PC.loc, {this:db.SCENES[$AP.E.PC.loc]})); } if($AP.G.screen) { sX.cls(); sX.replace(sX.s($AP.G.screen, {this:db.SCENES[$AP.G.screen]})); } */ /* EVERY LOCATION SHOULD HAVE A DEFAULT SCREEN... like Oakford triggering inTown... */ }, 'sys_onSave' : function(save) { /* force history update on save */ save.state.history[0].variables.AP = $AP; }, 'sys_requestFullscreen' : function() { /* VERBOSE to prevent ILLEGAL INVOCATION BULLSHIT ERROR! */ let el=document.documentElement; if(el.requestFullscreen) { document.documentElement.requestFullscreen(); } else if(el.webkitRequestFullscreen) { document.documentElement.webkitRequestFullscreen(); } else if(el.mozRequestFullScreen) { document.documentElement.mozRequestFullScreen(); } else if(el.msRequestFullscreen) { document.documentElement.msRequestFullscreen(); } else sX.sys_nofun(); }, 'sys_nofun' : function() { /* empty fallback for missing function calls, rather than trying to execute null() as a function... */ }, 'sys_saveDesc' : function() { let PC=$AP.E.PC, cLv=(PC.CLASS[PC.Class]||{}).lv||0; /* return `${PC.Name} (Lv.${cLv} ${PC.Race} ${PC.Class}) - ${PC.loc}`; */ /* DOES NOT WORK CORRECTLY!!! DOES NOT SUPPORT HTML/markup in SAVE/PASSAGE DESCRIPTION */ /* return `<save><icon/><name>${PC.Name}</name><br/>Lv.<lv>${cLv}</lv> <race>${PC.Race}</race> <class>${PC.Class}</class><br/> </loc>${PC.loc}</loc></save>`; */ return `${PC.Name}\n (Lv.${cLv} ${PC.Race} ${PC.Class})\n ~ ${db.SCENES[PC.loc]?.name??'unknown'} ~ \n`; }, 'sys_inputHasFocus' : function() { /* HTML always returns UPPERCASE, XML returns as tYppED */ return ((document.activeElement.tagName==='INPUT') ? document.activeElement.type : false ); }, 'sys_modalContext' : function() { if($('#gameMenu').is(':visible')) { return "#gameMenu "; } else if($('#modalScreen').is(':visible')) { return "#modalUI "; } else { return "#actionMenu "; } }, 'choose' : function(LIST, pos) { if(typeof LIST==='object' && !Array.isArray(LIST)) { return sX.spinTheWheel(LIST)??pos??Object.keys(LIST)[0]??-1; } else { if(typeof pos==='undefined'||pos===null) { pos=Math.random(); } if(typeof LIST==='string') { LIST=(setup.LISTS[LIST]||[]); } if(!Number.isInteger(pos)) { pos=Math.floor(pos*LIST.length); } return LIST[pos]; } }, 'spinTheWheel' : function(ODDS, P={}) { let total=0, WHEEL={}, wedge=0, tick=null, result=null, wedge1=null, spin=Math.random(); Object.keys(ODDS).forEach(function(name) { /* is it a static or dynamic rate? */ wedge=sX.val(ODDS[name]); if(wedge>0) { total+=parseFloat(wedge*1000); WHEEL[total]=name; wedge1??=total; } }); spin=Math.round(spin*total); result = Object.keys(WHEEL).find(function(ticks) { tick=ticks; return ticks>=spin; }); return WHEEL[(result??Object.keys(WHEEL)[0])]; /* return WHEEL[(result||wedge1)]; */ }, 'WTF' : function(WTF) { if(WTF) { alert(JSON.stringify(WTF)); } }, 'JSON5':{ /* to UNFUCK double-quoted strings in object literals passed to TWINE-script!!! */ 'stringify' : function(OBJ) { return JSON.stringify(OBJ).replaceAll(String.fromCharCode(34), String.fromCharCode(39)) }, }, }; >> <<set setup.SYS.MATH={ 'rand' : function(min, max, round2) { min??=0.0; max??=1.0; round2??=0; if(round2===true) { round2=1.0; } let roll=Math.random(), retval=roll*(max-min)+min; if(round2) { let r=(retval % round2); retval+=-r+((r/round2<0.5) ? 0 : 1); } return retval; }, 'rng_dice' : function(dieCount, dieSides) { let sum=0; for(var i=0; i<dieCount; i++) { sum+=sX.rand(1, dieSides, true); } return sum; }, 'roll' : function(dice) { let DICE=dice.split('d'); return sX.rng_dice(DICE[0], DICE[1]); }, 'rollProof' : function(dice, P={}) { let DICE=dice.split('d'), roll=null, dieCount=1*DICE[0], dieSides=1*DICE[1], out='', rStr=''; P.ROLLS=P.ROLLS||[]; P.sum=P.sum||0; P.label=P.label||dice; P.crit=0; P.fail=0; P.STATS={}; for(let i=0; i<dieCount; i++) { roll=sX.rand(1, dieSides, true); (roll===1)&&P.fail++; (roll===dieSides)&&P.crit++; rStr=`${roll}`; P.STATS[rStr]=(P.STATS[rStr]??0)+1; P.sum+=roll; P.ROLLS.push(roll); out+=`<td class='die c' roll='${roll}'></td>`; } P.pct=P.sum/(dieCount*dieSides); P.out=`<tr class='roll'><th class='lbl'>${P.label}</th><td>:</td><td class='val'>${P.sum}</td>${out}</tr>`; return P; }, 'rollStats' : function(ENT=null) { ENT??=sX.ent('PC'); let out='', i=null, sum=null, stat=null, roll=null, PROOF=null, P={ROLLS:[]}, entSTATS=ENT.STATS, sysSTATS=sX.sys_STATS(); Object.keys(sysSTATS).forEach(function(k) { P.label=k; P.sum=0; PROOF=sX.rollProof(`${entSTATS[k].dc}d${ENT.ATTR.d}`, P); entSTATS[k].st=PROOF.sum; out+=PROOF.out; }); return `<table class='stats'>${out}</table>`; }, 'stack' : function(n, trunc) { let val=(n+1)*(n)/2; return (trunc ?Math.floor(val) :val); }, 'unstack' : function(n, trunc) { let sign=n/(Math.abs(n)||1), abs=(sX.sqrt((8*Math.abs(n))+1, '-')-1)/2; return sign*(trunc ?Math.floor(abs) :abs); }, 'sqrt' : function(n, mode) { if(typeof mode==='undefined') { mode='!'; } let sign=n/(Math.abs(n)||1), root=Math.sqrt(Math.abs(n)); if(sign==-1) { switch(mode) { case 'i^2': case '-': case -1: case 'signed': return sign*root; break; case 'i^4': case '+': case 1: case 'unsigned': case 'abs': return root; break; case 'i-i': case '0': case 0: case 'zero': return 0; break; case 'i': case '!': case false: case 'error': default: if(settings.debugModeInfo && console && console.trace) { console.trace(); } throw "it ain’t yer imagination, you tried to root a negative!"; break; } } else { return root; } }, 'pow' : function(base, exp, mode) { let abs=(Math.abs(base)||1), sign=base/abs, val=Math.pow(abs, exp); if(sign==-1) { switch(mode) { case 'i^2': case '-': case -1: case 'signed': return sign*val; break; case 'i^4': case '+': case 1: case 'unsigned': case 'abs': return val; break; case 'i-i': case '0': case 0: case 'zero': return 0; break; case 'i': case '!': case false: case 'error': default: if(settings.debugModeInfo && console && console.trace) { console.trace(); } throw "oh my, a negative taken to a fractional exponent... whatever shall we do? (ask the dev why the hell he didn’t specify +/- mode.)"; break; } } else { return val; } }, 'math_i' : function(sign, val, mode) { if(sign==-1) { switch(mode) { case 'i^2': case '-': case -1: case 'signed': return sign*val; break; case 'i^4': case '+': case 1: case 'unsigned': case 'abs': return val; break; case 'i-i': case '0': case 0: case 'zero': return 0; break; case 'i' : case '!': case false: case 'error': default: throw "Result is a Complex Number."; break; } } else { return val; } }, 'math_logX' : function(base, n, mode) { let abs_n=Math.abs(n), abs_b=Math.abs(base), sign=(n/abs_n)*(base/abs_b), val=Math.log(abs_n) / Math.log(abs_b); return sX.math_i(sign, val, mode); }, 'math_round' : function(val, iv=1.0) { return iv * Math.round(val/iv); }, }; >> <<set setup.SYS.TAGS={ 'xml_attr' : function(ATTR, P={}) { let q=P.q??String.fromCharCode(34), e='', attr=''; if(typeof ATTR==='string') { attr=ATTR; } else { Object.keys(ATTR).forEach(function(a) { e=(P[a] ?` ${P[a]}` :''); attr+=` ${a}=${q}${ATTR[a]}${e}${q}`; }); } return attr; }, 'wrap_xml' : function(contents, wrap, cls, ATTR, P={}) { ATTR??={}; let q=P.q||String.fromCharCode(34), attr='', out=''; if(Array.isArray(ATTR?.['class'])) { ATTR['class']=ATTR['class'].join(' '); } if(cls) { if(typeof (ATTR?.['class'])==='string') { ATTR['class']+=` ${cls}`; cls=''; } else { cls=` class=${q}${cls}${q}`; } } else { cls=''; } attr=sX.xml_attr(ATTR); let bVoid=(['img','input','br','hr','col','wbr','link','meta','source','track','area','embed','param','base','command','keygen'].indexOf(wrap)!==-1); if('img'===wrap) { out+=`<img${cls} src=${q}${contents}${q}${attr} />`; } else if(bVoid) { out+=`<${wrap}${cls}${attr} />`; } else { out+=`<${wrap}${cls}${attr}>${contents}</${wrap}>`; } return out; }, /* sX.<tag> replaced with sX.XML.<tag>; recommend aliasing as X5.<tag> in use-heavy functions; same applies to SugarCube Macros rescoped into sX.SCM.<macro> */ 'XML':{ 'a' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'a', null, ATTR, P); }, 'div' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'div', null, ATTR, P); }, 'li' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'li', null, ATTR, P); }, 'ol' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'ol', null, ATTR, P); }, 'p' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'p', null, ATTR, P); }, 'span' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'span', null, ATTR, P); }, 'table' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'table', null, ATTR, P); }, 'tr' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'tr', null, ATTR, P); }, 'th' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'th', null, ATTR, P); }, 'td' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'td', null, ATTR, P); }, 'ul' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'ul', null, ATTR, P); }, 'sel' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'select', null, ATTR, P); }, 'opt' : function(contents, ATTR, P={}) { return sX.wrap_xml(contents, 'option', null, ATTR, P); }, }, 'wrap_macro' : function(macro, args, contents, P={}) { let out=''; out=`<<${macro} ${args}>>`+ (contents ?(contents +`<</${macro}>>`) :'' ); return out; }, 'SCM': { 'link' : function(args, contents, P={}) { return sX.wrap_macro('link', `'${args}'`, contents, P); }, 'linkreplace' : function(args, contents, P={}) { return sX.wrap_macro('linkreplace', `'${args}'`, contents, P); }, 'linkappend' : function(args, contents, P={}) { return sX.wrap_macro('linkappend', `'${args}'`, contents, P); }, 'print' : function(args, P={}) { return sX.wrap_macro('print', args, P); }, 'run' : function(args, P={}) { return sX.wrap_macro('run', args, P); }, 'set' : function(args, P={}) { return sX.wrap_macro('set', args, P); }, }, }; >> <<set setup.ALIAS={}; Object.keys(setup.SYS).forEach(function(lib) { Object.keys(setup.SYS[lib]).forEach(function(fun) { setup.ALIAS[fun]=setup.SYS[lib][fun]; }); }); window.sX=setup.ALIAS; /* create a global shorthand alias */ >>
<<include "SYS">> <<include "DAT">> <<include "BUILD">> <<set $AP={ G:{ diff:10, }, W:{}, E:{}, }; $AP.E.PC=sX.initPC(); setup.TEMP={ 'G':{ SHOPS:{}, }, 'W':{}, 'E':{}, }; >>
<<run /* SUPPORTER CONTENT */ /* CHEATS */ >>
<a onclick="sX.pcStatus()">STATUS</a>