rot.js 135 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306
  1. /*
  2. This is rot.js, the ROguelike Toolkit in JavaScript.
  3. Version 0.6~dev, generated on Fri Jan 29 21:37:11 EST 2016.
  4. */
  5. /**
  6. * @namespace Top-level ROT namespace
  7. */
  8. var ROT = {
  9. /**
  10. * @returns {bool} Is rot.js supported by this browser?
  11. */
  12. isSupported: function() {
  13. return !!(document.createElement("canvas").getContext && Function.prototype.bind);
  14. },
  15. /** Default with for display and map generators */
  16. DEFAULT_WIDTH: 80,
  17. /** Default height for display and map generators */
  18. DEFAULT_HEIGHT: 25,
  19. /** Directional constants. Ordering is important! */
  20. DIRS: {
  21. "4": [
  22. [ 0, -1],
  23. [ 1, 0],
  24. [ 0, 1],
  25. [-1, 0]
  26. ],
  27. "8": [
  28. [ 0, -1],
  29. [ 1, -1],
  30. [ 1, 0],
  31. [ 1, 1],
  32. [ 0, 1],
  33. [-1, 1],
  34. [-1, 0],
  35. [-1, -1]
  36. ],
  37. "6": [
  38. [-1, -1],
  39. [ 1, -1],
  40. [ 2, 0],
  41. [ 1, 1],
  42. [-1, 1],
  43. [-2, 0]
  44. ]
  45. },
  46. /** Cancel key. */
  47. VK_CANCEL: 3,
  48. /** Help key. */
  49. VK_HELP: 6,
  50. /** Backspace key. */
  51. VK_BACK_SPACE: 8,
  52. /** Tab key. */
  53. VK_TAB: 9,
  54. /** 5 key on Numpad when NumLock is unlocked. Or on Mac, clear key which is positioned at NumLock key. */
  55. VK_CLEAR: 12,
  56. /** Return/enter key on the main keyboard. */
  57. VK_RETURN: 13,
  58. /** Reserved, but not used. */
  59. VK_ENTER: 14,
  60. /** Shift key. */
  61. VK_SHIFT: 16,
  62. /** Control key. */
  63. VK_CONTROL: 17,
  64. /** Alt (Option on Mac) key. */
  65. VK_ALT: 18,
  66. /** Pause key. */
  67. VK_PAUSE: 19,
  68. /** Caps lock. */
  69. VK_CAPS_LOCK: 20,
  70. /** Escape key. */
  71. VK_ESCAPE: 27,
  72. /** Space bar. */
  73. VK_SPACE: 32,
  74. /** Page Up key. */
  75. VK_PAGE_UP: 33,
  76. /** Page Down key. */
  77. VK_PAGE_DOWN: 34,
  78. /** End key. */
  79. VK_END: 35,
  80. /** Home key. */
  81. VK_HOME: 36,
  82. /** Left arrow. */
  83. VK_LEFT: 37,
  84. /** Up arrow. */
  85. VK_UP: 38,
  86. /** Right arrow. */
  87. VK_RIGHT: 39,
  88. /** Down arrow. */
  89. VK_DOWN: 40,
  90. /** Print Screen key. */
  91. VK_PRINTSCREEN: 44,
  92. /** Ins(ert) key. */
  93. VK_INSERT: 45,
  94. /** Del(ete) key. */
  95. VK_DELETE: 46,
  96. /***/
  97. VK_0: 48,
  98. /***/
  99. VK_1: 49,
  100. /***/
  101. VK_2: 50,
  102. /***/
  103. VK_3: 51,
  104. /***/
  105. VK_4: 52,
  106. /***/
  107. VK_5: 53,
  108. /***/
  109. VK_6: 54,
  110. /***/
  111. VK_7: 55,
  112. /***/
  113. VK_8: 56,
  114. /***/
  115. VK_9: 57,
  116. /** Colon (:) key. Requires Gecko 15.0 */
  117. VK_COLON: 58,
  118. /** Semicolon (;) key. */
  119. VK_SEMICOLON: 59,
  120. /** Less-than (<) key. Requires Gecko 15.0 */
  121. VK_LESS_THAN: 60,
  122. /** Equals (=) key. */
  123. VK_EQUALS: 61,
  124. /** Greater-than (>) key. Requires Gecko 15.0 */
  125. VK_GREATER_THAN: 62,
  126. /** Question mark (?) key. Requires Gecko 15.0 */
  127. VK_QUESTION_MARK: 63,
  128. /** Atmark (@) key. Requires Gecko 15.0 */
  129. VK_AT: 64,
  130. /***/
  131. VK_A: 65,
  132. /***/
  133. VK_B: 66,
  134. /***/
  135. VK_C: 67,
  136. /***/
  137. VK_D: 68,
  138. /***/
  139. VK_E: 69,
  140. /***/
  141. VK_F: 70,
  142. /***/
  143. VK_G: 71,
  144. /***/
  145. VK_H: 72,
  146. /***/
  147. VK_I: 73,
  148. /***/
  149. VK_J: 74,
  150. /***/
  151. VK_K: 75,
  152. /***/
  153. VK_L: 76,
  154. /***/
  155. VK_M: 77,
  156. /***/
  157. VK_N: 78,
  158. /***/
  159. VK_O: 79,
  160. /***/
  161. VK_P: 80,
  162. /***/
  163. VK_Q: 81,
  164. /***/
  165. VK_R: 82,
  166. /***/
  167. VK_S: 83,
  168. /***/
  169. VK_T: 84,
  170. /***/
  171. VK_U: 85,
  172. /***/
  173. VK_V: 86,
  174. /***/
  175. VK_W: 87,
  176. /***/
  177. VK_X: 88,
  178. /***/
  179. VK_Y: 89,
  180. /***/
  181. VK_Z: 90,
  182. /***/
  183. VK_CONTEXT_MENU: 93,
  184. /** 0 on the numeric keypad. */
  185. VK_NUMPAD0: 96,
  186. /** 1 on the numeric keypad. */
  187. VK_NUMPAD1: 97,
  188. /** 2 on the numeric keypad. */
  189. VK_NUMPAD2: 98,
  190. /** 3 on the numeric keypad. */
  191. VK_NUMPAD3: 99,
  192. /** 4 on the numeric keypad. */
  193. VK_NUMPAD4: 100,
  194. /** 5 on the numeric keypad. */
  195. VK_NUMPAD5: 101,
  196. /** 6 on the numeric keypad. */
  197. VK_NUMPAD6: 102,
  198. /** 7 on the numeric keypad. */
  199. VK_NUMPAD7: 103,
  200. /** 8 on the numeric keypad. */
  201. VK_NUMPAD8: 104,
  202. /** 9 on the numeric keypad. */
  203. VK_NUMPAD9: 105,
  204. /** * on the numeric keypad. */
  205. VK_MULTIPLY: 106,
  206. /** + on the numeric keypad. */
  207. VK_ADD: 107,
  208. /***/
  209. VK_SEPARATOR: 108,
  210. /** - on the numeric keypad. */
  211. VK_SUBTRACT: 109,
  212. /** Decimal point on the numeric keypad. */
  213. VK_DECIMAL: 110,
  214. /** / on the numeric keypad. */
  215. VK_DIVIDE: 111,
  216. /** F1 key. */
  217. VK_F1: 112,
  218. /** F2 key. */
  219. VK_F2: 113,
  220. /** F3 key. */
  221. VK_F3: 114,
  222. /** F4 key. */
  223. VK_F4: 115,
  224. /** F5 key. */
  225. VK_F5: 116,
  226. /** F6 key. */
  227. VK_F6: 117,
  228. /** F7 key. */
  229. VK_F7: 118,
  230. /** F8 key. */
  231. VK_F8: 119,
  232. /** F9 key. */
  233. VK_F9: 120,
  234. /** F10 key. */
  235. VK_F10: 121,
  236. /** F11 key. */
  237. VK_F11: 122,
  238. /** F12 key. */
  239. VK_F12: 123,
  240. /** F13 key. */
  241. VK_F13: 124,
  242. /** F14 key. */
  243. VK_F14: 125,
  244. /** F15 key. */
  245. VK_F15: 126,
  246. /** F16 key. */
  247. VK_F16: 127,
  248. /** F17 key. */
  249. VK_F17: 128,
  250. /** F18 key. */
  251. VK_F18: 129,
  252. /** F19 key. */
  253. VK_F19: 130,
  254. /** F20 key. */
  255. VK_F20: 131,
  256. /** F21 key. */
  257. VK_F21: 132,
  258. /** F22 key. */
  259. VK_F22: 133,
  260. /** F23 key. */
  261. VK_F23: 134,
  262. /** F24 key. */
  263. VK_F24: 135,
  264. /** Num Lock key. */
  265. VK_NUM_LOCK: 144,
  266. /** Scroll Lock key. */
  267. VK_SCROLL_LOCK: 145,
  268. /** Circumflex (^) key. Requires Gecko 15.0 */
  269. VK_CIRCUMFLEX: 160,
  270. /** Exclamation (!) key. Requires Gecko 15.0 */
  271. VK_EXCLAMATION: 161,
  272. /** Double quote () key. Requires Gecko 15.0 */
  273. VK_DOUBLE_QUOTE: 162,
  274. /** Hash (#) key. Requires Gecko 15.0 */
  275. VK_HASH: 163,
  276. /** Dollar sign ($) key. Requires Gecko 15.0 */
  277. VK_DOLLAR: 164,
  278. /** Percent (%) key. Requires Gecko 15.0 */
  279. VK_PERCENT: 165,
  280. /** Ampersand (&) key. Requires Gecko 15.0 */
  281. VK_AMPERSAND: 166,
  282. /** Underscore (_) key. Requires Gecko 15.0 */
  283. VK_UNDERSCORE: 167,
  284. /** Open parenthesis (() key. Requires Gecko 15.0 */
  285. VK_OPEN_PAREN: 168,
  286. /** Close parenthesis ()) key. Requires Gecko 15.0 */
  287. VK_CLOSE_PAREN: 169,
  288. /* Asterisk (*) key. Requires Gecko 15.0 */
  289. VK_ASTERISK: 170,
  290. /** Plus (+) key. Requires Gecko 15.0 */
  291. VK_PLUS: 171,
  292. /** Pipe (|) key. Requires Gecko 15.0 */
  293. VK_PIPE: 172,
  294. /** Hyphen-US/docs/Minus (-) key. Requires Gecko 15.0 */
  295. VK_HYPHEN_MINUS: 173,
  296. /** Open curly bracket ({) key. Requires Gecko 15.0 */
  297. VK_OPEN_CURLY_BRACKET: 174,
  298. /** Close curly bracket (}) key. Requires Gecko 15.0 */
  299. VK_CLOSE_CURLY_BRACKET: 175,
  300. /** Tilde (~) key. Requires Gecko 15.0 */
  301. VK_TILDE: 176,
  302. /** Comma (,) key. */
  303. VK_COMMA: 188,
  304. /** Period (.) key. */
  305. VK_PERIOD: 190,
  306. /** Slash (/) key. */
  307. VK_SLASH: 191,
  308. /** Back tick (`) key. */
  309. VK_BACK_QUOTE: 192,
  310. /** Open square bracket ([) key. */
  311. VK_OPEN_BRACKET: 219,
  312. /** Back slash (\) key. */
  313. VK_BACK_SLASH: 220,
  314. /** Close square bracket (]) key. */
  315. VK_CLOSE_BRACKET: 221,
  316. /** Quote (''') key. */
  317. VK_QUOTE: 222,
  318. /** Meta key on Linux, Command key on Mac. */
  319. VK_META: 224,
  320. /** AltGr key on Linux. Requires Gecko 15.0 */
  321. VK_ALTGR: 225,
  322. /** Windows logo key on Windows. Or Super or Hyper key on Linux. Requires Gecko 15.0 */
  323. VK_WIN: 91,
  324. /** Linux support for this keycode was added in Gecko 4.0. */
  325. VK_KANA: 21,
  326. /** Linux support for this keycode was added in Gecko 4.0. */
  327. VK_HANGUL: 21,
  328. /** 英数 key on Japanese Mac keyboard. Requires Gecko 15.0 */
  329. VK_EISU: 22,
  330. /** Linux support for this keycode was added in Gecko 4.0. */
  331. VK_JUNJA: 23,
  332. /** Linux support for this keycode was added in Gecko 4.0. */
  333. VK_FINAL: 24,
  334. /** Linux support for this keycode was added in Gecko 4.0. */
  335. VK_HANJA: 25,
  336. /** Linux support for this keycode was added in Gecko 4.0. */
  337. VK_KANJI: 25,
  338. /** Linux support for this keycode was added in Gecko 4.0. */
  339. VK_CONVERT: 28,
  340. /** Linux support for this keycode was added in Gecko 4.0. */
  341. VK_NONCONVERT: 29,
  342. /** Linux support for this keycode was added in Gecko 4.0. */
  343. VK_ACCEPT: 30,
  344. /** Linux support for this keycode was added in Gecko 4.0. */
  345. VK_MODECHANGE: 31,
  346. /** Linux support for this keycode was added in Gecko 4.0. */
  347. VK_SELECT: 41,
  348. /** Linux support for this keycode was added in Gecko 4.0. */
  349. VK_PRINT: 42,
  350. /** Linux support for this keycode was added in Gecko 4.0. */
  351. VK_EXECUTE: 43,
  352. /** Linux support for this keycode was added in Gecko 4.0. */
  353. VK_SLEEP: 95
  354. };
  355. /**
  356. * @namespace
  357. * Contains text tokenization and breaking routines
  358. */
  359. ROT.Text = {
  360. RE_COLORS: /%([bc]){([^}]*)}/g,
  361. /* token types */
  362. TYPE_TEXT: 0,
  363. TYPE_NEWLINE: 1,
  364. TYPE_FG: 2,
  365. TYPE_BG: 3,
  366. /**
  367. * Measure size of a resulting text block
  368. */
  369. measure: function(str, maxWidth) {
  370. var result = {width:0, height:1};
  371. var tokens = this.tokenize(str, maxWidth);
  372. var lineWidth = 0;
  373. for (var i=0;i<tokens.length;i++) {
  374. var token = tokens[i];
  375. switch (token.type) {
  376. case this.TYPE_TEXT:
  377. lineWidth += token.value.length;
  378. break;
  379. case this.TYPE_NEWLINE:
  380. result.height++;
  381. result.width = Math.max(result.width, lineWidth);
  382. lineWidth = 0;
  383. break;
  384. }
  385. }
  386. result.width = Math.max(result.width, lineWidth);
  387. return result;
  388. },
  389. /**
  390. * Convert string to a series of a formatting commands
  391. */
  392. tokenize: function(str, maxWidth) {
  393. var result = [];
  394. /* first tokenization pass - split texts and color formatting commands */
  395. var offset = 0;
  396. str.replace(this.RE_COLORS, function(match, type, name, index) {
  397. /* string before */
  398. var part = str.substring(offset, index);
  399. if (part.length) {
  400. result.push({
  401. type: ROT.Text.TYPE_TEXT,
  402. value: part
  403. });
  404. }
  405. /* color command */
  406. result.push({
  407. type: (type == "c" ? ROT.Text.TYPE_FG : ROT.Text.TYPE_BG),
  408. value: name.trim()
  409. });
  410. offset = index + match.length;
  411. return "";
  412. });
  413. /* last remaining part */
  414. var part = str.substring(offset);
  415. if (part.length) {
  416. result.push({
  417. type: ROT.Text.TYPE_TEXT,
  418. value: part
  419. });
  420. }
  421. return this._breakLines(result, maxWidth);
  422. },
  423. /* insert line breaks into first-pass tokenized data */
  424. _breakLines: function(tokens, maxWidth) {
  425. if (!maxWidth) { maxWidth = Infinity; };
  426. var i = 0;
  427. var lineLength = 0;
  428. var lastTokenWithSpace = -1;
  429. while (i < tokens.length) { /* take all text tokens, remove space, apply linebreaks */
  430. var token = tokens[i];
  431. if (token.type == ROT.Text.TYPE_NEWLINE) { /* reset */
  432. lineLength = 0;
  433. lastTokenWithSpace = -1;
  434. }
  435. if (token.type != ROT.Text.TYPE_TEXT) { /* skip non-text tokens */
  436. i++;
  437. continue;
  438. }
  439. /* remove spaces at the beginning of line */
  440. while (lineLength == 0 && token.value.charAt(0) == " ") { token.value = token.value.substring(1); }
  441. /* forced newline? insert two new tokens after this one */
  442. var index = token.value.indexOf("\n");
  443. if (index != -1) {
  444. token.value = this._breakInsideToken(tokens, i, index, true);
  445. /* if there are spaces at the end, we must remove them (we do not want the line too long) */
  446. var arr = token.value.split("");
  447. while (arr.length && arr[arr.length-1] == " ") { arr.pop(); }
  448. token.value = arr.join("");
  449. }
  450. /* token degenerated? */
  451. if (!token.value.length) {
  452. tokens.splice(i, 1);
  453. continue;
  454. }
  455. if (lineLength + token.value.length > maxWidth) { /* line too long, find a suitable breaking spot */
  456. /* is it possible to break within this token? */
  457. var index = -1;
  458. while (1) {
  459. var nextIndex = token.value.indexOf(" ", index+1);
  460. if (nextIndex == -1) { break; }
  461. if (lineLength + nextIndex > maxWidth) { break; }
  462. index = nextIndex;
  463. }
  464. if (index != -1) { /* break at space within this one */
  465. token.value = this._breakInsideToken(tokens, i, index, true);
  466. } else if (lastTokenWithSpace != -1) { /* is there a previous token where a break can occur? */
  467. var token = tokens[lastTokenWithSpace];
  468. var breakIndex = token.value.lastIndexOf(" ");
  469. token.value = this._breakInsideToken(tokens, lastTokenWithSpace, breakIndex, true);
  470. i = lastTokenWithSpace;
  471. } else { /* force break in this token */
  472. token.value = this._breakInsideToken(tokens, i, maxWidth-lineLength, false);
  473. }
  474. } else { /* line not long, continue */
  475. lineLength += token.value.length;
  476. if (token.value.indexOf(" ") != -1) { lastTokenWithSpace = i; }
  477. }
  478. i++; /* advance to next token */
  479. }
  480. tokens.push({type: ROT.Text.TYPE_NEWLINE}); /* insert fake newline to fix the last text line */
  481. /* remove trailing space from text tokens before newlines */
  482. var lastTextToken = null;
  483. for (var i=0;i<tokens.length;i++) {
  484. var token = tokens[i];
  485. switch (token.type) {
  486. case ROT.Text.TYPE_TEXT: lastTextToken = token; break;
  487. case ROT.Text.TYPE_NEWLINE:
  488. if (lastTextToken) { /* remove trailing space */
  489. var arr = lastTextToken.value.split("");
  490. while (arr.length && arr[arr.length-1] == " ") { arr.pop(); }
  491. lastTextToken.value = arr.join("");
  492. }
  493. lastTextToken = null;
  494. break;
  495. }
  496. }
  497. tokens.pop(); /* remove fake token */
  498. return tokens;
  499. },
  500. /**
  501. * Create new tokens and insert them into the stream
  502. * @param {object[]} tokens
  503. * @param {int} tokenIndex Token being processed
  504. * @param {int} breakIndex Index within current token's value
  505. * @param {bool} removeBreakChar Do we want to remove the breaking character?
  506. * @returns {string} remaining unbroken token value
  507. */
  508. _breakInsideToken: function(tokens, tokenIndex, breakIndex, removeBreakChar) {
  509. var newBreakToken = {
  510. type: ROT.Text.TYPE_NEWLINE
  511. }
  512. var newTextToken = {
  513. type: ROT.Text.TYPE_TEXT,
  514. value: tokens[tokenIndex].value.substring(breakIndex + (removeBreakChar ? 1 : 0))
  515. }
  516. tokens.splice(tokenIndex+1, 0, newBreakToken, newTextToken);
  517. return tokens[tokenIndex].value.substring(0, breakIndex);
  518. }
  519. }
  520. /**
  521. * @returns {any} Randomly picked item, null when length=0
  522. */
  523. Array.prototype.random = Array.prototype.random || function() {
  524. if (!this.length) { return null; }
  525. return this[Math.floor(ROT.RNG.getUniform() * this.length)];
  526. }
  527. /**
  528. * @returns {array} New array with randomized items
  529. * FIXME destroys this!
  530. */
  531. Array.prototype.randomize = Array.prototype.randomize || function() {
  532. var result = [];
  533. while (this.length) {
  534. var index = this.indexOf(this.random());
  535. result.push(this.splice(index, 1)[0]);
  536. }
  537. return result;
  538. }
  539. /**
  540. * Always positive modulus
  541. * @param {int} n Modulus
  542. * @returns {int} this modulo n
  543. */
  544. Number.prototype.mod = Number.prototype.mod || function(n) {
  545. return ((this%n)+n)%n;
  546. }
  547. /**
  548. * @returns {string} First letter capitalized
  549. */
  550. String.prototype.capitalize = String.prototype.capitalize || function() {
  551. return this.charAt(0).toUpperCase() + this.substring(1);
  552. }
  553. /**
  554. * Left pad
  555. * @param {string} [character="0"]
  556. * @param {int} [count=2]
  557. */
  558. String.prototype.lpad = String.prototype.lpad || function(character, count) {
  559. var ch = character || "0";
  560. var cnt = count || 2;
  561. var s = "";
  562. while (s.length < (cnt - this.length)) { s += ch; }
  563. s = s.substring(0, cnt-this.length);
  564. return s+this;
  565. }
  566. /**
  567. * Right pad
  568. * @param {string} [character="0"]
  569. * @param {int} [count=2]
  570. */
  571. String.prototype.rpad = String.prototype.rpad || function(character, count) {
  572. var ch = character || "0";
  573. var cnt = count || 2;
  574. var s = "";
  575. while (s.length < (cnt - this.length)) { s += ch; }
  576. s = s.substring(0, cnt-this.length);
  577. return this+s;
  578. }
  579. /**
  580. * Format a string in a flexible way. Scans for %s strings and replaces them with arguments. List of patterns is modifiable via String.format.map.
  581. * @param {string} template
  582. * @param {any} [argv]
  583. */
  584. String.format = String.format || function(template) {
  585. var map = String.format.map;
  586. var args = Array.prototype.slice.call(arguments, 1);
  587. var replacer = function(match, group1, group2, index) {
  588. if (template.charAt(index-1) == "%") { return match.substring(1); }
  589. if (!args.length) { return match; }
  590. var obj = args[0];
  591. var group = group1 || group2;
  592. var parts = group.split(",");
  593. var name = parts.shift();
  594. var method = map[name.toLowerCase()];
  595. if (!method) { return match; }
  596. var obj = args.shift();
  597. var replaced = obj[method].apply(obj, parts);
  598. var first = name.charAt(0);
  599. if (first != first.toLowerCase()) { replaced = replaced.capitalize(); }
  600. return replaced;
  601. }
  602. return template.replace(/%(?:([a-z]+)|(?:{([^}]+)}))/gi, replacer);
  603. }
  604. String.format.map = String.format.map || {
  605. "s": "toString"
  606. }
  607. /**
  608. * Convenience shortcut to String.format(this)
  609. */
  610. String.prototype.format = String.prototype.format || function() {
  611. var args = Array.prototype.slice.call(arguments);
  612. args.unshift(this);
  613. return String.format.apply(String, args);
  614. }
  615. if (!Object.create) {
  616. /**
  617. * ES5 Object.create
  618. */
  619. Object.create = function(o) {
  620. var tmp = function() {};
  621. tmp.prototype = o;
  622. return new tmp();
  623. };
  624. }
  625. /**
  626. * Sets prototype of this function to an instance of parent function
  627. * @param {function} parent
  628. */
  629. Function.prototype.extend = Function.prototype.extend || function(parent) {
  630. this.prototype = Object.create(parent.prototype);
  631. this.prototype.constructor = this;
  632. return this;
  633. }
  634. if (typeof window != "undefined") {
  635. window.requestAnimationFrame =
  636. window.requestAnimationFrame
  637. || window.mozRequestAnimationFrame
  638. || window.webkitRequestAnimationFrame
  639. || window.oRequestAnimationFrame
  640. || window.msRequestAnimationFrame
  641. || function(cb) { return setTimeout(cb, 1000/60); };
  642. window.cancelAnimationFrame =
  643. window.cancelAnimationFrame
  644. || window.mozCancelAnimationFrame
  645. || window.webkitCancelAnimationFrame
  646. || window.oCancelAnimationFrame
  647. || window.msCancelAnimationFrame
  648. || function(id) { return clearTimeout(id); };
  649. }
  650. /**
  651. * @class Visual map display
  652. * @param {object} [options]
  653. * @param {int} [options.width=ROT.DEFAULT_WIDTH]
  654. * @param {int} [options.height=ROT.DEFAULT_HEIGHT]
  655. * @param {int} [options.fontSize=15]
  656. * @param {string} [options.fontFamily="monospace"]
  657. * @param {string} [options.fontStyle=""] bold/italic/none/both
  658. * @param {string} [options.fg="#ccc"]
  659. * @param {string} [options.bg="#000"]
  660. * @param {float} [options.spacing=1]
  661. * @param {float} [options.border=0]
  662. * @param {string} [options.layout="rect"]
  663. * @param {bool} [options.forceSquareRatio=false]
  664. * @param {int} [options.tileWidth=32]
  665. * @param {int} [options.tileHeight=32]
  666. * @param {object} [options.tileMap={}]
  667. * @param {image} [options.tileSet=null]
  668. * @param {image} [options.tileColorize=false]
  669. */
  670. ROT.Display = function(options) {
  671. var canvas = document.createElement("canvas");
  672. this._context = canvas.getContext("2d");
  673. this._data = {};
  674. this._dirty = false; /* false = nothing, true = all, object = dirty cells */
  675. this._options = {};
  676. this._backend = null;
  677. var defaultOptions = {
  678. width: ROT.DEFAULT_WIDTH,
  679. height: ROT.DEFAULT_HEIGHT,
  680. transpose: false,
  681. layout: "rect",
  682. fontSize: 15,
  683. spacing: 1,
  684. border: 0,
  685. forceSquareRatio: false,
  686. fontFamily: "monospace",
  687. fontStyle: "",
  688. fg: "#ccc",
  689. bg: "#000",
  690. tileWidth: 32,
  691. tileHeight: 32,
  692. tileMap: {},
  693. tileSet: null,
  694. tileColorize: false,
  695. termColor: "xterm"
  696. };
  697. for (var p in options) { defaultOptions[p] = options[p]; }
  698. this.setOptions(defaultOptions);
  699. this.DEBUG = this.DEBUG.bind(this);
  700. this._tick = this._tick.bind(this);
  701. requestAnimationFrame(this._tick);
  702. }
  703. /**
  704. * Debug helper, ideal as a map generator callback. Always bound to this.
  705. * @param {int} x
  706. * @param {int} y
  707. * @param {int} what
  708. */
  709. ROT.Display.prototype.DEBUG = function(x, y, what) {
  710. var colors = [this._options.bg, this._options.fg];
  711. this.draw(x, y, null, null, colors[what % colors.length]);
  712. }
  713. /**
  714. * Clear the whole display (cover it with background color)
  715. */
  716. ROT.Display.prototype.clear = function() {
  717. this._data = {};
  718. this._dirty = true;
  719. }
  720. /**
  721. * @see ROT.Display
  722. */
  723. ROT.Display.prototype.setOptions = function(options) {
  724. for (var p in options) { this._options[p] = options[p]; }
  725. if (options.width || options.height || options.fontSize || options.fontFamily || options.spacing || options.layout) {
  726. if (options.layout) {
  727. this._backend = new ROT.Display[options.layout.capitalize()](this._context);
  728. }
  729. var font = (this._options.fontStyle ? this._options.fontStyle + " " : "") + this._options.fontSize + "px " + this._options.fontFamily;
  730. this._context.font = font;
  731. this._backend.compute(this._options);
  732. this._context.font = font;
  733. this._context.textAlign = "center";
  734. this._context.textBaseline = "middle";
  735. this._dirty = true;
  736. }
  737. return this;
  738. }
  739. /**
  740. * Returns currently set options
  741. * @returns {object} Current options object
  742. */
  743. ROT.Display.prototype.getOptions = function() {
  744. return this._options;
  745. }
  746. /**
  747. * Returns the DOM node of this display
  748. * @returns {node} DOM node
  749. */
  750. ROT.Display.prototype.getContainer = function() {
  751. return this._context.canvas;
  752. }
  753. /**
  754. * Compute the maximum width/height to fit into a set of given constraints
  755. * @param {int} availWidth Maximum allowed pixel width
  756. * @param {int} availHeight Maximum allowed pixel height
  757. * @returns {int[2]} cellWidth,cellHeight
  758. */
  759. ROT.Display.prototype.computeSize = function(availWidth, availHeight) {
  760. return this._backend.computeSize(availWidth, availHeight, this._options);
  761. }
  762. /**
  763. * Compute the maximum font size to fit into a set of given constraints
  764. * @param {int} availWidth Maximum allowed pixel width
  765. * @param {int} availHeight Maximum allowed pixel height
  766. * @returns {int} fontSize
  767. */
  768. ROT.Display.prototype.computeFontSize = function(availWidth, availHeight) {
  769. return this._backend.computeFontSize(availWidth, availHeight, this._options);
  770. }
  771. /**
  772. * Convert a DOM event (mouse or touch) to map coordinates. Uses first touch for multi-touch.
  773. * @param {Event} e event
  774. * @returns {int[2]} -1 for values outside of the canvas
  775. */
  776. ROT.Display.prototype.eventToPosition = function(e) {
  777. if (e.touches) {
  778. var x = e.touches[0].clientX;
  779. var y = e.touches[0].clientY;
  780. } else {
  781. var x = e.clientX;
  782. var y = e.clientY;
  783. }
  784. var rect = this._context.canvas.getBoundingClientRect();
  785. x -= rect.left;
  786. y -= rect.top;
  787. x *= this._context.canvas.width / this._context.canvas.clientWidth;
  788. y *= this._context.canvas.height / this._context.canvas.clientHeight;
  789. if (x < 0 || y < 0 || x >= this._context.canvas.width || y >= this._context.canvas.height) { return [-1, -1]; }
  790. return this._backend.eventToPosition(x, y);
  791. }
  792. /**
  793. * @param {int} x
  794. * @param {int} y
  795. * @param {string || string[]} ch One or more chars (will be overlapping themselves)
  796. * @param {string} [fg] foreground color
  797. * @param {string} [bg] background color
  798. */
  799. ROT.Display.prototype.draw = function(x, y, ch, fg, bg) {
  800. if (!fg) { fg = this._options.fg; }
  801. if (!bg) { bg = this._options.bg; }
  802. this._data[x+","+y] = [x, y, ch, fg, bg];
  803. if (this._dirty === true) { return; } /* will already redraw everything */
  804. if (!this._dirty) { this._dirty = {}; } /* first! */
  805. this._dirty[x+","+y] = true;
  806. }
  807. /**
  808. * Draws a text at given position. Optionally wraps at a maximum length. Currently does not work with hex layout.
  809. * @param {int} x
  810. * @param {int} y
  811. * @param {string} text May contain color/background format specifiers, %c{name}/%b{name}, both optional. %c{}/%b{} resets to default.
  812. * @param {int} [maxWidth] wrap at what width?
  813. * @returns {int} lines drawn
  814. */
  815. ROT.Display.prototype.drawText = function(x, y, text, maxWidth) {
  816. var fg = null;
  817. var bg = null;
  818. var cx = x;
  819. var cy = y;
  820. var lines = 1;
  821. if (!maxWidth) { maxWidth = this._options.width-x; }
  822. var tokens = ROT.Text.tokenize(text, maxWidth);
  823. while (tokens.length) { /* interpret tokenized opcode stream */
  824. var token = tokens.shift();
  825. switch (token.type) {
  826. case ROT.Text.TYPE_TEXT:
  827. var isSpace = false, isPrevSpace = false, isFullWidth = false, isPrevFullWidth = false;
  828. for (var i=0;i<token.value.length;i++) {
  829. var cc = token.value.charCodeAt(i);
  830. var c = token.value.charAt(i);
  831. // Assign to `true` when the current char is full-width.
  832. isFullWidth = (cc > 0xff && cc < 0xff61) || (cc > 0xffdc && cc < 0xffe8) && cc > 0xffee;
  833. // Current char is space, whatever full-width or half-width both are OK.
  834. isSpace = (c.charCodeAt(0) == 0x20 || c.charCodeAt(0) == 0x3000);
  835. // The previous char is full-width and
  836. // current char is nether half-width nor a space.
  837. if (isPrevFullWidth && !isFullWidth && !isSpace) { cx++; } // add an extra position
  838. // The current char is full-width and
  839. // the previous char is not a space.
  840. if(isFullWidth && !isPrevSpace) { cx++; } // add an extra position
  841. this.draw(cx++, cy, c, fg, bg);
  842. isPrevSpace = isSpace;
  843. isPrevFullWidth = isFullWidth;
  844. }
  845. break;
  846. case ROT.Text.TYPE_FG:
  847. fg = token.value || null;
  848. break;
  849. case ROT.Text.TYPE_BG:
  850. bg = token.value || null;
  851. break;
  852. case ROT.Text.TYPE_NEWLINE:
  853. cx = x;
  854. cy++;
  855. lines++
  856. break;
  857. }
  858. }
  859. return lines;
  860. }
  861. /**
  862. * Timer tick: update dirty parts
  863. */
  864. ROT.Display.prototype._tick = function() {
  865. requestAnimationFrame(this._tick);
  866. if (!this._dirty) { return; }
  867. if (this._dirty === true) { /* draw all */
  868. this._context.fillStyle = this._options.bg;
  869. this._context.fillRect(0, 0, this._context.canvas.width, this._context.canvas.height);
  870. for (var id in this._data) { /* redraw cached data */
  871. this._draw(id, false);
  872. }
  873. } else { /* draw only dirty */
  874. for (var key in this._dirty) {
  875. this._draw(key, true);
  876. }
  877. }
  878. this._dirty = false;
  879. }
  880. /**
  881. * @param {string} key What to draw
  882. * @param {bool} clearBefore Is it necessary to clean before?
  883. */
  884. ROT.Display.prototype._draw = function(key, clearBefore) {
  885. var data = this._data[key];
  886. if (data[4] != this._options.bg) { clearBefore = true; }
  887. this._backend.draw(data, clearBefore);
  888. }
  889. /**
  890. * @class Abstract display backend module
  891. * @private
  892. */
  893. ROT.Display.Backend = function(context) {
  894. this._context = context;
  895. }
  896. ROT.Display.Backend.prototype.compute = function(options) {
  897. }
  898. ROT.Display.Backend.prototype.draw = function(data, clearBefore) {
  899. }
  900. ROT.Display.Backend.prototype.computeSize = function(availWidth, availHeight) {
  901. }
  902. ROT.Display.Backend.prototype.computeFontSize = function(availWidth, availHeight) {
  903. }
  904. ROT.Display.Backend.prototype.eventToPosition = function(x, y) {
  905. }
  906. /**
  907. * @class Rectangular backend
  908. * @private
  909. */
  910. ROT.Display.Rect = function(context) {
  911. ROT.Display.Backend.call(this, context);
  912. this._spacingX = 0;
  913. this._spacingY = 0;
  914. this._canvasCache = {};
  915. this._options = {};
  916. }
  917. ROT.Display.Rect.extend(ROT.Display.Backend);
  918. ROT.Display.Rect.cache = false;
  919. ROT.Display.Rect.prototype.compute = function(options) {
  920. this._canvasCache = {};
  921. this._options = options;
  922. var charWidth = Math.ceil(this._context.measureText("W").width);
  923. this._spacingX = Math.ceil(options.spacing * charWidth);
  924. this._spacingY = Math.ceil(options.spacing * options.fontSize);
  925. if (this._options.forceSquareRatio) {
  926. this._spacingX = this._spacingY = Math.max(this._spacingX, this._spacingY);
  927. }
  928. this._context.canvas.width = options.width * this._spacingX;
  929. this._context.canvas.height = options.height * this._spacingY;
  930. }
  931. ROT.Display.Rect.prototype.draw = function(data, clearBefore) {
  932. if (this.constructor.cache) {
  933. this._drawWithCache(data, clearBefore);
  934. } else {
  935. this._drawNoCache(data, clearBefore);
  936. }
  937. }
  938. ROT.Display.Rect.prototype._drawWithCache = function(data, clearBefore) {
  939. var x = data[0];
  940. var y = data[1];
  941. var ch = data[2];
  942. var fg = data[3];
  943. var bg = data[4];
  944. var hash = ""+ch+fg+bg;
  945. if (hash in this._canvasCache) {
  946. var canvas = this._canvasCache[hash];
  947. } else {
  948. var b = this._options.border;
  949. var canvas = document.createElement("canvas");
  950. var ctx = canvas.getContext("2d");
  951. canvas.width = this._spacingX;
  952. canvas.height = this._spacingY;
  953. ctx.fillStyle = bg;
  954. ctx.fillRect(b, b, canvas.width-b, canvas.height-b);
  955. if (ch) {
  956. ctx.fillStyle = fg;
  957. ctx.font = this._context.font;
  958. ctx.textAlign = "center";
  959. ctx.textBaseline = "middle";
  960. var chars = [].concat(ch);
  961. for (var i=0;i<chars.length;i++) {
  962. ctx.fillText(chars[i], this._spacingX/2, Math.ceil(this._spacingY/2));
  963. }
  964. }
  965. this._canvasCache[hash] = canvas;
  966. }
  967. this._context.drawImage(canvas, x*this._spacingX, y*this._spacingY);
  968. }
  969. ROT.Display.Rect.prototype._drawNoCache = function(data, clearBefore) {
  970. var x = data[0];
  971. var y = data[1];
  972. var ch = data[2];
  973. var fg = data[3];
  974. var bg = data[4];
  975. if (clearBefore) {
  976. var b = this._options.border;
  977. this._context.fillStyle = bg;
  978. this._context.fillRect(x*this._spacingX + b, y*this._spacingY + b, this._spacingX - b, this._spacingY - b);
  979. }
  980. if (!ch) { return; }
  981. this._context.fillStyle = fg;
  982. var chars = [].concat(ch);
  983. for (var i=0;i<chars.length;i++) {
  984. this._context.fillText(chars[i], (x+0.5) * this._spacingX, Math.ceil((y+0.5) * this._spacingY));
  985. }
  986. }
  987. ROT.Display.Rect.prototype.computeSize = function(availWidth, availHeight) {
  988. var width = Math.floor(availWidth / this._spacingX);
  989. var height = Math.floor(availHeight / this._spacingY);
  990. return [width, height];
  991. }
  992. ROT.Display.Rect.prototype.computeFontSize = function(availWidth, availHeight) {
  993. var boxWidth = Math.floor(availWidth / this._options.width);
  994. var boxHeight = Math.floor(availHeight / this._options.height);
  995. /* compute char ratio */
  996. var oldFont = this._context.font;
  997. this._context.font = "100px " + this._options.fontFamily;
  998. var width = Math.ceil(this._context.measureText("W").width);
  999. this._context.font = oldFont;
  1000. var ratio = width / 100;
  1001. var widthFraction = ratio * boxHeight / boxWidth;
  1002. if (widthFraction > 1) { /* too wide with current aspect ratio */
  1003. boxHeight = Math.floor(boxHeight / widthFraction);
  1004. }
  1005. return Math.floor(boxHeight / this._options.spacing);
  1006. }
  1007. ROT.Display.Rect.prototype.eventToPosition = function(x, y) {
  1008. return [Math.floor(x/this._spacingX), Math.floor(y/this._spacingY)];
  1009. }
  1010. /**
  1011. * @class Hexagonal backend
  1012. * @private
  1013. */
  1014. ROT.Display.Hex = function(context) {
  1015. ROT.Display.Backend.call(this, context);
  1016. this._spacingX = 0;
  1017. this._spacingY = 0;
  1018. this._hexSize = 0;
  1019. this._options = {};
  1020. }
  1021. ROT.Display.Hex.extend(ROT.Display.Backend);
  1022. ROT.Display.Hex.prototype.compute = function(options) {
  1023. this._options = options;
  1024. /* FIXME char size computation does not respect transposed hexes */
  1025. var charWidth = Math.ceil(this._context.measureText("W").width);
  1026. this._hexSize = Math.floor(options.spacing * (options.fontSize + charWidth/Math.sqrt(3)) / 2);
  1027. this._spacingX = this._hexSize * Math.sqrt(3) / 2;
  1028. this._spacingY = this._hexSize * 1.5;
  1029. if (options.transpose) {
  1030. var xprop = "height";
  1031. var yprop = "width";
  1032. } else {
  1033. var xprop = "width";
  1034. var yprop = "height";
  1035. }
  1036. this._context.canvas[xprop] = Math.ceil( (options.width + 1) * this._spacingX );
  1037. this._context.canvas[yprop] = Math.ceil( (options.height - 1) * this._spacingY + 2*this._hexSize );
  1038. }
  1039. ROT.Display.Hex.prototype.draw = function(data, clearBefore) {
  1040. var x = data[0];
  1041. var y = data[1];
  1042. var ch = data[2];
  1043. var fg = data[3];
  1044. var bg = data[4];
  1045. var px = [
  1046. (x+1) * this._spacingX,
  1047. y * this._spacingY + this._hexSize
  1048. ];
  1049. if (this._options.transpose) { px.reverse(); }
  1050. if (clearBefore) {
  1051. this._context.fillStyle = bg;
  1052. this._fill(px[0], px[1]);
  1053. }
  1054. if (!ch) { return; }
  1055. this._context.fillStyle = fg;
  1056. var chars = [].concat(ch);
  1057. for (var i=0;i<chars.length;i++) {
  1058. this._context.fillText(chars[i], px[0], Math.ceil(px[1]));
  1059. }
  1060. }
  1061. ROT.Display.Hex.prototype.computeSize = function(availWidth, availHeight) {
  1062. if (this._options.transpose) {
  1063. availWidth += availHeight;
  1064. availHeight = availWidth - availHeight;
  1065. availWidth -= availHeight;
  1066. }
  1067. var width = Math.floor(availWidth / this._spacingX) - 1;
  1068. var height = Math.floor((availHeight - 2*this._hexSize) / this._spacingY + 1);
  1069. return [width, height];
  1070. }
  1071. ROT.Display.Hex.prototype.computeFontSize = function(availWidth, availHeight) {
  1072. if (this._options.transpose) {
  1073. availWidth += availHeight;
  1074. availHeight = availWidth - availHeight;
  1075. availWidth -= availHeight;
  1076. }
  1077. var hexSizeWidth = 2*availWidth / ((this._options.width+1) * Math.sqrt(3)) - 1;
  1078. var hexSizeHeight = availHeight / (2 + 1.5*(this._options.height-1));
  1079. var hexSize = Math.min(hexSizeWidth, hexSizeHeight);
  1080. /* compute char ratio */
  1081. var oldFont = this._context.font;
  1082. this._context.font = "100px " + this._options.fontFamily;
  1083. var width = Math.ceil(this._context.measureText("W").width);
  1084. this._context.font = oldFont;
  1085. var ratio = width / 100;
  1086. hexSize = Math.floor(hexSize)+1; /* closest larger hexSize */
  1087. /* FIXME char size computation does not respect transposed hexes */
  1088. var fontSize = 2*hexSize / (this._options.spacing * (1 + ratio / Math.sqrt(3)));
  1089. /* closest smaller fontSize */
  1090. return Math.ceil(fontSize)-1;
  1091. }
  1092. ROT.Display.Hex.prototype.eventToPosition = function(x, y) {
  1093. if (this._options.transpose) {
  1094. x += y;
  1095. y = x-y;
  1096. x -= y;
  1097. var prop = "width";
  1098. } else {
  1099. var prop = "height";
  1100. }
  1101. var size = this._context.canvas[prop] / this._options[prop];
  1102. y = Math.floor(y/size);
  1103. if (y.mod(2)) { /* odd row */
  1104. x -= this._spacingX;
  1105. x = 1 + 2*Math.floor(x/(2*this._spacingX));
  1106. } else {
  1107. x = 2*Math.floor(x/(2*this._spacingX));
  1108. }
  1109. return [x, y];
  1110. }
  1111. /**
  1112. * Arguments are pixel values. If "transposed" mode is enabled, then these two are already swapped.
  1113. */
  1114. ROT.Display.Hex.prototype._fill = function(cx, cy) {
  1115. var a = this._hexSize;
  1116. var b = this._options.border;
  1117. this._context.beginPath();
  1118. if (this._options.transpose) {
  1119. this._context.moveTo(cx-a+b, cy);
  1120. this._context.lineTo(cx-a/2+b, cy+this._spacingX-b);
  1121. this._context.lineTo(cx+a/2-b, cy+this._spacingX-b);
  1122. this._context.lineTo(cx+a-b, cy);
  1123. this._context.lineTo(cx+a/2-b, cy-this._spacingX+b);
  1124. this._context.lineTo(cx-a/2+b, cy-this._spacingX+b);
  1125. this._context.lineTo(cx-a+b, cy);
  1126. } else {
  1127. this._context.moveTo(cx, cy-a+b);
  1128. this._context.lineTo(cx+this._spacingX-b, cy-a/2+b);
  1129. this._context.lineTo(cx+this._spacingX-b, cy+a/2-b);
  1130. this._context.lineTo(cx, cy+a-b);
  1131. this._context.lineTo(cx-this._spacingX+b, cy+a/2-b);
  1132. this._context.lineTo(cx-this._spacingX+b, cy-a/2+b);
  1133. this._context.lineTo(cx, cy-a+b);
  1134. }
  1135. this._context.fill();
  1136. }
  1137. /**
  1138. * @class Tile backend
  1139. * @private
  1140. */
  1141. ROT.Display.Tile = function(context) {
  1142. ROT.Display.Rect.call(this, context);
  1143. this._options = {};
  1144. this._colorCanvas = document.createElement("canvas");
  1145. }
  1146. ROT.Display.Tile.extend(ROT.Display.Rect);
  1147. ROT.Display.Tile.prototype.compute = function(options) {
  1148. this._options = options;
  1149. this._context.canvas.width = options.width * options.tileWidth;
  1150. this._context.canvas.height = options.height * options.tileHeight;
  1151. this._colorCanvas.width = options.tileWidth;
  1152. this._colorCanvas.height = options.tileHeight;
  1153. }
  1154. ROT.Display.Tile.prototype.draw = function(data, clearBefore) {
  1155. var x = data[0];
  1156. var y = data[1];
  1157. var ch = data[2];
  1158. var fg = data[3];
  1159. var bg = data[4];
  1160. var tileWidth = this._options.tileWidth;
  1161. var tileHeight = this._options.tileHeight;
  1162. if (clearBefore) {
  1163. if (this._options.tileColorize) {
  1164. this._context.clearRect(x*tileWidth, y*tileHeight, tileWidth, tileHeight);
  1165. } else {
  1166. this._context.fillStyle = bg;
  1167. this._context.fillRect(x*tileWidth, y*tileHeight, tileWidth, tileHeight);
  1168. }
  1169. }
  1170. if (!ch) { return; }
  1171. var chars = [].concat(ch);
  1172. for (var i=0;i<chars.length;i++) {
  1173. var tile = this._options.tileMap[chars[i]];
  1174. if (!tile) { throw new Error("Char '" + chars[i] + "' not found in tileMap"); }
  1175. if (this._options.tileColorize) { /* apply colorization */
  1176. var canvas = this._colorCanvas;
  1177. var context = canvas.getContext("2d");
  1178. context.clearRect(0, 0, tileWidth, tileHeight);
  1179. context.drawImage(
  1180. this._options.tileSet,
  1181. tile[0], tile[1], tileWidth, tileHeight,
  1182. 0, 0, tileWidth, tileHeight
  1183. );
  1184. if (fg != "transparent") {
  1185. context.fillStyle = fg;
  1186. context.globalCompositeOperation = "source-atop";
  1187. context.fillRect(0, 0, tileWidth, tileHeight);
  1188. }
  1189. if (bg != "transparent") {
  1190. context.fillStyle = bg;
  1191. context.globalCompositeOperation = "destination-over";
  1192. context.fillRect(0, 0, tileWidth, tileHeight);
  1193. }
  1194. this._context.drawImage(canvas, x*tileWidth, y*tileHeight, tileWidth, tileHeight);
  1195. } else { /* no colorizing, easy */
  1196. this._context.drawImage(
  1197. this._options.tileSet,
  1198. tile[0], tile[1], tileWidth, tileHeight,
  1199. x*tileWidth, y*tileHeight, tileWidth, tileHeight
  1200. );
  1201. }
  1202. }
  1203. }
  1204. ROT.Display.Tile.prototype.computeSize = function(availWidth, availHeight) {
  1205. var width = Math.floor(availWidth / this._options.tileWidth);
  1206. var height = Math.floor(availHeight / this._options.tileHeight);
  1207. return [width, height];
  1208. }
  1209. ROT.Display.Tile.prototype.computeFontSize = function(availWidth, availHeight) {
  1210. var width = Math.floor(availWidth / this._options.width);
  1211. var height = Math.floor(availHeight / this._options.height);
  1212. return [width, height];
  1213. }
  1214. ROT.Display.Tile.prototype.eventToPosition = function(x, y) {
  1215. return [Math.floor(x/this._options.tileWidth), Math.floor(y/this._options.tileHeight)];
  1216. }
  1217. /**
  1218. * @namespace
  1219. * This code is an implementation of Alea algorithm; (C) 2010 Johannes Baagøe.
  1220. * Alea is licensed according to the http://en.wikipedia.org/wiki/MIT_License.
  1221. */
  1222. ROT.RNG = {
  1223. /**
  1224. * @returns {number}
  1225. */
  1226. getSeed: function() {
  1227. return this._seed;
  1228. },
  1229. /**
  1230. * @param {number} seed Seed the number generator
  1231. */
  1232. setSeed: function(seed) {
  1233. seed = (seed < 1 ? 1/seed : seed);
  1234. this._seed = seed;
  1235. this._s0 = (seed >>> 0) * this._frac;
  1236. seed = (seed*69069 + 1) >>> 0;
  1237. this._s1 = seed * this._frac;
  1238. seed = (seed*69069 + 1) >>> 0;
  1239. this._s2 = seed * this._frac;
  1240. this._c = 1;
  1241. return this;
  1242. },
  1243. /**
  1244. * @returns {float} Pseudorandom value [0,1), uniformly distributed
  1245. */
  1246. getUniform: function() {
  1247. var t = 2091639 * this._s0 + this._c * this._frac;
  1248. this._s0 = this._s1;
  1249. this._s1 = this._s2;
  1250. this._c = t | 0;
  1251. this._s2 = t - this._c;
  1252. return this._s2;
  1253. },
  1254. /**
  1255. * @param {int} lowerBound The lower end of the range to return a value from, inclusive
  1256. * @param {int} upperBound The upper end of the range to return a value from, inclusive
  1257. * @returns {int} Pseudorandom value [lowerBound, upperBound], using ROT.RNG.getUniform() to distribute the value
  1258. */
  1259. getUniformInt: function(lowerBound, upperBound) {
  1260. var max = Math.max(lowerBound, upperBound);
  1261. var min = Math.min(lowerBound, upperBound);
  1262. return Math.floor(this.getUniform() * (max - min + 1)) + min;
  1263. },
  1264. /**
  1265. * @param {float} [mean=0] Mean value
  1266. * @param {float} [stddev=1] Standard deviation. ~95% of the absolute values will be lower than 2*stddev.
  1267. * @returns {float} A normally distributed pseudorandom value
  1268. */
  1269. getNormal: function(mean, stddev) {
  1270. do {
  1271. var u = 2*this.getUniform()-1;
  1272. var v = 2*this.getUniform()-1;
  1273. var r = u*u + v*v;
  1274. } while (r > 1 || r == 0);
  1275. var gauss = u * Math.sqrt(-2*Math.log(r)/r);
  1276. return (mean || 0) + gauss*(stddev || 1);
  1277. },
  1278. /**
  1279. * @returns {int} Pseudorandom value [1,100] inclusive, uniformly distributed
  1280. */
  1281. getPercentage: function() {
  1282. return 1 + Math.floor(this.getUniform()*100);
  1283. },
  1284. /**
  1285. * @param {object} data key=whatever, value=weight (relative probability)
  1286. * @returns {string} whatever
  1287. */
  1288. getWeightedValue: function(data) {
  1289. var total = 0;
  1290. for (var id in data) {
  1291. total += data[id];
  1292. }
  1293. var random = this.getUniform()*total;
  1294. var part = 0;
  1295. for (var id in data) {
  1296. part += data[id];
  1297. if (random < part) { return id; }
  1298. }
  1299. // If by some floating-point annoyance we have
  1300. // random >= total, just return the last id.
  1301. return id;
  1302. },
  1303. /**
  1304. * Get RNG state. Useful for storing the state and re-setting it via setState.
  1305. * @returns {?} Internal state
  1306. */
  1307. getState: function() {
  1308. return [this._s0, this._s1, this._s2, this._c];
  1309. },
  1310. /**
  1311. * Set a previously retrieved state.
  1312. * @param {?} state
  1313. */
  1314. setState: function(state) {
  1315. this._s0 = state[0];
  1316. this._s1 = state[1];
  1317. this._s2 = state[2];
  1318. this._c = state[3];
  1319. return this;
  1320. },
  1321. /**
  1322. * Returns a cloned RNG
  1323. */
  1324. clone: function() {
  1325. var clone = Object.create(this);
  1326. clone.setState(this.getState());
  1327. return clone;
  1328. },
  1329. _s0: 0,
  1330. _s1: 0,
  1331. _s2: 0,
  1332. _c: 0,
  1333. _frac: 2.3283064365386963e-10 /* 2^-32 */
  1334. }
  1335. ROT.RNG.setSeed(Date.now());
  1336. /**
  1337. * @class (Markov process)-based string generator.
  1338. * Copied from a <a href="http://www.roguebasin.roguelikedevelopment.org/index.php?title=Names_from_a_high_order_Markov_Process_and_a_simplified_Katz_back-off_scheme">RogueBasin article</a>.
  1339. * Offers configurable order and prior.
  1340. * @param {object} [options]
  1341. * @param {bool} [options.words=false] Use word mode?
  1342. * @param {int} [options.order=3]
  1343. * @param {float} [options.prior=0.001]
  1344. */
  1345. ROT.StringGenerator = function(options) {
  1346. this._options = {
  1347. words: false,
  1348. order: 3,
  1349. prior: 0.001
  1350. }
  1351. for (var p in options) { this._options[p] = options[p]; }
  1352. this._boundary = String.fromCharCode(0);
  1353. this._suffix = this._boundary;
  1354. this._prefix = [];
  1355. for (var i=0;i<this._options.order;i++) { this._prefix.push(this._boundary); }
  1356. this._priorValues = {};
  1357. this._priorValues[this._boundary] = this._options.prior;
  1358. this._data = {};
  1359. }
  1360. /**
  1361. * Remove all learning data
  1362. */
  1363. ROT.StringGenerator.prototype.clear = function() {
  1364. this._data = {};
  1365. this._priorValues = {};
  1366. }
  1367. /**
  1368. * @returns {string} Generated string
  1369. */
  1370. ROT.StringGenerator.prototype.generate = function() {
  1371. var result = [this._sample(this._prefix)];
  1372. while (result[result.length-1] != this._boundary) {
  1373. result.push(this._sample(result));
  1374. }
  1375. return this._join(result.slice(0, -1));
  1376. }
  1377. /**
  1378. * Observe (learn) a string from a training set
  1379. */
  1380. ROT.StringGenerator.prototype.observe = function(string) {
  1381. var tokens = this._split(string);
  1382. for (var i=0; i<tokens.length; i++) {
  1383. this._priorValues[tokens[i]] = this._options.prior;
  1384. }
  1385. tokens = this._prefix.concat(tokens).concat(this._suffix); /* add boundary symbols */
  1386. for (var i=this._options.order; i<tokens.length; i++) {
  1387. var context = tokens.slice(i-this._options.order, i);
  1388. var event = tokens[i];
  1389. for (var j=0; j<context.length; j++) {
  1390. var subcontext = context.slice(j);
  1391. this._observeEvent(subcontext, event);
  1392. }
  1393. }
  1394. }
  1395. ROT.StringGenerator.prototype.getStats = function() {
  1396. var parts = [];
  1397. var priorCount = 0;
  1398. for (var p in this._priorValues) { priorCount++; }
  1399. priorCount--; /* boundary */
  1400. parts.push("distinct samples: " + priorCount);
  1401. var dataCount = 0;
  1402. var eventCount = 0;
  1403. for (var p in this._data) {
  1404. dataCount++;
  1405. for (var key in this._data[p]) {
  1406. eventCount++;
  1407. }
  1408. }
  1409. parts.push("dictionary size (contexts): " + dataCount);
  1410. parts.push("dictionary size (events): " + eventCount);
  1411. return parts.join(", ");
  1412. }
  1413. /**
  1414. * @param {string}
  1415. * @returns {string[]}
  1416. */
  1417. ROT.StringGenerator.prototype._split = function(str) {
  1418. return str.split(this._options.words ? /\s+/ : "");
  1419. }
  1420. /**
  1421. * @param {string[]}
  1422. * @returns {string}
  1423. */
  1424. ROT.StringGenerator.prototype._join = function(arr) {
  1425. return arr.join(this._options.words ? " " : "");
  1426. }
  1427. /**
  1428. * @param {string[]} context
  1429. * @param {string} event
  1430. */
  1431. ROT.StringGenerator.prototype._observeEvent = function(context, event) {
  1432. var key = this._join(context);
  1433. if (!(key in this._data)) { this._data[key] = {}; }
  1434. var data = this._data[key];
  1435. if (!(event in data)) { data[event] = 0; }
  1436. data[event]++;
  1437. }
  1438. /**
  1439. * @param {string[]}
  1440. * @returns {string}
  1441. */
  1442. ROT.StringGenerator.prototype._sample = function(context) {
  1443. context = this._backoff(context);
  1444. var key = this._join(context);
  1445. var data = this._data[key];
  1446. var available = {};
  1447. if (this._options.prior) {
  1448. for (var event in this._priorValues) { available[event] = this._priorValues[event]; }
  1449. for (var event in data) { available[event] += data[event]; }
  1450. } else {
  1451. available = data;
  1452. }
  1453. return ROT.RNG.getWeightedValue(available);
  1454. }
  1455. /**
  1456. * @param {string[]}
  1457. * @returns {string[]}
  1458. */
  1459. ROT.StringGenerator.prototype._backoff = function(context) {
  1460. if (context.length > this._options.order) {
  1461. context = context.slice(-this._options.order);
  1462. } else if (context.length < this._options.order) {
  1463. context = this._prefix.slice(0, this._options.order - context.length).concat(context);
  1464. }
  1465. while (!(this._join(context) in this._data) && context.length > 0) { context = context.slice(1); }
  1466. return context;
  1467. }
  1468. /**
  1469. * @class Generic event queue: stores events and retrieves them based on their time
  1470. */
  1471. ROT.EventQueue = function() {
  1472. this._time = 0;
  1473. this._events = [];
  1474. this._eventTimes = [];
  1475. }
  1476. /**
  1477. * @returns {number} Elapsed time
  1478. */
  1479. ROT.EventQueue.prototype.getTime = function() {
  1480. return this._time;
  1481. }
  1482. /**
  1483. * Clear all scheduled events
  1484. */
  1485. ROT.EventQueue.prototype.clear = function() {
  1486. this._events = [];
  1487. this._eventTimes = [];
  1488. return this;
  1489. }
  1490. /**
  1491. * @param {?} event
  1492. * @param {number} time
  1493. */
  1494. ROT.EventQueue.prototype.add = function(event, time) {
  1495. var index = this._events.length;
  1496. for (var i=0;i<this._eventTimes.length;i++) {
  1497. if (this._eventTimes[i] > time) {
  1498. index = i;
  1499. break;
  1500. }
  1501. }
  1502. this._events.splice(index, 0, event);
  1503. this._eventTimes.splice(index, 0, time);
  1504. }
  1505. /**
  1506. * Locates the nearest event, advances time if necessary. Returns that event and removes it from the queue.
  1507. * @returns {? || null} The event previously added by addEvent, null if no event available
  1508. */
  1509. ROT.EventQueue.prototype.get = function() {
  1510. if (!this._events.length) { return null; }
  1511. var time = this._eventTimes.splice(0, 1)[0];
  1512. if (time > 0) { /* advance */
  1513. this._time += time;
  1514. for (var i=0;i<this._eventTimes.length;i++) { this._eventTimes[i] -= time; }
  1515. }
  1516. return this._events.splice(0, 1)[0];
  1517. }
  1518. /**
  1519. * Remove an event from the queue
  1520. * @param {?} event
  1521. * @returns {bool} success?
  1522. */
  1523. ROT.EventQueue.prototype.remove = function(event) {
  1524. var index = this._events.indexOf(event);
  1525. if (index == -1) { return false }
  1526. this._remove(index);
  1527. return true;
  1528. }
  1529. /**
  1530. * Remove an event from the queue
  1531. * @param {int} index
  1532. */
  1533. ROT.EventQueue.prototype._remove = function(index) {
  1534. this._events.splice(index, 1);
  1535. this._eventTimes.splice(index, 1);
  1536. }
  1537. /**
  1538. * @class Abstract scheduler
  1539. */
  1540. ROT.Scheduler = function() {
  1541. this._queue = new ROT.EventQueue();
  1542. this._repeat = [];
  1543. this._current = null;
  1544. }
  1545. /**
  1546. * @see ROT.EventQueue#getTime
  1547. */
  1548. ROT.Scheduler.prototype.getTime = function() {
  1549. return this._queue.getTime();
  1550. }
  1551. /**
  1552. * @param {?} item
  1553. * @param {bool} repeat
  1554. */
  1555. ROT.Scheduler.prototype.add = function(item, repeat) {
  1556. if (repeat) { this._repeat.push(item); }
  1557. return this;
  1558. }
  1559. /**
  1560. * Clear all items
  1561. */
  1562. ROT.Scheduler.prototype.clear = function() {
  1563. this._queue.clear();
  1564. this._repeat = [];
  1565. this._current = null;
  1566. return this;
  1567. }
  1568. /**
  1569. * Remove a previously added item
  1570. * @param {?} item
  1571. * @returns {bool} successful?
  1572. */
  1573. ROT.Scheduler.prototype.remove = function(item) {
  1574. var result = this._queue.remove(item);
  1575. var index = this._repeat.indexOf(item);
  1576. if (index != -1) { this._repeat.splice(index, 1); }
  1577. if (this._current == item) { this._current = null; }
  1578. return result;
  1579. }
  1580. /**
  1581. * Schedule next item
  1582. * @returns {?}
  1583. */
  1584. ROT.Scheduler.prototype.next = function() {
  1585. this._current = this._queue.get();
  1586. return this._current;
  1587. }
  1588. /**
  1589. * @class Simple fair scheduler (round-robin style)
  1590. * @augments ROT.Scheduler
  1591. */
  1592. ROT.Scheduler.Simple = function() {
  1593. ROT.Scheduler.call(this);
  1594. }
  1595. ROT.Scheduler.Simple.extend(ROT.Scheduler);
  1596. /**
  1597. * @see ROT.Scheduler#add
  1598. */
  1599. ROT.Scheduler.Simple.prototype.add = function(item, repeat) {
  1600. this._queue.add(item, 0);
  1601. return ROT.Scheduler.prototype.add.call(this, item, repeat);
  1602. }
  1603. /**
  1604. * @see ROT.Scheduler#next
  1605. */
  1606. ROT.Scheduler.Simple.prototype.next = function() {
  1607. if (this._current && this._repeat.indexOf(this._current) != -1) {
  1608. this._queue.add(this._current, 0);
  1609. }
  1610. return ROT.Scheduler.prototype.next.call(this);
  1611. }
  1612. /**
  1613. * @class Speed-based scheduler
  1614. * @augments ROT.Scheduler
  1615. */
  1616. ROT.Scheduler.Speed = function() {
  1617. ROT.Scheduler.call(this);
  1618. }
  1619. ROT.Scheduler.Speed.extend(ROT.Scheduler);
  1620. /**
  1621. * @param {object} item anything with "getSpeed" method
  1622. * @param {bool} repeat
  1623. * @see ROT.Scheduler#add
  1624. */
  1625. ROT.Scheduler.Speed.prototype.add = function(item, repeat) {
  1626. this._queue.add(item, 1/item.getSpeed());
  1627. return ROT.Scheduler.prototype.add.call(this, item, repeat);
  1628. }
  1629. /**
  1630. * @see ROT.Scheduler#next
  1631. */
  1632. ROT.Scheduler.Speed.prototype.next = function() {
  1633. if (this._current && this._repeat.indexOf(this._current) != -1) {
  1634. this._queue.add(this._current, 1/this._current.getSpeed());
  1635. }
  1636. return ROT.Scheduler.prototype.next.call(this);
  1637. }
  1638. /**
  1639. * @class Action-based scheduler
  1640. * @augments ROT.Scheduler
  1641. */
  1642. ROT.Scheduler.Action = function() {
  1643. ROT.Scheduler.call(this);
  1644. this._defaultDuration = 1; /* for newly added */
  1645. this._duration = this._defaultDuration; /* for this._current */
  1646. }
  1647. ROT.Scheduler.Action.extend(ROT.Scheduler);
  1648. /**
  1649. * @param {object} item
  1650. * @param {bool} repeat
  1651. * @param {number} [time=1]
  1652. * @see ROT.Scheduler#add
  1653. */
  1654. ROT.Scheduler.Action.prototype.add = function(item, repeat, time) {
  1655. this._queue.add(item, time || this._defaultDuration);
  1656. return ROT.Scheduler.prototype.add.call(this, item, repeat);
  1657. }
  1658. ROT.Scheduler.Action.prototype.clear = function() {
  1659. this._duration = this._defaultDuration;
  1660. return ROT.Scheduler.prototype.clear.call(this);
  1661. }
  1662. ROT.Scheduler.Action.prototype.remove = function(item) {
  1663. if (item == this._current) { this._duration = this._defaultDuration; }
  1664. return ROT.Scheduler.prototype.remove.call(this, item);
  1665. }
  1666. /**
  1667. * @see ROT.Scheduler#next
  1668. */
  1669. ROT.Scheduler.Action.prototype.next = function() {
  1670. if (this._current && this._repeat.indexOf(this._current) != -1) {
  1671. this._queue.add(this._current, this._duration || this._defaultDuration);
  1672. this._duration = this._defaultDuration;
  1673. }
  1674. return ROT.Scheduler.prototype.next.call(this);
  1675. }
  1676. /**
  1677. * Set duration for the active item
  1678. */
  1679. ROT.Scheduler.Action.prototype.setDuration = function(time) {
  1680. if (this._current) { this._duration = time; }
  1681. return this;
  1682. }
  1683. /**
  1684. * @class Asynchronous main loop
  1685. * @param {ROT.Scheduler} scheduler
  1686. */
  1687. ROT.Engine = function(scheduler) {
  1688. this._scheduler = scheduler;
  1689. this._lock = 1;
  1690. }
  1691. /**
  1692. * Start the main loop. When this call returns, the loop is locked.
  1693. */
  1694. ROT.Engine.prototype.start = function() {
  1695. return this.unlock();
  1696. }
  1697. /**
  1698. * Interrupt the engine by an asynchronous action
  1699. */
  1700. ROT.Engine.prototype.lock = function() {
  1701. this._lock++;
  1702. return this;
  1703. }
  1704. /**
  1705. * Resume execution (paused by a previous lock)
  1706. */
  1707. ROT.Engine.prototype.unlock = function() {
  1708. if (!this._lock) { throw new Error("Cannot unlock unlocked engine"); }
  1709. this._lock--;
  1710. while (!this._lock) {
  1711. var actor = this._scheduler.next();
  1712. if (!actor) { return this.lock(); } /* no actors */
  1713. var result = actor.act();
  1714. if (result && result.then) { /* actor returned a "thenable", looks like a Promise */
  1715. this.lock();
  1716. result.then(this.unlock.bind(this));
  1717. }
  1718. }
  1719. return this;
  1720. }
  1721. /**
  1722. * @class Base map generator
  1723. * @param {int} [width=ROT.DEFAULT_WIDTH]
  1724. * @param {int} [height=ROT.DEFAULT_HEIGHT]
  1725. */
  1726. ROT.Map = function(width, height) {
  1727. this._width = width || ROT.DEFAULT_WIDTH;
  1728. this._height = height || ROT.DEFAULT_HEIGHT;
  1729. };
  1730. ROT.Map.prototype.create = function(callback) {}
  1731. ROT.Map.prototype._fillMap = function(value) {
  1732. var map = [];
  1733. for (var i=0;i<this._width;i++) {
  1734. map.push([]);
  1735. for (var j=0;j<this._height;j++) { map[i].push(value); }
  1736. }
  1737. return map;
  1738. }
  1739. /**
  1740. * @class Simple empty rectangular room
  1741. * @augments ROT.Map
  1742. */
  1743. ROT.Map.Arena = function(width, height) {
  1744. ROT.Map.call(this, width, height);
  1745. }
  1746. ROT.Map.Arena.extend(ROT.Map);
  1747. ROT.Map.Arena.prototype.create = function(callback) {
  1748. var w = this._width-1;
  1749. var h = this._height-1;
  1750. for (var i=0;i<=w;i++) {
  1751. for (var j=0;j<=h;j++) {
  1752. var empty = (i && j && i<w && j<h);
  1753. callback(i, j, empty ? 0 : 1);
  1754. }
  1755. }
  1756. return this;
  1757. }
  1758. /**
  1759. * @class Recursively divided maze, http://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_division_method
  1760. * @augments ROT.Map
  1761. */
  1762. ROT.Map.DividedMaze = function(width, height) {
  1763. ROT.Map.call(this, width, height);
  1764. this._stack = [];
  1765. }
  1766. ROT.Map.DividedMaze.extend(ROT.Map);
  1767. ROT.Map.DividedMaze.prototype.create = function(callback) {
  1768. var w = this._width;
  1769. var h = this._height;
  1770. this._map = [];
  1771. for (var i=0;i<w;i++) {
  1772. this._map.push([]);
  1773. for (var j=0;j<h;j++) {
  1774. var border = (i == 0 || j == 0 || i+1 == w || j+1 == h);
  1775. this._map[i].push(border ? 1 : 0);
  1776. }
  1777. }
  1778. this._stack = [
  1779. [1, 1, w-2, h-2]
  1780. ];
  1781. this._process();
  1782. for (var i=0;i<w;i++) {
  1783. for (var j=0;j<h;j++) {
  1784. callback(i, j, this._map[i][j]);
  1785. }
  1786. }
  1787. this._map = null;
  1788. return this;
  1789. }
  1790. ROT.Map.DividedMaze.prototype._process = function() {
  1791. while (this._stack.length) {
  1792. var room = this._stack.shift(); /* [left, top, right, bottom] */
  1793. this._partitionRoom(room);
  1794. }
  1795. }
  1796. ROT.Map.DividedMaze.prototype._partitionRoom = function(room) {
  1797. var availX = [];
  1798. var availY = [];
  1799. for (var i=room[0]+1;i<room[2];i++) {
  1800. var top = this._map[i][room[1]-1];
  1801. var bottom = this._map[i][room[3]+1];
  1802. if (top && bottom && !(i % 2)) { availX.push(i); }
  1803. }
  1804. for (var j=room[1]+1;j<room[3];j++) {
  1805. var left = this._map[room[0]-1][j];
  1806. var right = this._map[room[2]+1][j];
  1807. if (left && right && !(j % 2)) { availY.push(j); }
  1808. }
  1809. if (!availX.length || !availY.length) { return; }
  1810. var x = availX.random();
  1811. var y = availY.random();
  1812. this._map[x][y] = 1;
  1813. var walls = [];
  1814. var w = []; walls.push(w); /* left part */
  1815. for (var i=room[0]; i<x; i++) {
  1816. this._map[i][y] = 1;
  1817. w.push([i, y]);
  1818. }
  1819. var w = []; walls.push(w); /* right part */
  1820. for (var i=x+1; i<=room[2]; i++) {
  1821. this._map[i][y] = 1;
  1822. w.push([i, y]);
  1823. }
  1824. var w = []; walls.push(w); /* top part */
  1825. for (var j=room[1]; j<y; j++) {
  1826. this._map[x][j] = 1;
  1827. w.push([x, j]);
  1828. }
  1829. var w = []; walls.push(w); /* bottom part */
  1830. for (var j=y+1; j<=room[3]; j++) {
  1831. this._map[x][j] = 1;
  1832. w.push([x, j]);
  1833. }
  1834. var solid = walls.random();
  1835. for (var i=0;i<walls.length;i++) {
  1836. var w = walls[i];
  1837. if (w == solid) { continue; }
  1838. var hole = w.random();
  1839. this._map[hole[0]][hole[1]] = 0;
  1840. }
  1841. this._stack.push([room[0], room[1], x-1, y-1]); /* left top */
  1842. this._stack.push([x+1, room[1], room[2], y-1]); /* right top */
  1843. this._stack.push([room[0], y+1, x-1, room[3]]); /* left bottom */
  1844. this._stack.push([x+1, y+1, room[2], room[3]]); /* right bottom */
  1845. }
  1846. /**
  1847. * @class Icey's Maze generator
  1848. * See http://www.roguebasin.roguelikedevelopment.org/index.php?title=Simple_maze for explanation
  1849. * @augments ROT.Map
  1850. */
  1851. ROT.Map.IceyMaze = function(width, height, regularity) {
  1852. ROT.Map.call(this, width, height);
  1853. this._regularity = regularity || 0;
  1854. }
  1855. ROT.Map.IceyMaze.extend(ROT.Map);
  1856. ROT.Map.IceyMaze.prototype.create = function(callback) {
  1857. var width = this._width;
  1858. var height = this._height;
  1859. var map = this._fillMap(1);
  1860. width -= (width % 2 ? 1 : 2);
  1861. height -= (height % 2 ? 1 : 2);
  1862. var cx = 0;
  1863. var cy = 0;
  1864. var nx = 0;
  1865. var ny = 0;
  1866. var done = 0;
  1867. var blocked = false;
  1868. var dirs = [
  1869. [0, 0],
  1870. [0, 0],
  1871. [0, 0],
  1872. [0, 0]
  1873. ];
  1874. do {
  1875. cx = 1 + 2*Math.floor(ROT.RNG.getUniform()*(width-1) / 2);
  1876. cy = 1 + 2*Math.floor(ROT.RNG.getUniform()*(height-1) / 2);
  1877. if (!done) { map[cx][cy] = 0; }
  1878. if (!map[cx][cy]) {
  1879. this._randomize(dirs);
  1880. do {
  1881. if (Math.floor(ROT.RNG.getUniform()*(this._regularity+1)) == 0) { this._randomize(dirs); }
  1882. blocked = true;
  1883. for (var i=0;i<4;i++) {
  1884. nx = cx + dirs[i][0]*2;
  1885. ny = cy + dirs[i][1]*2;
  1886. if (this._isFree(map, nx, ny, width, height)) {
  1887. map[nx][ny] = 0;
  1888. map[cx + dirs[i][0]][cy + dirs[i][1]] = 0;
  1889. cx = nx;
  1890. cy = ny;
  1891. blocked = false;
  1892. done++;
  1893. break;
  1894. }
  1895. }
  1896. } while (!blocked);
  1897. }
  1898. } while (done+1 < width*height/4);
  1899. for (var i=0;i<this._width;i++) {
  1900. for (var j=0;j<this._height;j++) {
  1901. callback(i, j, map[i][j]);
  1902. }
  1903. }
  1904. this._map = null;
  1905. return this;
  1906. }
  1907. ROT.Map.IceyMaze.prototype._randomize = function(dirs) {
  1908. for (var i=0;i<4;i++) {
  1909. dirs[i][0] = 0;
  1910. dirs[i][1] = 0;
  1911. }
  1912. switch (Math.floor(ROT.RNG.getUniform()*4)) {
  1913. case 0:
  1914. dirs[0][0] = -1; dirs[1][0] = 1;
  1915. dirs[2][1] = -1; dirs[3][1] = 1;
  1916. break;
  1917. case 1:
  1918. dirs[3][0] = -1; dirs[2][0] = 1;
  1919. dirs[1][1] = -1; dirs[0][1] = 1;
  1920. break;
  1921. case 2:
  1922. dirs[2][0] = -1; dirs[3][0] = 1;
  1923. dirs[0][1] = -1; dirs[1][1] = 1;
  1924. break;
  1925. case 3:
  1926. dirs[1][0] = -1; dirs[0][0] = 1;
  1927. dirs[3][1] = -1; dirs[2][1] = 1;
  1928. break;
  1929. }
  1930. }
  1931. ROT.Map.IceyMaze.prototype._isFree = function(map, x, y, width, height) {
  1932. if (x < 1 || y < 1 || x >= width || y >= height) { return false; }
  1933. return map[x][y];
  1934. }
  1935. /**
  1936. * @class Maze generator - Eller's algorithm
  1937. * See http://homepages.cwi.nl/~tromp/maze.html for explanation
  1938. * @augments ROT.Map
  1939. */
  1940. ROT.Map.EllerMaze = function(width, height) {
  1941. ROT.Map.call(this, width, height);
  1942. }
  1943. ROT.Map.EllerMaze.extend(ROT.Map);
  1944. ROT.Map.EllerMaze.prototype.create = function(callback) {
  1945. var map = this._fillMap(1);
  1946. var w = Math.ceil((this._width-2)/2);
  1947. var rand = 9/24;
  1948. var L = [];
  1949. var R = [];
  1950. for (var i=0;i<w;i++) {
  1951. L.push(i);
  1952. R.push(i);
  1953. }
  1954. L.push(w-1); /* fake stop-block at the right side */
  1955. for (var j=1;j+3<this._height;j+=2) {
  1956. /* one row */
  1957. for (var i=0;i<w;i++) {
  1958. /* cell coords (will be always empty) */
  1959. var x = 2*i+1;
  1960. var y = j;
  1961. map[x][y] = 0;
  1962. /* right connection */
  1963. if (i != L[i+1] && ROT.RNG.getUniform() > rand) {
  1964. this._addToList(i, L, R);
  1965. map[x+1][y] = 0;
  1966. }
  1967. /* bottom connection */
  1968. if (i != L[i] && ROT.RNG.getUniform() > rand) {
  1969. /* remove connection */
  1970. this._removeFromList(i, L, R);
  1971. } else {
  1972. /* create connection */
  1973. map[x][y+1] = 0;
  1974. }
  1975. }
  1976. }
  1977. /* last row */
  1978. for (var i=0;i<w;i++) {
  1979. /* cell coords (will be always empty) */
  1980. var x = 2*i+1;
  1981. var y = j;
  1982. map[x][y] = 0;
  1983. /* right connection */
  1984. if (i != L[i+1] && (i == L[i] || ROT.RNG.getUniform() > rand)) {
  1985. /* dig right also if the cell is separated, so it gets connected to the rest of maze */
  1986. this._addToList(i, L, R);
  1987. map[x+1][y] = 0;
  1988. }
  1989. this._removeFromList(i, L, R);
  1990. }
  1991. for (var i=0;i<this._width;i++) {
  1992. for (var j=0;j<this._height;j++) {
  1993. callback(i, j, map[i][j]);
  1994. }
  1995. }
  1996. return this;
  1997. }
  1998. /**
  1999. * Remove "i" from its list
  2000. */
  2001. ROT.Map.EllerMaze.prototype._removeFromList = function(i, L, R) {
  2002. R[L[i]] = R[i];
  2003. L[R[i]] = L[i];
  2004. R[i] = i;
  2005. L[i] = i;
  2006. }
  2007. /**
  2008. * Join lists with "i" and "i+1"
  2009. */
  2010. ROT.Map.EllerMaze.prototype._addToList = function(i, L, R) {
  2011. R[L[i+1]] = R[i];
  2012. L[R[i]] = L[i+1];
  2013. R[i] = i+1;
  2014. L[i+1] = i;
  2015. }
  2016. /**
  2017. * @class Cellular automaton map generator
  2018. * @augments ROT.Map
  2019. * @param {int} [width=ROT.DEFAULT_WIDTH]
  2020. * @param {int} [height=ROT.DEFAULT_HEIGHT]
  2021. * @param {object} [options] Options
  2022. * @param {int[]} [options.born] List of neighbor counts for a new cell to be born in empty space
  2023. * @param {int[]} [options.survive] List of neighbor counts for an existing cell to survive
  2024. * @param {int} [options.topology] Topology 4 or 6 or 8
  2025. */
  2026. ROT.Map.Cellular = function(width, height, options) {
  2027. ROT.Map.call(this, width, height);
  2028. this._options = {
  2029. born: [5, 6, 7, 8],
  2030. survive: [4, 5, 6, 7, 8],
  2031. topology: 8,
  2032. connected: false
  2033. };
  2034. this.setOptions(options);
  2035. this._dirs = ROT.DIRS[this._options.topology];
  2036. this._map = this._fillMap(0);
  2037. }
  2038. ROT.Map.Cellular.extend(ROT.Map);
  2039. /**
  2040. * Fill the map with random values
  2041. * @param {float} probability Probability for a cell to become alive; 0 = all empty, 1 = all full
  2042. */
  2043. ROT.Map.Cellular.prototype.randomize = function(probability) {
  2044. for (var i=0;i<this._width;i++) {
  2045. for (var j=0;j<this._height;j++) {
  2046. this._map[i][j] = (ROT.RNG.getUniform() < probability ? 1 : 0);
  2047. }
  2048. }
  2049. return this;
  2050. }
  2051. /**
  2052. * Change options.
  2053. * @see ROT.Map.Cellular
  2054. */
  2055. ROT.Map.Cellular.prototype.setOptions = function(options) {
  2056. for (var p in options) { this._options[p] = options[p]; }
  2057. }
  2058. ROT.Map.Cellular.prototype.set = function(x, y, value) {
  2059. this._map[x][y] = value;
  2060. }
  2061. ROT.Map.Cellular.prototype.create = function(callback) {
  2062. var newMap = this._fillMap(0);
  2063. var born = this._options.born;
  2064. var survive = this._options.survive;
  2065. for (var j=0;j<this._height;j++) {
  2066. var widthStep = 1;
  2067. var widthStart = 0;
  2068. if (this._options.topology == 6) {
  2069. widthStep = 2;
  2070. widthStart = j%2;
  2071. }
  2072. for (var i=widthStart; i<this._width; i+=widthStep) {
  2073. var cur = this._map[i][j];
  2074. var ncount = this._getNeighbors(i, j);
  2075. if (cur && survive.indexOf(ncount) != -1) { /* survive */
  2076. newMap[i][j] = 1;
  2077. } else if (!cur && born.indexOf(ncount) != -1) { /* born */
  2078. newMap[i][j] = 1;
  2079. }
  2080. }
  2081. }
  2082. this._map = newMap;
  2083. if (this._options.connected) { this._completeMaze(); } // optionally connect every space
  2084. if (!callback) { return; }
  2085. for (var j=0;j<this._height;j++) {
  2086. var widthStep = 1;
  2087. var widthStart = 0;
  2088. if (this._options.topology == 6) {
  2089. widthStep = 2;
  2090. widthStart = j%2;
  2091. }
  2092. for (var i=widthStart; i<this._width; i+=widthStep) {
  2093. callback(i, j, newMap[i][j]);
  2094. }
  2095. }
  2096. }
  2097. /**
  2098. * Get neighbor count at [i,j] in this._map
  2099. */
  2100. ROT.Map.Cellular.prototype._getNeighbors = function(cx, cy) {
  2101. var result = 0;
  2102. for (var i=0;i<this._dirs.length;i++) {
  2103. var dir = this._dirs[i];
  2104. var x = cx + dir[0];
  2105. var y = cy + dir[1];
  2106. if (x < 0 || x >= this._width || x < 0 || y >= this._width) { continue; }
  2107. result += (this._map[x][y] == 1 ? 1 : 0);
  2108. }
  2109. return result;
  2110. }
  2111. /**
  2112. * Make sure every non-wall space is accessible.
  2113. */
  2114. ROT.Map.Cellular.prototype._completeMaze = function() {
  2115. var allFreeSpace = [];
  2116. var notConnected = {};
  2117. // find all free space
  2118. for (var x = 0; x < this._width; x++) {
  2119. for (var y = 0; y < this._height; y++) {
  2120. if (this._freeSpace(x, y)) {
  2121. var p = [x, y];
  2122. notConnected[this._pointKey(p)] = p;
  2123. allFreeSpace.push([x, y]);
  2124. }
  2125. }
  2126. }
  2127. var start = allFreeSpace[ROT.RNG.getUniformInt(0, allFreeSpace.length - 1)];
  2128. var key = this._pointKey(start);
  2129. var connected = {};
  2130. connected[key] = start;
  2131. delete notConnected[key]
  2132. // find what's connected to the starting point
  2133. this._findConnected(connected, notConnected, [start]);
  2134. while (Object.keys(notConnected).length > 0) {
  2135. // find two points from notConnected to connected
  2136. var p = this._getFromTo(connected, notConnected);
  2137. var from = p[0]; // notConnected
  2138. var to = p[1]; // connected
  2139. // find everything connected to the starting point
  2140. var local = {};
  2141. local[this._pointKey(from)] = from;
  2142. this._findConnected(local, notConnected, [from], true);
  2143. // connect to a connected square
  2144. this._tunnelToConnected(to, from, connected, notConnected);
  2145. // now all of local is connected
  2146. for (var k in local) {
  2147. var pp = local[k];
  2148. this._map[pp[0]][pp[1]] = 0;
  2149. connected[k] = pp;
  2150. delete notConnected[k];
  2151. }
  2152. }
  2153. }
  2154. /**
  2155. * Find random points to connect. Search for the closest point in the larger space.
  2156. * This is to minimize the length of the passage while maintaining good performance.
  2157. */
  2158. ROT.Map.Cellular.prototype._getFromTo = function(connected, notConnected) {
  2159. var from, to, d;
  2160. var connectedKeys = Object.keys(connected);
  2161. var notConnectedKeys = Object.keys(notConnected);
  2162. for (var i = 0; i < 5; i++) {
  2163. if (connectedKeys.length < notConnectedKeys.length) {
  2164. var keys = connectedKeys;
  2165. to = connected[keys[ROT.RNG.getUniformInt(0, keys.length - 1)]]
  2166. from = this._getClosest(to, notConnected);
  2167. } else {
  2168. var keys = notConnectedKeys;
  2169. from = notConnected[keys[ROT.RNG.getUniformInt(0, keys.length - 1)]]
  2170. to = this._getClosest(from, connected);
  2171. }
  2172. d = (from[0] - to[0]) * (from[0] - to[0]) + (from[1] - to[1]) * (from[1] - to[1]);
  2173. if (d < 64) {
  2174. break;
  2175. }
  2176. }
  2177. // console.log(">>> connected=" + to + " notConnected=" + from + " dist=" + d);
  2178. return [from, to];
  2179. }
  2180. ROT.Map.Cellular.prototype._getClosest = function(point, space) {
  2181. var minPoint = null;
  2182. var minDist = null;
  2183. for (k in space) {
  2184. var p = space[k];
  2185. var d = (p[0] - point[0]) * (p[0] - point[0]) + (p[1] - point[1]) * (p[1] - point[1]);
  2186. if (minDist == null || d < minDist) {
  2187. minDist = d;
  2188. minPoint = p;
  2189. }
  2190. }
  2191. return minPoint;
  2192. }
  2193. ROT.Map.Cellular.prototype._findConnected = function(connected, notConnected, stack, keepNotConnected) {
  2194. while(stack.length > 0) {
  2195. var p = stack.splice(0, 1)[0];
  2196. var tests = [
  2197. [p[0] + 1, p[1]],
  2198. [p[0] - 1, p[1]],
  2199. [p[0], p[1] + 1],
  2200. [p[0], p[1] - 1]
  2201. ];
  2202. for (var i = 0; i < tests.length; i++) {
  2203. var key = this._pointKey(tests[i]);
  2204. if (connected[key] == null && this._freeSpace(tests[i][0], tests[i][1])) {
  2205. connected[key] = tests[i];
  2206. if (!keepNotConnected) {
  2207. delete notConnected[key];
  2208. }
  2209. stack.push(tests[i]);
  2210. }
  2211. }
  2212. }
  2213. }
  2214. ROT.Map.Cellular.prototype._tunnelToConnected = function(to, from, connected, notConnected) {
  2215. var key = this._pointKey(from);
  2216. var a, b;
  2217. if (from[0] < to[0]) {
  2218. a = from;
  2219. b = to;
  2220. } else {
  2221. a = to;
  2222. b = from;
  2223. }
  2224. for (var xx = a[0]; xx <= b[0]; xx++) {
  2225. this._map[xx][a[1]] = 0;
  2226. var p = [xx, a[1]];
  2227. var pkey = this._pointKey(p);
  2228. connected[pkey] = p;
  2229. delete notConnected[pkey];
  2230. }
  2231. // x is now fixed
  2232. var x = b[0];
  2233. if (from[1] < to[1]) {
  2234. a = from;
  2235. b = to;
  2236. } else {
  2237. a = to;
  2238. b = from;
  2239. }
  2240. for (var yy = a[1]; yy < b[1]; yy++) {
  2241. this._map[x][yy] = 0;
  2242. var p = [x, yy];
  2243. var pkey = this._pointKey(p);
  2244. connected[pkey] = p;
  2245. delete notConnected[pkey];
  2246. }
  2247. }
  2248. ROT.Map.Cellular.prototype._freeSpace = function(x, y) {
  2249. return x >= 0 && x < this._width && y >= 0 && y < this._height && this._map[x][y] != 1;
  2250. }
  2251. ROT.Map.Cellular.prototype._pointKey = function(p) {
  2252. return p[0] + "." + p[1];
  2253. }
  2254. /**
  2255. * @class Dungeon map: has rooms and corridors
  2256. * @augments ROT.Map
  2257. */
  2258. ROT.Map.Dungeon = function(width, height) {
  2259. ROT.Map.call(this, width, height);
  2260. this._rooms = []; /* list of all rooms */
  2261. this._corridors = [];
  2262. }
  2263. ROT.Map.Dungeon.extend(ROT.Map);
  2264. /**
  2265. * Get all generated rooms
  2266. * @returns {ROT.Map.Feature.Room[]}
  2267. */
  2268. ROT.Map.Dungeon.prototype.getRooms = function() {
  2269. return this._rooms;
  2270. }
  2271. /**
  2272. * Get all generated corridors
  2273. * @returns {ROT.Map.Feature.Corridor[]}
  2274. */
  2275. ROT.Map.Dungeon.prototype.getCorridors = function() {
  2276. return this._corridors;
  2277. }
  2278. /**
  2279. * @class Random dungeon generator using human-like digging patterns.
  2280. * Heavily based on Mike Anderson's ideas from the "Tyrant" algo, mentioned at
  2281. * http://www.roguebasin.roguelikedevelopment.org/index.php?title=Dungeon-Building_Algorithm.
  2282. * @augments ROT.Map.Dungeon
  2283. */
  2284. ROT.Map.Digger = function(width, height, options) {
  2285. ROT.Map.Dungeon.call(this, width, height);
  2286. this._options = {
  2287. roomWidth: [3, 9], /* room minimum and maximum width */
  2288. roomHeight: [3, 5], /* room minimum and maximum height */
  2289. corridorLength: [3, 10], /* corridor minimum and maximum length */
  2290. dugPercentage: 0.2, /* we stop after this percentage of level area has been dug out */
  2291. timeLimit: 1000 /* we stop after this much time has passed (msec) */
  2292. }
  2293. for (var p in options) { this._options[p] = options[p]; }
  2294. this._features = {
  2295. "Room": 4,
  2296. "Corridor": 4
  2297. }
  2298. this._featureAttempts = 20; /* how many times do we try to create a feature on a suitable wall */
  2299. this._walls = {}; /* these are available for digging */
  2300. this._digCallback = this._digCallback.bind(this);
  2301. this._canBeDugCallback = this._canBeDugCallback.bind(this);
  2302. this._isWallCallback = this._isWallCallback.bind(this);
  2303. this._priorityWallCallback = this._priorityWallCallback.bind(this);
  2304. }
  2305. ROT.Map.Digger.extend(ROT.Map.Dungeon);
  2306. /**
  2307. * Create a map
  2308. * @see ROT.Map#create
  2309. */
  2310. ROT.Map.Digger.prototype.create = function(callback) {
  2311. this._rooms = [];
  2312. this._corridors = [];
  2313. this._map = this._fillMap(1);
  2314. this._walls = {};
  2315. this._dug = 0;
  2316. var area = (this._width-2) * (this._height-2);
  2317. this._firstRoom();
  2318. var t1 = Date.now();
  2319. do {
  2320. var t2 = Date.now();
  2321. if (t2 - t1 > this._options.timeLimit) { break; }
  2322. /* find a good wall */
  2323. var wall = this._findWall();
  2324. if (!wall) { break; } /* no more walls */
  2325. var parts = wall.split(",");
  2326. var x = parseInt(parts[0]);
  2327. var y = parseInt(parts[1]);
  2328. var dir = this._getDiggingDirection(x, y);
  2329. if (!dir) { continue; } /* this wall is not suitable */
  2330. // console.log("wall", x, y);
  2331. /* try adding a feature */
  2332. var featureAttempts = 0;
  2333. do {
  2334. featureAttempts++;
  2335. if (this._tryFeature(x, y, dir[0], dir[1])) { /* feature added */
  2336. //if (this._rooms.length + this._corridors.length == 2) { this._rooms[0].addDoor(x, y); } /* first room oficially has doors */
  2337. this._removeSurroundingWalls(x, y);
  2338. this._removeSurroundingWalls(x-dir[0], y-dir[1]);
  2339. break;
  2340. }
  2341. } while (featureAttempts < this._featureAttempts);
  2342. var priorityWalls = 0;
  2343. for (var id in this._walls) {
  2344. if (this._walls[id] > 1) { priorityWalls++; }
  2345. }
  2346. } while (this._dug/area < this._options.dugPercentage || priorityWalls); /* fixme number of priority walls */
  2347. this._addDoors();
  2348. if (callback) {
  2349. for (var i=0;i<this._width;i++) {
  2350. for (var j=0;j<this._height;j++) {
  2351. callback(i, j, this._map[i][j]);
  2352. }
  2353. }
  2354. }
  2355. this._walls = {};
  2356. this._map = null;
  2357. return this;
  2358. }
  2359. ROT.Map.Digger.prototype._digCallback = function(x, y, value) {
  2360. if (value == 0 || value == 2) { /* empty */
  2361. this._map[x][y] = 0;
  2362. this._dug++;
  2363. } else { /* wall */
  2364. this._walls[x+","+y] = 1;
  2365. }
  2366. }
  2367. ROT.Map.Digger.prototype._isWallCallback = function(x, y) {
  2368. if (x < 0 || y < 0 || x >= this._width || y >= this._height) { return false; }
  2369. return (this._map[x][y] == 1);
  2370. }
  2371. ROT.Map.Digger.prototype._canBeDugCallback = function(x, y) {
  2372. if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; }
  2373. return (this._map[x][y] == 1);
  2374. }
  2375. ROT.Map.Digger.prototype._priorityWallCallback = function(x, y) {
  2376. this._walls[x+","+y] = 2;
  2377. }
  2378. ROT.Map.Digger.prototype._firstRoom = function() {
  2379. var cx = Math.floor(this._width/2);
  2380. var cy = Math.floor(this._height/2);
  2381. var room = ROT.Map.Feature.Room.createRandomCenter(cx, cy, this._options);
  2382. this._rooms.push(room);
  2383. room.create(this._digCallback);
  2384. }
  2385. /**
  2386. * Get a suitable wall
  2387. */
  2388. ROT.Map.Digger.prototype._findWall = function() {
  2389. var prio1 = [];
  2390. var prio2 = [];
  2391. for (var id in this._walls) {
  2392. var prio = this._walls[id];
  2393. if (prio == 2) {
  2394. prio2.push(id);
  2395. } else {
  2396. prio1.push(id);
  2397. }
  2398. }
  2399. var arr = (prio2.length ? prio2 : prio1);
  2400. if (!arr.length) { return null; } /* no walls :/ */
  2401. var id = arr.random();
  2402. delete this._walls[id];
  2403. return id;
  2404. }
  2405. /**
  2406. * Tries adding a feature
  2407. * @returns {bool} was this a successful try?
  2408. */
  2409. ROT.Map.Digger.prototype._tryFeature = function(x, y, dx, dy) {
  2410. var feature = ROT.RNG.getWeightedValue(this._features);
  2411. feature = ROT.Map.Feature[feature].createRandomAt(x, y, dx, dy, this._options);
  2412. if (!feature.isValid(this._isWallCallback, this._canBeDugCallback)) {
  2413. // console.log("not valid");
  2414. // feature.debug();
  2415. return false;
  2416. }
  2417. feature.create(this._digCallback);
  2418. // feature.debug();
  2419. if (feature instanceof ROT.Map.Feature.Room) { this._rooms.push(feature); }
  2420. if (feature instanceof ROT.Map.Feature.Corridor) {
  2421. feature.createPriorityWalls(this._priorityWallCallback);
  2422. this._corridors.push(feature);
  2423. }
  2424. return true;
  2425. }
  2426. ROT.Map.Digger.prototype._removeSurroundingWalls = function(cx, cy) {
  2427. var deltas = ROT.DIRS[4];
  2428. for (var i=0;i<deltas.length;i++) {
  2429. var delta = deltas[i];
  2430. var x = cx + delta[0];
  2431. var y = cy + delta[1];
  2432. delete this._walls[x+","+y];
  2433. var x = cx + 2*delta[0];
  2434. var y = cy + 2*delta[1];
  2435. delete this._walls[x+","+y];
  2436. }
  2437. }
  2438. /**
  2439. * Returns vector in "digging" direction, or false, if this does not exist (or is not unique)
  2440. */
  2441. ROT.Map.Digger.prototype._getDiggingDirection = function(cx, cy) {
  2442. if (cx <= 0 || cy <= 0 || cx >= this._width - 1 || cy >= this._height - 1) { return null; }
  2443. var result = null;
  2444. var deltas = ROT.DIRS[4];
  2445. for (var i=0;i<deltas.length;i++) {
  2446. var delta = deltas[i];
  2447. var x = cx + delta[0];
  2448. var y = cy + delta[1];
  2449. if (!this._map[x][y]) { /* there already is another empty neighbor! */
  2450. if (result) { return null; }
  2451. result = delta;
  2452. }
  2453. }
  2454. /* no empty neighbor */
  2455. if (!result) { return null; }
  2456. return [-result[0], -result[1]];
  2457. }
  2458. /**
  2459. * Find empty spaces surrounding rooms, and apply doors.
  2460. */
  2461. ROT.Map.Digger.prototype._addDoors = function() {
  2462. var data = this._map;
  2463. var isWallCallback = function(x, y) {
  2464. return (data[x][y] == 1);
  2465. }
  2466. for (var i = 0; i < this._rooms.length; i++ ) {
  2467. var room = this._rooms[i];
  2468. room.clearDoors();
  2469. room.addDoors(isWallCallback);
  2470. }
  2471. }
  2472. /**
  2473. * @class Dungeon generator which tries to fill the space evenly. Generates independent rooms and tries to connect them.
  2474. * @augments ROT.Map.Dungeon
  2475. */
  2476. ROT.Map.Uniform = function(width, height, options) {
  2477. ROT.Map.Dungeon.call(this, width, height);
  2478. this._options = {
  2479. roomWidth: [3, 9], /* room minimum and maximum width */
  2480. roomHeight: [3, 5], /* room minimum and maximum height */
  2481. roomDugPercentage: 0.1, /* we stop after this percentage of level area has been dug out by rooms */
  2482. timeLimit: 1000 /* we stop after this much time has passed (msec) */
  2483. }
  2484. for (var p in options) { this._options[p] = options[p]; }
  2485. this._roomAttempts = 20; /* new room is created N-times until is considered as impossible to generate */
  2486. this._corridorAttempts = 20; /* corridors are tried N-times until the level is considered as impossible to connect */
  2487. this._connected = []; /* list of already connected rooms */
  2488. this._unconnected = []; /* list of remaining unconnected rooms */
  2489. this._digCallback = this._digCallback.bind(this);
  2490. this._canBeDugCallback = this._canBeDugCallback.bind(this);
  2491. this._isWallCallback = this._isWallCallback.bind(this);
  2492. }
  2493. ROT.Map.Uniform.extend(ROT.Map.Dungeon);
  2494. /**
  2495. * Create a map. If the time limit has been hit, returns null.
  2496. * @see ROT.Map#create
  2497. */
  2498. ROT.Map.Uniform.prototype.create = function(callback) {
  2499. var t1 = Date.now();
  2500. while (1) {
  2501. var t2 = Date.now();
  2502. if (t2 - t1 > this._options.timeLimit) { return null; } /* time limit! */
  2503. this._map = this._fillMap(1);
  2504. this._dug = 0;
  2505. this._rooms = [];
  2506. this._unconnected = [];
  2507. this._generateRooms();
  2508. if (this._rooms.length < 2) { continue; }
  2509. if (this._generateCorridors()) { break; }
  2510. }
  2511. if (callback) {
  2512. for (var i=0;i<this._width;i++) {
  2513. for (var j=0;j<this._height;j++) {
  2514. callback(i, j, this._map[i][j]);
  2515. }
  2516. }
  2517. }
  2518. return this;
  2519. }
  2520. /**
  2521. * Generates a suitable amount of rooms
  2522. */
  2523. ROT.Map.Uniform.prototype._generateRooms = function() {
  2524. var w = this._width-2;
  2525. var h = this._height-2;
  2526. do {
  2527. var room = this._generateRoom();
  2528. if (this._dug/(w*h) > this._options.roomDugPercentage) { break; } /* achieved requested amount of free space */
  2529. } while (room);
  2530. /* either enough rooms, or not able to generate more of them :) */
  2531. }
  2532. /**
  2533. * Try to generate one room
  2534. */
  2535. ROT.Map.Uniform.prototype._generateRoom = function() {
  2536. var count = 0;
  2537. while (count < this._roomAttempts) {
  2538. count++;
  2539. var room = ROT.Map.Feature.Room.createRandom(this._width, this._height, this._options);
  2540. if (!room.isValid(this._isWallCallback, this._canBeDugCallback)) { continue; }
  2541. room.create(this._digCallback);
  2542. this._rooms.push(room);
  2543. return room;
  2544. }
  2545. /* no room was generated in a given number of attempts */
  2546. return null;
  2547. }
  2548. /**
  2549. * Generates connectors beween rooms
  2550. * @returns {bool} success Was this attempt successfull?
  2551. */
  2552. ROT.Map.Uniform.prototype._generateCorridors = function() {
  2553. var cnt = 0;
  2554. while (cnt < this._corridorAttempts) {
  2555. cnt++;
  2556. this._corridors = [];
  2557. /* dig rooms into a clear map */
  2558. this._map = this._fillMap(1);
  2559. for (var i=0;i<this._rooms.length;i++) {
  2560. var room = this._rooms[i];
  2561. room.clearDoors();
  2562. room.create(this._digCallback);
  2563. }
  2564. this._unconnected = this._rooms.slice().randomize();
  2565. this._connected = [];
  2566. if (this._unconnected.length) { this._connected.push(this._unconnected.pop()); } /* first one is always connected */
  2567. while (1) {
  2568. /* 1. pick random connected room */
  2569. var connected = this._connected.random();
  2570. /* 2. find closest unconnected */
  2571. var room1 = this._closestRoom(this._unconnected, connected);
  2572. /* 3. connect it to closest connected */
  2573. var room2 = this._closestRoom(this._connected, room1);
  2574. var ok = this._connectRooms(room1, room2);
  2575. if (!ok) { break; } /* stop connecting, re-shuffle */
  2576. if (!this._unconnected.length) { return true; } /* done; no rooms remain */
  2577. }
  2578. }
  2579. return false;
  2580. }
  2581. /**
  2582. * For a given room, find the closest one from the list
  2583. */
  2584. ROT.Map.Uniform.prototype._closestRoom = function(rooms, room) {
  2585. var dist = Infinity;
  2586. var center = room.getCenter();
  2587. var result = null;
  2588. for (var i=0;i<rooms.length;i++) {
  2589. var r = rooms[i];
  2590. var c = r.getCenter();
  2591. var dx = c[0]-center[0];
  2592. var dy = c[1]-center[1];
  2593. var d = dx*dx+dy*dy;
  2594. if (d < dist) {
  2595. dist = d;
  2596. result = r;
  2597. }
  2598. }
  2599. return result;
  2600. }
  2601. ROT.Map.Uniform.prototype._connectRooms = function(room1, room2) {
  2602. /*
  2603. room1.debug();
  2604. room2.debug();
  2605. */
  2606. var center1 = room1.getCenter();
  2607. var center2 = room2.getCenter();
  2608. var diffX = center2[0] - center1[0];
  2609. var diffY = center2[1] - center1[1];
  2610. if (Math.abs(diffX) < Math.abs(diffY)) { /* first try connecting north-south walls */
  2611. var dirIndex1 = (diffY > 0 ? 2 : 0);
  2612. var dirIndex2 = (dirIndex1 + 2) % 4;
  2613. var min = room2.getLeft();
  2614. var max = room2.getRight();
  2615. var index = 0;
  2616. } else { /* first try connecting east-west walls */
  2617. var dirIndex1 = (diffX > 0 ? 1 : 3);
  2618. var dirIndex2 = (dirIndex1 + 2) % 4;
  2619. var min = room2.getTop();
  2620. var max = room2.getBottom();
  2621. var index = 1;
  2622. }
  2623. var start = this._placeInWall(room1, dirIndex1); /* corridor will start here */
  2624. if (!start) { return false; }
  2625. if (start[index] >= min && start[index] <= max) { /* possible to connect with straight line (I-like) */
  2626. var end = start.slice();
  2627. var value = null;
  2628. switch (dirIndex2) {
  2629. case 0: value = room2.getTop()-1; break;
  2630. case 1: value = room2.getRight()+1; break;
  2631. case 2: value = room2.getBottom()+1; break;
  2632. case 3: value = room2.getLeft()-1; break;
  2633. }
  2634. end[(index+1)%2] = value;
  2635. this._digLine([start, end]);
  2636. } else if (start[index] < min-1 || start[index] > max+1) { /* need to switch target wall (L-like) */
  2637. var diff = start[index] - center2[index];
  2638. switch (dirIndex2) {
  2639. case 0:
  2640. case 1: var rotation = (diff < 0 ? 3 : 1); break;
  2641. case 2:
  2642. case 3: var rotation = (diff < 0 ? 1 : 3); break;
  2643. }
  2644. dirIndex2 = (dirIndex2 + rotation) % 4;
  2645. var end = this._placeInWall(room2, dirIndex2);
  2646. if (!end) { return false; }
  2647. var mid = [0, 0];
  2648. mid[index] = start[index];
  2649. var index2 = (index+1)%2;
  2650. mid[index2] = end[index2];
  2651. this._digLine([start, mid, end]);
  2652. } else { /* use current wall pair, but adjust the line in the middle (S-like) */
  2653. var index2 = (index+1)%2;
  2654. var end = this._placeInWall(room2, dirIndex2);
  2655. if (!end) { return false; }
  2656. var mid = Math.round((end[index2] + start[index2])/2);
  2657. var mid1 = [0, 0];
  2658. var mid2 = [0, 0];
  2659. mid1[index] = start[index];
  2660. mid1[index2] = mid;
  2661. mid2[index] = end[index];
  2662. mid2[index2] = mid;
  2663. this._digLine([start, mid1, mid2, end]);
  2664. }
  2665. room1.addDoor(start[0], start[1]);
  2666. room2.addDoor(end[0], end[1]);
  2667. var index = this._unconnected.indexOf(room1);
  2668. if (index != -1) {
  2669. this._unconnected.splice(index, 1);
  2670. this._connected.push(room1);
  2671. }
  2672. var index = this._unconnected.indexOf(room2);
  2673. if (index != -1) {
  2674. this._unconnected.splice(index, 1);
  2675. this._connected.push(room2);
  2676. }
  2677. return true;
  2678. }
  2679. ROT.Map.Uniform.prototype._placeInWall = function(room, dirIndex) {
  2680. var start = [0, 0];
  2681. var dir = [0, 0];
  2682. var length = 0;
  2683. switch (dirIndex) {
  2684. case 0:
  2685. dir = [1, 0];
  2686. start = [room.getLeft(), room.getTop()-1];
  2687. length = room.getRight()-room.getLeft()+1;
  2688. break;
  2689. case 1:
  2690. dir = [0, 1];
  2691. start = [room.getRight()+1, room.getTop()];
  2692. length = room.getBottom()-room.getTop()+1;
  2693. break;
  2694. case 2:
  2695. dir = [1, 0];
  2696. start = [room.getLeft(), room.getBottom()+1];
  2697. length = room.getRight()-room.getLeft()+1;
  2698. break;
  2699. case 3:
  2700. dir = [0, 1];
  2701. start = [room.getLeft()-1, room.getTop()];
  2702. length = room.getBottom()-room.getTop()+1;
  2703. break;
  2704. }
  2705. var avail = [];
  2706. var lastBadIndex = -2;
  2707. for (var i=0;i<length;i++) {
  2708. var x = start[0] + i*dir[0];
  2709. var y = start[1] + i*dir[1];
  2710. avail.push(null);
  2711. var isWall = (this._map[x][y] == 1);
  2712. if (isWall) {
  2713. if (lastBadIndex != i-1) { avail[i] = [x, y]; }
  2714. } else {
  2715. lastBadIndex = i;
  2716. if (i) { avail[i-1] = null; }
  2717. }
  2718. }
  2719. for (var i=avail.length-1; i>=0; i--) {
  2720. if (!avail[i]) { avail.splice(i, 1); }
  2721. }
  2722. return (avail.length ? avail.random() : null);
  2723. }
  2724. /**
  2725. * Dig a polyline.
  2726. */
  2727. ROT.Map.Uniform.prototype._digLine = function(points) {
  2728. for (var i=1;i<points.length;i++) {
  2729. var start = points[i-1];
  2730. var end = points[i];
  2731. var corridor = new ROT.Map.Feature.Corridor(start[0], start[1], end[0], end[1]);
  2732. corridor.create(this._digCallback);
  2733. this._corridors.push(corridor);
  2734. }
  2735. }
  2736. ROT.Map.Uniform.prototype._digCallback = function(x, y, value) {
  2737. this._map[x][y] = value;
  2738. if (value == 0) { this._dug++; }
  2739. }
  2740. ROT.Map.Uniform.prototype._isWallCallback = function(x, y) {
  2741. if (x < 0 || y < 0 || x >= this._width || y >= this._height) { return false; }
  2742. return (this._map[x][y] == 1);
  2743. }
  2744. ROT.Map.Uniform.prototype._canBeDugCallback = function(x, y) {
  2745. if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; }
  2746. return (this._map[x][y] == 1);
  2747. }
  2748. /**
  2749. * @author hyakugei
  2750. * @class Dungeon generator which uses the "orginal" Rogue dungeon generation algorithm. See http://kuoi.com/~kamikaze/GameDesign/art07_rogue_dungeon.php
  2751. * @augments ROT.Map
  2752. * @param {int} [width=ROT.DEFAULT_WIDTH]
  2753. * @param {int} [height=ROT.DEFAULT_HEIGHT]
  2754. * @param {object} [options] Options
  2755. * @param {int[]} [options.cellWidth=3] Number of cells to create on the horizontal (number of rooms horizontally)
  2756. * @param {int[]} [options.cellHeight=3] Number of cells to create on the vertical (number of rooms vertically)
  2757. * @param {int} [options.roomWidth] Room min and max width - normally set auto-magically via the constructor.
  2758. * @param {int} [options.roomHeight] Room min and max height - normally set auto-magically via the constructor.
  2759. */
  2760. ROT.Map.Rogue = function(width, height, options) {
  2761. ROT.Map.call(this, width, height);
  2762. this._options = {
  2763. cellWidth: 3, // NOTE to self, these could probably work the same as the roomWidth/room Height values
  2764. cellHeight: 3 // ie. as an array with min-max values for each direction....
  2765. }
  2766. for (var p in options) { this._options[p] = options[p]; }
  2767. /*
  2768. Set the room sizes according to the over-all width of the map,
  2769. and the cell sizes.
  2770. */
  2771. if (!this._options.hasOwnProperty("roomWidth")) {
  2772. this._options["roomWidth"] = this._calculateRoomSize(this._width, this._options["cellWidth"]);
  2773. }
  2774. if (!this._options.hasOwnProperty("roomHeight")) {
  2775. this._options["roomHeight"] = this._calculateRoomSize(this._height, this._options["cellHeight"]);
  2776. }
  2777. }
  2778. ROT.Map.Rogue.extend(ROT.Map);
  2779. /**
  2780. * @see ROT.Map#create
  2781. */
  2782. ROT.Map.Rogue.prototype.create = function(callback) {
  2783. this.map = this._fillMap(1);
  2784. this.rooms = [];
  2785. this.connectedCells = [];
  2786. this._initRooms();
  2787. this._connectRooms();
  2788. this._connectUnconnectedRooms();
  2789. this._createRandomRoomConnections();
  2790. this._createRooms();
  2791. this._createCorridors();
  2792. if (callback) {
  2793. for (var i = 0; i < this._width; i++) {
  2794. for (var j = 0; j < this._height; j++) {
  2795. callback(i, j, this.map[i][j]);
  2796. }
  2797. }
  2798. }
  2799. return this;
  2800. }
  2801. ROT.Map.Rogue.prototype._calculateRoomSize = function(size, cell) {
  2802. var max = Math.floor((size/cell) * 0.8);
  2803. var min = Math.floor((size/cell) * 0.25);
  2804. if (min < 2) min = 2;
  2805. if (max < 2) max = 2;
  2806. return [min, max];
  2807. }
  2808. ROT.Map.Rogue.prototype._initRooms = function () {
  2809. // create rooms array. This is the "grid" list from the algo.
  2810. for (var i = 0; i < this._options.cellWidth; i++) {
  2811. this.rooms.push([]);
  2812. for(var j = 0; j < this._options.cellHeight; j++) {
  2813. this.rooms[i].push({"x":0, "y":0, "width":0, "height":0, "connections":[], "cellx":i, "celly":j});
  2814. }
  2815. }
  2816. }
  2817. ROT.Map.Rogue.prototype._connectRooms = function() {
  2818. //pick random starting grid
  2819. var cgx = ROT.RNG.getUniformInt(0, this._options.cellWidth-1);
  2820. var cgy = ROT.RNG.getUniformInt(0, this._options.cellHeight-1);
  2821. var idx;
  2822. var ncgx;
  2823. var ncgy;
  2824. var found = false;
  2825. var room;
  2826. var otherRoom;
  2827. // find unconnected neighbour cells
  2828. do {
  2829. //var dirToCheck = [0,1,2,3,4,5,6,7];
  2830. var dirToCheck = [0,2,4,6];
  2831. dirToCheck = dirToCheck.randomize();
  2832. do {
  2833. found = false;
  2834. idx = dirToCheck.pop();
  2835. ncgx = cgx + ROT.DIRS[8][idx][0];
  2836. ncgy = cgy + ROT.DIRS[8][idx][1];
  2837. if(ncgx < 0 || ncgx >= this._options.cellWidth) continue;
  2838. if(ncgy < 0 || ncgy >= this._options.cellHeight) continue;
  2839. room = this.rooms[cgx][cgy];
  2840. if(room["connections"].length > 0)
  2841. {
  2842. // as long as this room doesn't already coonect to me, we are ok with it.
  2843. if(room["connections"][0][0] == ncgx &&
  2844. room["connections"][0][1] == ncgy)
  2845. {
  2846. break;
  2847. }
  2848. }
  2849. otherRoom = this.rooms[ncgx][ncgy];
  2850. if (otherRoom["connections"].length == 0) {
  2851. otherRoom["connections"].push([cgx,cgy]);
  2852. this.connectedCells.push([ncgx, ncgy]);
  2853. cgx = ncgx;
  2854. cgy = ncgy;
  2855. found = true;
  2856. }
  2857. } while (dirToCheck.length > 0 && found == false)
  2858. } while (dirToCheck.length > 0)
  2859. }
  2860. ROT.Map.Rogue.prototype._connectUnconnectedRooms = function() {
  2861. //While there are unconnected rooms, try to connect them to a random connected neighbor
  2862. //(if a room has no connected neighbors yet, just keep cycling, you'll fill out to it eventually).
  2863. var cw = this._options.cellWidth;
  2864. var ch = this._options.cellHeight;
  2865. var randomConnectedCell;
  2866. this.connectedCells = this.connectedCells.randomize();
  2867. var room;
  2868. var otherRoom;
  2869. var validRoom;
  2870. for (var i = 0; i < this._options.cellWidth; i++) {
  2871. for (var j = 0; j < this._options.cellHeight; j++) {
  2872. room = this.rooms[i][j];
  2873. if (room["connections"].length == 0) {
  2874. var directions = [0,2,4,6];
  2875. directions = directions.randomize();
  2876. var validRoom = false;
  2877. do {
  2878. var dirIdx = directions.pop();
  2879. var newI = i + ROT.DIRS[8][dirIdx][0];
  2880. var newJ = j + ROT.DIRS[8][dirIdx][1];
  2881. if (newI < 0 || newI >= cw ||
  2882. newJ < 0 || newJ >= ch) {
  2883. continue;
  2884. }
  2885. otherRoom = this.rooms[newI][newJ];
  2886. validRoom = true;
  2887. if (otherRoom["connections"].length == 0) {
  2888. break;
  2889. }
  2890. for (var k = 0; k < otherRoom["connections"].length; k++) {
  2891. if(otherRoom["connections"][k][0] == i &&
  2892. otherRoom["connections"][k][1] == j) {
  2893. validRoom = false;
  2894. break;
  2895. }
  2896. }
  2897. if (validRoom) break;
  2898. } while (directions.length)
  2899. if(validRoom) {
  2900. room["connections"].push( [otherRoom["cellx"], otherRoom["celly"]] );
  2901. } else {
  2902. console.log("-- Unable to connect room.");
  2903. }
  2904. }
  2905. }
  2906. }
  2907. }
  2908. ROT.Map.Rogue.prototype._createRandomRoomConnections = function(connections) {
  2909. // Empty for now.
  2910. }
  2911. ROT.Map.Rogue.prototype._createRooms = function() {
  2912. // Create Rooms
  2913. var w = this._width;
  2914. var h = this._height;
  2915. var cw = this._options.cellWidth;
  2916. var ch = this._options.cellHeight;
  2917. var cwp = Math.floor(this._width / cw);
  2918. var chp = Math.floor(this._height / ch);
  2919. var roomw;
  2920. var roomh;
  2921. var roomWidth = this._options["roomWidth"];
  2922. var roomHeight = this._options["roomHeight"];
  2923. var sx;
  2924. var sy;
  2925. var tx;
  2926. var ty;
  2927. var otherRoom;
  2928. for (var i = 0; i < cw; i++) {
  2929. for (var j = 0; j < ch; j++) {
  2930. sx = cwp * i;
  2931. sy = chp * j;
  2932. if (sx == 0) sx = 1;
  2933. if (sy == 0) sy = 1;
  2934. roomw = ROT.RNG.getUniformInt(roomWidth[0], roomWidth[1]);
  2935. roomh = ROT.RNG.getUniformInt(roomHeight[0], roomHeight[1]);
  2936. if (j > 0) {
  2937. otherRoom = this.rooms[i][j-1];
  2938. while (sy - (otherRoom["y"] + otherRoom["height"] ) < 3) {
  2939. sy++;
  2940. }
  2941. }
  2942. if (i > 0) {
  2943. otherRoom = this.rooms[i-1][j];
  2944. while(sx - (otherRoom["x"] + otherRoom["width"]) < 3) {
  2945. sx++;
  2946. }
  2947. }
  2948. var sxOffset = Math.round(ROT.RNG.getUniformInt(0, cwp-roomw)/2);
  2949. var syOffset = Math.round(ROT.RNG.getUniformInt(0, chp-roomh)/2);
  2950. while (sx + sxOffset + roomw >= w) {
  2951. if(sxOffset) {
  2952. sxOffset--;
  2953. } else {
  2954. roomw--;
  2955. }
  2956. }
  2957. while (sy + syOffset + roomh >= h) {
  2958. if(syOffset) {
  2959. syOffset--;
  2960. } else {
  2961. roomh--;
  2962. }
  2963. }
  2964. sx = sx + sxOffset;
  2965. sy = sy + syOffset;
  2966. this.rooms[i][j]["x"] = sx;
  2967. this.rooms[i][j]["y"] = sy;
  2968. this.rooms[i][j]["width"] = roomw;
  2969. this.rooms[i][j]["height"] = roomh;
  2970. for (var ii = sx; ii < sx + roomw; ii++) {
  2971. for (var jj = sy; jj < sy + roomh; jj++) {
  2972. this.map[ii][jj] = 0;
  2973. }
  2974. }
  2975. }
  2976. }
  2977. }
  2978. ROT.Map.Rogue.prototype._getWallPosition = function(aRoom, aDirection) {
  2979. var rx;
  2980. var ry;
  2981. var door;
  2982. if (aDirection == 1 || aDirection == 3) {
  2983. rx = ROT.RNG.getUniformInt(aRoom["x"] + 1, aRoom["x"] + aRoom["width"] - 2);
  2984. if (aDirection == 1) {
  2985. ry = aRoom["y"] - 2;
  2986. door = ry + 1;
  2987. } else {
  2988. ry = aRoom["y"] + aRoom["height"] + 1;
  2989. door = ry -1;
  2990. }
  2991. this.map[rx][door] = 0; // i'm not setting a specific 'door' tile value right now, just empty space.
  2992. } else if (aDirection == 2 || aDirection == 4) {
  2993. ry = ROT.RNG.getUniformInt(aRoom["y"] + 1, aRoom["y"] + aRoom["height"] - 2);
  2994. if(aDirection == 2) {
  2995. rx = aRoom["x"] + aRoom["width"] + 1;
  2996. door = rx - 1;
  2997. } else {
  2998. rx = aRoom["x"] - 2;
  2999. door = rx + 1;
  3000. }
  3001. this.map[door][ry] = 0; // i'm not setting a specific 'door' tile value right now, just empty space.
  3002. }
  3003. return [rx, ry];
  3004. }
  3005. /***
  3006. * @param startPosition a 2 element array
  3007. * @param endPosition a 2 element array
  3008. */
  3009. ROT.Map.Rogue.prototype._drawCorridore = function (startPosition, endPosition) {
  3010. var xOffset = endPosition[0] - startPosition[0];
  3011. var yOffset = endPosition[1] - startPosition[1];
  3012. var xpos = startPosition[0];
  3013. var ypos = startPosition[1];
  3014. var tempDist;
  3015. var xDir;
  3016. var yDir;
  3017. var move; // 2 element array, element 0 is the direction, element 1 is the total value to move.
  3018. var moves = []; // a list of 2 element arrays
  3019. var xAbs = Math.abs(xOffset);
  3020. var yAbs = Math.abs(yOffset);
  3021. var percent = ROT.RNG.getUniform(); // used to split the move at different places along the long axis
  3022. var firstHalf = percent;
  3023. var secondHalf = 1 - percent;
  3024. xDir = xOffset > 0 ? 2 : 6;
  3025. yDir = yOffset > 0 ? 4 : 0;
  3026. if (xAbs < yAbs) {
  3027. // move firstHalf of the y offset
  3028. tempDist = Math.ceil(yAbs * firstHalf);
  3029. moves.push([yDir, tempDist]);
  3030. // move all the x offset
  3031. moves.push([xDir, xAbs]);
  3032. // move sendHalf of the y offset
  3033. tempDist = Math.floor(yAbs * secondHalf);
  3034. moves.push([yDir, tempDist]);
  3035. } else {
  3036. // move firstHalf of the x offset
  3037. tempDist = Math.ceil(xAbs * firstHalf);
  3038. moves.push([xDir, tempDist]);
  3039. // move all the y offset
  3040. moves.push([yDir, yAbs]);
  3041. // move secondHalf of the x offset.
  3042. tempDist = Math.floor(xAbs * secondHalf);
  3043. moves.push([xDir, tempDist]);
  3044. }
  3045. this.map[xpos][ypos] = 0;
  3046. while (moves.length > 0) {
  3047. move = moves.pop();
  3048. while (move[1] > 0) {
  3049. xpos += ROT.DIRS[8][move[0]][0];
  3050. ypos += ROT.DIRS[8][move[0]][1];
  3051. this.map[xpos][ypos] = 0;
  3052. move[1] = move[1] - 1;
  3053. }
  3054. }
  3055. }
  3056. ROT.Map.Rogue.prototype._createCorridors = function () {
  3057. // Draw Corridors between connected rooms
  3058. var cw = this._options.cellWidth;
  3059. var ch = this._options.cellHeight;
  3060. var room;
  3061. var connection;
  3062. var otherRoom;
  3063. var wall;
  3064. var otherWall;
  3065. for (var i = 0; i < cw; i++) {
  3066. for (var j = 0; j < ch; j++) {
  3067. room = this.rooms[i][j];
  3068. for (var k = 0; k < room["connections"].length; k++) {
  3069. connection = room["connections"][k];
  3070. otherRoom = this.rooms[connection[0]][connection[1]];
  3071. // figure out what wall our corridor will start one.
  3072. // figure out what wall our corridor will end on.
  3073. if (otherRoom["cellx"] > room["cellx"] ) {
  3074. wall = 2;
  3075. otherWall = 4;
  3076. } else if (otherRoom["cellx"] < room["cellx"] ) {
  3077. wall = 4;
  3078. otherWall = 2;
  3079. } else if(otherRoom["celly"] > room["celly"]) {
  3080. wall = 3;
  3081. otherWall = 1;
  3082. } else if(otherRoom["celly"] < room["celly"]) {
  3083. wall = 1;
  3084. otherWall = 3;
  3085. }
  3086. this._drawCorridore(this._getWallPosition(room, wall), this._getWallPosition(otherRoom, otherWall));
  3087. }
  3088. }
  3089. }
  3090. }
  3091. /**
  3092. * @class Dungeon feature; has own .create() method
  3093. */
  3094. ROT.Map.Feature = function() {}
  3095. ROT.Map.Feature.prototype.isValid = function(canBeDugCallback) {}
  3096. ROT.Map.Feature.prototype.create = function(digCallback) {}
  3097. ROT.Map.Feature.prototype.debug = function() {}
  3098. ROT.Map.Feature.createRandomAt = function(x, y, dx, dy, options) {}
  3099. /**
  3100. * @class Room
  3101. * @augments ROT.Map.Feature
  3102. * @param {int} x1
  3103. * @param {int} y1
  3104. * @param {int} x2
  3105. * @param {int} y2
  3106. * @param {int} [doorX]
  3107. * @param {int} [doorY]
  3108. */
  3109. ROT.Map.Feature.Room = function(x1, y1, x2, y2, doorX, doorY) {
  3110. this._x1 = x1;
  3111. this._y1 = y1;
  3112. this._x2 = x2;
  3113. this._y2 = y2;
  3114. this._doors = {};
  3115. if (arguments.length > 4) { this.addDoor(doorX, doorY); }
  3116. }
  3117. ROT.Map.Feature.Room.extend(ROT.Map.Feature);
  3118. /**
  3119. * Room of random size, with a given doors and direction
  3120. */
  3121. ROT.Map.Feature.Room.createRandomAt = function(x, y, dx, dy, options) {
  3122. var min = options.roomWidth[0];
  3123. var max = options.roomWidth[1];
  3124. var width = ROT.RNG.getUniformInt(min, max);
  3125. var min = options.roomHeight[0];
  3126. var max = options.roomHeight[1];
  3127. var height = ROT.RNG.getUniformInt(min, max);
  3128. if (dx == 1) { /* to the right */
  3129. var y2 = y - Math.floor(ROT.RNG.getUniform() * height);
  3130. return new this(x+1, y2, x+width, y2+height-1, x, y);
  3131. }
  3132. if (dx == -1) { /* to the left */
  3133. var y2 = y - Math.floor(ROT.RNG.getUniform() * height);
  3134. return new this(x-width, y2, x-1, y2+height-1, x, y);
  3135. }
  3136. if (dy == 1) { /* to the bottom */
  3137. var x2 = x - Math.floor(ROT.RNG.getUniform() * width);
  3138. return new this(x2, y+1, x2+width-1, y+height, x, y);
  3139. }
  3140. if (dy == -1) { /* to the top */
  3141. var x2 = x - Math.floor(ROT.RNG.getUniform() * width);
  3142. return new this(x2, y-height, x2+width-1, y-1, x, y);
  3143. }
  3144. throw new Error("dx or dy must be 1 or -1");
  3145. }
  3146. /**
  3147. * Room of random size, positioned around center coords
  3148. */
  3149. ROT.Map.Feature.Room.createRandomCenter = function(cx, cy, options) {
  3150. var min = options.roomWidth[0];
  3151. var max = options.roomWidth[1];
  3152. var width = ROT.RNG.getUniformInt(min, max);
  3153. var min = options.roomHeight[0];
  3154. var max = options.roomHeight[1];
  3155. var height = ROT.RNG.getUniformInt(min, max);
  3156. var x1 = cx - Math.floor(ROT.RNG.getUniform()*width);
  3157. var y1 = cy - Math.floor(ROT.RNG.getUniform()*height);
  3158. var x2 = x1 + width - 1;
  3159. var y2 = y1 + height - 1;
  3160. return new this(x1, y1, x2, y2);
  3161. }
  3162. /**
  3163. * Room of random size within a given dimensions
  3164. */
  3165. ROT.Map.Feature.Room.createRandom = function(availWidth, availHeight, options) {
  3166. var min = options.roomWidth[0];
  3167. var max = options.roomWidth[1];
  3168. var width = ROT.RNG.getUniformInt(min, max);
  3169. var min = options.roomHeight[0];
  3170. var max = options.roomHeight[1];
  3171. var height = ROT.RNG.getUniformInt(min, max);
  3172. var left = availWidth - width - 1;
  3173. var top = availHeight - height - 1;
  3174. var x1 = 1 + Math.floor(ROT.RNG.getUniform()*left);
  3175. var y1 = 1 + Math.floor(ROT.RNG.getUniform()*top);
  3176. var x2 = x1 + width - 1;
  3177. var y2 = y1 + height - 1;
  3178. return new this(x1, y1, x2, y2);
  3179. }
  3180. ROT.Map.Feature.Room.prototype.addDoor = function(x, y) {
  3181. this._doors[x+","+y] = 1;
  3182. return this;
  3183. }
  3184. /**
  3185. * @param {function}
  3186. */
  3187. ROT.Map.Feature.Room.prototype.getDoors = function(callback) {
  3188. for (var key in this._doors) {
  3189. var parts = key.split(",");
  3190. callback(parseInt(parts[0]), parseInt(parts[1]));
  3191. }
  3192. return this;
  3193. }
  3194. ROT.Map.Feature.Room.prototype.clearDoors = function() {
  3195. this._doors = {};
  3196. return this;
  3197. }
  3198. ROT.Map.Feature.Room.prototype.addDoors = function(isWallCallback) {
  3199. var left = this._x1-1;
  3200. var right = this._x2+1;
  3201. var top = this._y1-1;
  3202. var bottom = this._y2+1;
  3203. for (var x=left; x<=right; x++) {
  3204. for (var y=top; y<=bottom; y++) {
  3205. if (x != left && x != right && y != top && y != bottom) { continue; }
  3206. if (isWallCallback(x, y)) { continue; }
  3207. this.addDoor(x, y);
  3208. }
  3209. }
  3210. return this;
  3211. }
  3212. ROT.Map.Feature.Room.prototype.debug = function() {
  3213. console.log("room", this._x1, this._y1, this._x2, this._y2);
  3214. }
  3215. ROT.Map.Feature.Room.prototype.isValid = function(isWallCallback, canBeDugCallback) {
  3216. var left = this._x1-1;
  3217. var right = this._x2+1;
  3218. var top = this._y1-1;
  3219. var bottom = this._y2+1;
  3220. for (var x=left; x<=right; x++) {
  3221. for (var y=top; y<=bottom; y++) {
  3222. if (x == left || x == right || y == top || y == bottom) {
  3223. if (!isWallCallback(x, y)) { return false; }
  3224. } else {
  3225. if (!canBeDugCallback(x, y)) { return false; }
  3226. }
  3227. }
  3228. }
  3229. return true;
  3230. }
  3231. /**
  3232. * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty, 1 = wall, 2 = door. Multiple doors are allowed.
  3233. */
  3234. ROT.Map.Feature.Room.prototype.create = function(digCallback) {
  3235. var left = this._x1-1;
  3236. var right = this._x2+1;
  3237. var top = this._y1-1;
  3238. var bottom = this._y2+1;
  3239. var value = 0;
  3240. for (var x=left; x<=right; x++) {
  3241. for (var y=top; y<=bottom; y++) {
  3242. if (x+","+y in this._doors) {
  3243. value = 2;
  3244. } else if (x == left || x == right || y == top || y == bottom) {
  3245. value = 1;
  3246. } else {
  3247. value = 0;
  3248. }
  3249. digCallback(x, y, value);
  3250. }
  3251. }
  3252. }
  3253. ROT.Map.Feature.Room.prototype.getCenter = function() {
  3254. return [Math.round((this._x1 + this._x2)/2), Math.round((this._y1 + this._y2)/2)];
  3255. }
  3256. ROT.Map.Feature.Room.prototype.getLeft = function() {
  3257. return this._x1;
  3258. }
  3259. ROT.Map.Feature.Room.prototype.getRight = function() {
  3260. return this._x2;
  3261. }
  3262. ROT.Map.Feature.Room.prototype.getTop = function() {
  3263. return this._y1;
  3264. }
  3265. ROT.Map.Feature.Room.prototype.getBottom = function() {
  3266. return this._y2;
  3267. }
  3268. /**
  3269. * @class Corridor
  3270. * @augments ROT.Map.Feature
  3271. * @param {int} startX
  3272. * @param {int} startY
  3273. * @param {int} endX
  3274. * @param {int} endY
  3275. */
  3276. ROT.Map.Feature.Corridor = function(startX, startY, endX, endY) {
  3277. this._startX = startX;
  3278. this._startY = startY;
  3279. this._endX = endX;
  3280. this._endY = endY;
  3281. this._endsWithAWall = true;
  3282. }
  3283. ROT.Map.Feature.Corridor.extend(ROT.Map.Feature);
  3284. ROT.Map.Feature.Corridor.createRandomAt = function(x, y, dx, dy, options) {
  3285. var min = options.corridorLength[0];
  3286. var max = options.corridorLength[1];
  3287. var length = ROT.RNG.getUniformInt(min, max);
  3288. return new this(x, y, x + dx*length, y + dy*length);
  3289. }
  3290. ROT.Map.Feature.Corridor.prototype.debug = function() {
  3291. console.log("corridor", this._startX, this._startY, this._endX, this._endY);
  3292. }
  3293. ROT.Map.Feature.Corridor.prototype.isValid = function(isWallCallback, canBeDugCallback){
  3294. var sx = this._startX;
  3295. var sy = this._startY;
  3296. var dx = this._endX-sx;
  3297. var dy = this._endY-sy;
  3298. var length = 1 + Math.max(Math.abs(dx), Math.abs(dy));
  3299. if (dx) { dx = dx/Math.abs(dx); }
  3300. if (dy) { dy = dy/Math.abs(dy); }
  3301. var nx = dy;
  3302. var ny = -dx;
  3303. var ok = true;
  3304. for (var i=0; i<length; i++) {
  3305. var x = sx + i*dx;
  3306. var y = sy + i*dy;
  3307. if (!canBeDugCallback( x, y)) { ok = false; }
  3308. if (!isWallCallback (x + nx, y + ny)) { ok = false; }
  3309. if (!isWallCallback (x - nx, y - ny)) { ok = false; }
  3310. if (!ok) {
  3311. length = i;
  3312. this._endX = x-dx;
  3313. this._endY = y-dy;
  3314. break;
  3315. }
  3316. }
  3317. /**
  3318. * If the length degenerated, this corridor might be invalid
  3319. */
  3320. /* not supported */
  3321. if (length == 0) { return false; }
  3322. /* length 1 allowed only if the next space is empty */
  3323. if (length == 1 && isWallCallback(this._endX + dx, this._endY + dy)) { return false; }
  3324. /**
  3325. * We do not want the corridor to crash into a corner of a room;
  3326. * if any of the ending corners is empty, the N+1th cell of this corridor must be empty too.
  3327. *
  3328. * Situation:
  3329. * #######1
  3330. * .......?
  3331. * #######2
  3332. *
  3333. * The corridor was dug from left to right.
  3334. * 1, 2 - problematic corners, ? = N+1th cell (not dug)
  3335. */
  3336. var firstCornerBad = !isWallCallback(this._endX + dx + nx, this._endY + dy + ny);
  3337. var secondCornerBad = !isWallCallback(this._endX + dx - nx, this._endY + dy - ny);
  3338. this._endsWithAWall = isWallCallback(this._endX + dx, this._endY + dy);
  3339. if ((firstCornerBad || secondCornerBad) && this._endsWithAWall) { return false; }
  3340. return true;
  3341. }
  3342. /**
  3343. * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty.
  3344. */
  3345. ROT.Map.Feature.Corridor.prototype.create = function(digCallback) {
  3346. var sx = this._startX;
  3347. var sy = this._startY;
  3348. var dx = this._endX-sx;
  3349. var dy = this._endY-sy;
  3350. var length = 1+Math.max(Math.abs(dx), Math.abs(dy));
  3351. if (dx) { dx = dx/Math.abs(dx); }
  3352. if (dy) { dy = dy/Math.abs(dy); }
  3353. var nx = dy;
  3354. var ny = -dx;
  3355. for (var i=0; i<length; i++) {
  3356. var x = sx + i*dx;
  3357. var y = sy + i*dy;
  3358. digCallback(x, y, 0);
  3359. }
  3360. return true;
  3361. }
  3362. ROT.Map.Feature.Corridor.prototype.createPriorityWalls = function(priorityWallCallback) {
  3363. if (!this._endsWithAWall) { return; }
  3364. var sx = this._startX;
  3365. var sy = this._startY;
  3366. var dx = this._endX-sx;
  3367. var dy = this._endY-sy;
  3368. if (dx) { dx = dx/Math.abs(dx); }
  3369. if (dy) { dy = dy/Math.abs(dy); }
  3370. var nx = dy;
  3371. var ny = -dx;
  3372. priorityWallCallback(this._endX + dx, this._endY + dy);
  3373. priorityWallCallback(this._endX + nx, this._endY + ny);
  3374. priorityWallCallback(this._endX - nx, this._endY - ny);
  3375. }
  3376. /**
  3377. * @class Base noise generator
  3378. */
  3379. ROT.Noise = function() {
  3380. };
  3381. ROT.Noise.prototype.get = function(x, y) {}
  3382. /**
  3383. * A simple 2d implementation of simplex noise by Ondrej Zara
  3384. *
  3385. * Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
  3386. * Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).
  3387. * With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
  3388. * Better rank ordering method by Stefan Gustavson in 2012.
  3389. */
  3390. /**
  3391. * @class 2D simplex noise generator
  3392. * @param {int} [gradients=256] Random gradients
  3393. */
  3394. ROT.Noise.Simplex = function(gradients) {
  3395. ROT.Noise.call(this);
  3396. this._F2 = 0.5 * (Math.sqrt(3) - 1);
  3397. this._G2 = (3 - Math.sqrt(3)) / 6;
  3398. this._gradients = [
  3399. [ 0, -1],
  3400. [ 1, -1],
  3401. [ 1, 0],
  3402. [ 1, 1],
  3403. [ 0, 1],
  3404. [-1, 1],
  3405. [-1, 0],
  3406. [-1, -1]
  3407. ];
  3408. var permutations = [];
  3409. var count = gradients || 256;
  3410. for (var i=0;i<count;i++) { permutations.push(i); }
  3411. permutations = permutations.randomize();
  3412. this._perms = [];
  3413. this._indexes = [];
  3414. for (var i=0;i<2*count;i++) {
  3415. this._perms.push(permutations[i % count]);
  3416. this._indexes.push(this._perms[i] % this._gradients.length);
  3417. }
  3418. };
  3419. ROT.Noise.Simplex.extend(ROT.Noise);
  3420. ROT.Noise.Simplex.prototype.get = function(xin, yin) {
  3421. var perms = this._perms;
  3422. var indexes = this._indexes;
  3423. var count = perms.length/2;
  3424. var G2 = this._G2;
  3425. var n0 =0, n1 = 0, n2 = 0, gi; // Noise contributions from the three corners
  3426. // Skew the input space to determine which simplex cell we're in
  3427. var s = (xin + yin) * this._F2; // Hairy factor for 2D
  3428. var i = Math.floor(xin + s);
  3429. var j = Math.floor(yin + s);
  3430. var t = (i + j) * G2;
  3431. var X0 = i - t; // Unskew the cell origin back to (x,y) space
  3432. var Y0 = j - t;
  3433. var x0 = xin - X0; // The x,y distances from the cell origin
  3434. var y0 = yin - Y0;
  3435. // For the 2D case, the simplex shape is an equilateral triangle.
  3436. // Determine which simplex we are in.
  3437. var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
  3438. if (x0 > y0) {
  3439. i1 = 1;
  3440. j1 = 0;
  3441. } else { // lower triangle, XY order: (0,0)->(1,0)->(1,1)
  3442. i1 = 0;
  3443. j1 = 1;
  3444. } // upper triangle, YX order: (0,0)->(0,1)->(1,1)
  3445. // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
  3446. // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
  3447. // c = (3-sqrt(3))/6
  3448. var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
  3449. var y1 = y0 - j1 + G2;
  3450. var x2 = x0 - 1 + 2*G2; // Offsets for last corner in (x,y) unskewed coords
  3451. var y2 = y0 - 1 + 2*G2;
  3452. // Work out the hashed gradient indices of the three simplex corners
  3453. var ii = i.mod(count);
  3454. var jj = j.mod(count);
  3455. // Calculate the contribution from the three corners
  3456. var t0 = 0.5 - x0*x0 - y0*y0;
  3457. if (t0 >= 0) {
  3458. t0 *= t0;
  3459. gi = indexes[ii+perms[jj]];
  3460. var grad = this._gradients[gi];
  3461. n0 = t0 * t0 * (grad[0] * x0 + grad[1] * y0);
  3462. }
  3463. var t1 = 0.5 - x1*x1 - y1*y1;
  3464. if (t1 >= 0) {
  3465. t1 *= t1;
  3466. gi = indexes[ii+i1+perms[jj+j1]];
  3467. var grad = this._gradients[gi];
  3468. n1 = t1 * t1 * (grad[0] * x1 + grad[1] * y1);
  3469. }
  3470. var t2 = 0.5 - x2*x2 - y2*y2;
  3471. if (t2 >= 0) {
  3472. t2 *= t2;
  3473. gi = indexes[ii+1+perms[jj+1]];
  3474. var grad = this._gradients[gi];
  3475. n2 = t2 * t2 * (grad[0] * x2 + grad[1] * y2);
  3476. }
  3477. // Add contributions from each corner to get the final noise value.
  3478. // The result is scaled to return values in the interval [-1,1].
  3479. return 70 * (n0 + n1 + n2);
  3480. }
  3481. /**
  3482. * @class Abstract FOV algorithm
  3483. * @param {function} lightPassesCallback Does the light pass through x,y?
  3484. * @param {object} [options]
  3485. * @param {int} [options.topology=8] 4/6/8
  3486. */
  3487. ROT.FOV = function(lightPassesCallback, options) {
  3488. this._lightPasses = lightPassesCallback;
  3489. this._options = {
  3490. topology: 8
  3491. }
  3492. for (var p in options) { this._options[p] = options[p]; }
  3493. };
  3494. /**
  3495. * Compute visibility for a 360-degree circle
  3496. * @param {int} x
  3497. * @param {int} y
  3498. * @param {int} R Maximum visibility radius
  3499. * @param {function} callback
  3500. */
  3501. ROT.FOV.prototype.compute = function(x, y, R, callback) {}
  3502. /**
  3503. * Return all neighbors in a concentric ring
  3504. * @param {int} cx center-x
  3505. * @param {int} cy center-y
  3506. * @param {int} r range
  3507. */
  3508. ROT.FOV.prototype._getCircle = function(cx, cy, r) {
  3509. var result = [];
  3510. var dirs, countFactor, startOffset;
  3511. switch (this._options.topology) {
  3512. case 4:
  3513. countFactor = 1;
  3514. startOffset = [0, 1];
  3515. dirs = [
  3516. ROT.DIRS[8][7],
  3517. ROT.DIRS[8][1],
  3518. ROT.DIRS[8][3],
  3519. ROT.DIRS[8][5]
  3520. ]
  3521. break;
  3522. case 6:
  3523. dirs = ROT.DIRS[6];
  3524. countFactor = 1;
  3525. startOffset = [-1, 1];
  3526. break;
  3527. case 8:
  3528. dirs = ROT.DIRS[4];
  3529. countFactor = 2;
  3530. startOffset = [-1, 1];
  3531. break;
  3532. }
  3533. /* starting neighbor */
  3534. var x = cx + startOffset[0]*r;
  3535. var y = cy + startOffset[1]*r;
  3536. /* circle */
  3537. for (var i=0;i<dirs.length;i++) {
  3538. for (var j=0;j<r*countFactor;j++) {
  3539. result.push([x, y]);
  3540. x += dirs[i][0];
  3541. y += dirs[i][1];
  3542. }
  3543. }
  3544. return result;
  3545. }
  3546. /**
  3547. * @class Discrete shadowcasting algorithm. Obsoleted by Precise shadowcasting.
  3548. * @augments ROT.FOV
  3549. */
  3550. ROT.FOV.DiscreteShadowcasting = function(lightPassesCallback, options) {
  3551. ROT.FOV.call(this, lightPassesCallback, options);
  3552. }
  3553. ROT.FOV.DiscreteShadowcasting.extend(ROT.FOV);
  3554. /**
  3555. * @see ROT.FOV#compute
  3556. */
  3557. ROT.FOV.DiscreteShadowcasting.prototype.compute = function(x, y, R, callback) {
  3558. var center = this._coords;
  3559. var map = this._map;
  3560. /* this place is always visible */
  3561. callback(x, y, 0, 1);
  3562. /* standing in a dark place. FIXME is this a good idea? */
  3563. if (!this._lightPasses(x, y)) { return; }
  3564. /* start and end angles */
  3565. var DATA = [];
  3566. var A, B, cx, cy, blocks;
  3567. /* analyze surrounding cells in concentric rings, starting from the center */
  3568. for (var r=1; r<=R; r++) {
  3569. var neighbors = this._getCircle(x, y, r);
  3570. var angle = 360 / neighbors.length;
  3571. for (var i=0;i<neighbors.length;i++) {
  3572. cx = neighbors[i][0];
  3573. cy = neighbors[i][1];
  3574. A = angle * (i - 0.5);
  3575. B = A + angle;
  3576. blocks = !this._lightPasses(cx, cy);
  3577. if (this._visibleCoords(Math.floor(A), Math.ceil(B), blocks, DATA)) { callback(cx, cy, r, 1); }
  3578. if (DATA.length == 2 && DATA[0] == 0 && DATA[1] == 360) { return; } /* cutoff? */
  3579. } /* for all cells in this ring */
  3580. } /* for all rings */
  3581. }
  3582. /**
  3583. * @param {int} A start angle
  3584. * @param {int} B end angle
  3585. * @param {bool} blocks Does current cell block visibility?
  3586. * @param {int[][]} DATA shadowed angle pairs
  3587. */
  3588. ROT.FOV.DiscreteShadowcasting.prototype._visibleCoords = function(A, B, blocks, DATA) {
  3589. if (A < 0) {
  3590. var v1 = arguments.callee(0, B, blocks, DATA);
  3591. var v2 = arguments.callee(360+A, 360, blocks, DATA);
  3592. return v1 || v2;
  3593. }
  3594. var index = 0;
  3595. while (index < DATA.length && DATA[index] < A) { index++; }
  3596. if (index == DATA.length) { /* completely new shadow */
  3597. if (blocks) { DATA.push(A, B); }
  3598. return true;
  3599. }
  3600. var count = 0;
  3601. if (index % 2) { /* this shadow starts in an existing shadow, or within its ending boundary */
  3602. while (index < DATA.length && DATA[index] < B) {
  3603. index++;
  3604. count++;
  3605. }
  3606. if (count == 0) { return false; }
  3607. if (blocks) {
  3608. if (count % 2) {
  3609. DATA.splice(index-count, count, B);
  3610. } else {
  3611. DATA.splice(index-count, count);
  3612. }
  3613. }
  3614. return true;
  3615. } else { /* this shadow starts outside an existing shadow, or within a starting boundary */
  3616. while (index < DATA.length && DATA[index] < B) {
  3617. index++;
  3618. count++;
  3619. }
  3620. /* visible when outside an existing shadow, or when overlapping */
  3621. if (A == DATA[index-count] && count == 1) { return false; }
  3622. if (blocks) {
  3623. if (count % 2) {
  3624. DATA.splice(index-count, count, A);
  3625. } else {
  3626. DATA.splice(index-count, count, A, B);
  3627. }
  3628. }
  3629. return true;
  3630. }
  3631. }
  3632. /**
  3633. * @class Precise shadowcasting algorithm
  3634. * @augments ROT.FOV
  3635. */
  3636. ROT.FOV.PreciseShadowcasting = function(lightPassesCallback, options) {
  3637. ROT.FOV.call(this, lightPassesCallback, options);
  3638. }
  3639. ROT.FOV.PreciseShadowcasting.extend(ROT.FOV);
  3640. /**
  3641. * @see ROT.FOV#compute
  3642. */
  3643. ROT.FOV.PreciseShadowcasting.prototype.compute = function(x, y, R, callback) {
  3644. /* this place is always visible */
  3645. callback(x, y, 0, 1);
  3646. /* standing in a dark place. FIXME is this a good idea? */
  3647. if (!this._lightPasses(x, y)) { return; }
  3648. /* list of all shadows */
  3649. var SHADOWS = [];
  3650. var cx, cy, blocks, A1, A2, visibility;
  3651. /* analyze surrounding cells in concentric rings, starting from the center */
  3652. for (var r=1; r<=R; r++) {
  3653. var neighbors = this._getCircle(x, y, r);
  3654. var neighborCount = neighbors.length;
  3655. for (var i=0;i<neighborCount;i++) {
  3656. cx = neighbors[i][0];
  3657. cy = neighbors[i][1];
  3658. /* shift half-an-angle backwards to maintain consistency of 0-th cells */
  3659. A1 = [i ? 2*i-1 : 2*neighborCount-1, 2*neighborCount];
  3660. A2 = [2*i+1, 2*neighborCount];
  3661. blocks = !this._lightPasses(cx, cy);
  3662. visibility = this._checkVisibility(A1, A2, blocks, SHADOWS);
  3663. if (visibility) { callback(cx, cy, r, visibility); }
  3664. if (SHADOWS.length == 2 && SHADOWS[0][0] == 0 && SHADOWS[1][0] == SHADOWS[1][1]) { return; } /* cutoff? */
  3665. } /* for all cells in this ring */
  3666. } /* for all rings */
  3667. }
  3668. /**
  3669. * @param {int[2]} A1 arc start
  3670. * @param {int[2]} A2 arc end
  3671. * @param {bool} blocks Does current arc block visibility?
  3672. * @param {int[][]} SHADOWS list of active shadows
  3673. */
  3674. ROT.FOV.PreciseShadowcasting.prototype._checkVisibility = function(A1, A2, blocks, SHADOWS) {
  3675. if (A1[0] > A2[0]) { /* split into two sub-arcs */
  3676. var v1 = this._checkVisibility(A1, [A1[1], A1[1]], blocks, SHADOWS);
  3677. var v2 = this._checkVisibility([0, 1], A2, blocks, SHADOWS);
  3678. return (v1+v2)/2;
  3679. }
  3680. /* index1: first shadow >= A1 */
  3681. var index1 = 0, edge1 = false;
  3682. while (index1 < SHADOWS.length) {
  3683. var old = SHADOWS[index1];
  3684. var diff = old[0]*A1[1] - A1[0]*old[1];
  3685. if (diff >= 0) { /* old >= A1 */
  3686. if (diff == 0 && !(index1 % 2)) { edge1 = true; }
  3687. break;
  3688. }
  3689. index1++;
  3690. }
  3691. /* index2: last shadow <= A2 */
  3692. var index2 = SHADOWS.length, edge2 = false;
  3693. while (index2--) {
  3694. var old = SHADOWS[index2];
  3695. var diff = A2[0]*old[1] - old[0]*A2[1];
  3696. if (diff >= 0) { /* old <= A2 */
  3697. if (diff == 0 && (index2 % 2)) { edge2 = true; }
  3698. break;
  3699. }
  3700. }
  3701. var visible = true;
  3702. if (index1 == index2 && (edge1 || edge2)) { /* subset of existing shadow, one of the edges match */
  3703. visible = false;
  3704. } else if (edge1 && edge2 && index1+1==index2 && (index2 % 2)) { /* completely equivalent with existing shadow */
  3705. visible = false;
  3706. } else if (index1 > index2 && (index1 % 2)) { /* subset of existing shadow, not touching */
  3707. visible = false;
  3708. }
  3709. if (!visible) { return 0; } /* fast case: not visible */
  3710. var visibleLength, P;
  3711. /* compute the length of visible arc, adjust list of shadows (if blocking) */
  3712. var remove = index2-index1+1;
  3713. if (remove % 2) {
  3714. if (index1 % 2) { /* first edge within existing shadow, second outside */
  3715. var P = SHADOWS[index1];
  3716. visibleLength = (A2[0]*P[1] - P[0]*A2[1]) / (P[1] * A2[1]);
  3717. if (blocks) { SHADOWS.splice(index1, remove, A2); }
  3718. } else { /* second edge within existing shadow, first outside */
  3719. var P = SHADOWS[index2];
  3720. visibleLength = (P[0]*A1[1] - A1[0]*P[1]) / (A1[1] * P[1]);
  3721. if (blocks) { SHADOWS.splice(index1, remove, A1); }
  3722. }
  3723. } else {
  3724. if (index1 % 2) { /* both edges within existing shadows */
  3725. var P1 = SHADOWS[index1];
  3726. var P2 = SHADOWS[index2];
  3727. visibleLength = (P2[0]*P1[1] - P1[0]*P2[1]) / (P1[1] * P2[1]);
  3728. if (blocks) { SHADOWS.splice(index1, remove); }
  3729. } else { /* both edges outside existing shadows */
  3730. if (blocks) { SHADOWS.splice(index1, remove, A1, A2); }
  3731. return 1; /* whole arc visible! */
  3732. }
  3733. }
  3734. var arcLength = (A2[0]*A1[1] - A1[0]*A2[1]) / (A1[1] * A2[1]);
  3735. return visibleLength/arcLength;
  3736. }
  3737. /**
  3738. * @class Recursive shadowcasting algorithm
  3739. * Currently only supports 4/8 topologies, not hexagonal.
  3740. * Based on Peter Harkins' implementation of Björn Bergström's algorithm described here: http://www.roguebasin.com/index.php?title=FOV_using_recursive_shadowcasting
  3741. * @augments ROT.FOV
  3742. */
  3743. ROT.FOV.RecursiveShadowcasting = function(lightPassesCallback, options) {
  3744. ROT.FOV.call(this, lightPassesCallback, options);
  3745. }
  3746. ROT.FOV.RecursiveShadowcasting.extend(ROT.FOV);
  3747. /** Octants used for translating recursive shadowcasting offsets */
  3748. ROT.FOV.RecursiveShadowcasting.OCTANTS = [
  3749. [-1, 0, 0, 1],
  3750. [ 0, -1, 1, 0],
  3751. [ 0, -1, -1, 0],
  3752. [-1, 0, 0, -1],
  3753. [ 1, 0, 0, -1],
  3754. [ 0, 1, -1, 0],
  3755. [ 0, 1, 1, 0],
  3756. [ 1, 0, 0, 1]
  3757. ];
  3758. /**
  3759. * Compute visibility for a 360-degree circle
  3760. * @param {int} x
  3761. * @param {int} y
  3762. * @param {int} R Maximum visibility radius
  3763. * @param {function} callback
  3764. */
  3765. ROT.FOV.RecursiveShadowcasting.prototype.compute = function(x, y, R, callback) {
  3766. //You can always see your own tile
  3767. callback(x, y, 0, 1);
  3768. for(var i = 0; i < ROT.FOV.RecursiveShadowcasting.OCTANTS.length; i++) {
  3769. this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[i], R, callback);
  3770. }
  3771. }
  3772. /**
  3773. * Compute visibility for a 180-degree arc
  3774. * @param {int} x
  3775. * @param {int} y
  3776. * @param {int} R Maximum visibility radius
  3777. * @param {int} dir Direction to look in (expressed in a ROT.DIRS value);
  3778. * @param {function} callback
  3779. */
  3780. ROT.FOV.RecursiveShadowcasting.prototype.compute180 = function(x, y, R, dir, callback) {
  3781. //You can always see your own tile
  3782. callback(x, y, 0, 1);
  3783. var previousOctant = (dir - 1 + 8) % 8; //Need to retrieve the previous octant to render a full 180 degrees
  3784. var nextPreviousOctant = (dir - 2 + 8) % 8; //Need to retrieve the previous two octants to render a full 180 degrees
  3785. var nextOctant = (dir+ 1 + 8) % 8; //Need to grab to next octant to render a full 180 degrees
  3786. this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[nextPreviousOctant], R, callback);
  3787. this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[previousOctant], R, callback);
  3788. this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[dir], R, callback);
  3789. this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[nextOctant], R, callback);
  3790. }
  3791. /**
  3792. * Compute visibility for a 90-degree arc
  3793. * @param {int} x
  3794. * @param {int} y
  3795. * @param {int} R Maximum visibility radius
  3796. * @param {int} dir Direction to look in (expressed in a ROT.DIRS value);
  3797. * @param {function} callback
  3798. */
  3799. ROT.FOV.RecursiveShadowcasting.prototype.compute90 = function(x, y, R, dir, callback) {
  3800. //You can always see your own tile
  3801. callback(x, y, 0, 1);
  3802. var previousOctant = (dir - 1 + 8) % 8; //Need to retrieve the previous octant to render a full 90 degrees
  3803. this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[dir], R, callback);
  3804. this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[previousOctant], R, callback);
  3805. }
  3806. /**
  3807. * Render one octant (45-degree arc) of the viewshed
  3808. * @param {int} x
  3809. * @param {int} y
  3810. * @param {int} octant Octant to be rendered
  3811. * @param {int} R Maximum visibility radius
  3812. * @param {function} callback
  3813. */
  3814. ROT.FOV.RecursiveShadowcasting.prototype._renderOctant = function(x, y, octant, R, callback) {
  3815. //Radius incremented by 1 to provide same coverage area as other shadowcasting radiuses
  3816. this._castVisibility(x, y, 1, 1.0, 0.0, R + 1, octant[0], octant[1], octant[2], octant[3], callback);
  3817. }
  3818. /**
  3819. * Actually calculates the visibility
  3820. * @param {int} startX The starting X coordinate
  3821. * @param {int} startY The starting Y coordinate
  3822. * @param {int} row The row to render
  3823. * @param {float} visSlopeStart The slope to start at
  3824. * @param {float} visSlopeEnd The slope to end at
  3825. * @param {int} radius The radius to reach out to
  3826. * @param {int} xx
  3827. * @param {int} xy
  3828. * @param {int} yx
  3829. * @param {int} yy
  3830. * @param {function} callback The callback to use when we hit a block that is visible
  3831. */
  3832. ROT.FOV.RecursiveShadowcasting.prototype._castVisibility = function(startX, startY, row, visSlopeStart, visSlopeEnd, radius, xx, xy, yx, yy, callback) {
  3833. if(visSlopeStart < visSlopeEnd) { return; }
  3834. for(var i = row; i <= radius; i++) {
  3835. var dx = -i - 1;
  3836. var dy = -i;
  3837. var blocked = false;
  3838. var newStart = 0;
  3839. //'Row' could be column, names here assume octant 0 and would be flipped for half the octants
  3840. while(dx <= 0) {
  3841. dx += 1;
  3842. //Translate from relative coordinates to map coordinates
  3843. var mapX = startX + dx * xx + dy * xy;
  3844. var mapY = startY + dx * yx + dy * yy;
  3845. //Range of the row
  3846. var slopeStart = (dx - 0.5) / (dy + 0.5);
  3847. var slopeEnd = (dx + 0.5) / (dy - 0.5);
  3848. //Ignore if not yet at left edge of Octant
  3849. if(slopeEnd > visSlopeStart) { continue; }
  3850. //Done if past right edge
  3851. if(slopeStart < visSlopeEnd) { break; }
  3852. //If it's in range, it's visible
  3853. if((dx * dx + dy * dy) < (radius * radius)) {
  3854. callback(mapX, mapY, i, 1);
  3855. }
  3856. if(!blocked) {
  3857. //If tile is a blocking tile, cast around it
  3858. if(!this._lightPasses(mapX, mapY) && i < radius) {
  3859. blocked = true;
  3860. this._castVisibility(startX, startY, i + 1, visSlopeStart, slopeStart, radius, xx, xy, yx, yy, callback);
  3861. newStart = slopeEnd;
  3862. }
  3863. } else {
  3864. //Keep narrowing if scanning across a block
  3865. if(!this._lightPasses(mapX, mapY)) {
  3866. newStart = slopeEnd;
  3867. continue;
  3868. }
  3869. //Block has ended
  3870. blocked = false;
  3871. visSlopeStart = newStart;
  3872. }
  3873. }
  3874. if(blocked) { break; }
  3875. }
  3876. }
  3877. /**
  3878. * @namespace Color operations
  3879. */
  3880. ROT.Color = {
  3881. fromString: function(str) {
  3882. var cached, r;
  3883. if (str in this._cache) {
  3884. cached = this._cache[str];
  3885. } else {
  3886. if (str.charAt(0) == "#") { /* hex rgb */
  3887. var values = str.match(/[0-9a-f]/gi).map(function(x) { return parseInt(x, 16); });
  3888. if (values.length == 3) {
  3889. cached = values.map(function(x) { return x*17; });
  3890. } else {
  3891. for (var i=0;i<3;i++) {
  3892. values[i+1] += 16*values[i];
  3893. values.splice(i, 1);
  3894. }
  3895. cached = values;
  3896. }
  3897. } else if ((r = str.match(/rgb\(([0-9, ]+)\)/i))) { /* decimal rgb */
  3898. cached = r[1].split(/\s*,\s*/).map(function(x) { return parseInt(x); });
  3899. } else { /* html name */
  3900. cached = [0, 0, 0];
  3901. }
  3902. this._cache[str] = cached;
  3903. }
  3904. return cached.slice();
  3905. },
  3906. /**
  3907. * Add two or more colors
  3908. * @param {number[]} color1
  3909. * @param {number[]} color2
  3910. * @returns {number[]}
  3911. */
  3912. add: function(color1, color2) {
  3913. var result = color1.slice();
  3914. for (var i=0;i<3;i++) {
  3915. for (var j=1;j<arguments.length;j++) {
  3916. result[i] += arguments[j][i];
  3917. }
  3918. }
  3919. return result;
  3920. },
  3921. /**
  3922. * Add two or more colors, MODIFIES FIRST ARGUMENT
  3923. * @param {number[]} color1
  3924. * @param {number[]} color2
  3925. * @returns {number[]}
  3926. */
  3927. add_: function(color1, color2) {
  3928. for (var i=0;i<3;i++) {
  3929. for (var j=1;j<arguments.length;j++) {
  3930. color1[i] += arguments[j][i];
  3931. }
  3932. }
  3933. return color1;
  3934. },
  3935. /**
  3936. * Multiply (mix) two or more colors
  3937. * @param {number[]} color1
  3938. * @param {number[]} color2
  3939. * @returns {number[]}
  3940. */
  3941. multiply: function(color1, color2) {
  3942. var result = color1.slice();
  3943. for (var i=0;i<3;i++) {
  3944. for (var j=1;j<arguments.length;j++) {
  3945. result[i] *= arguments[j][i] / 255;
  3946. }
  3947. result[i] = Math.round(result[i]);
  3948. }
  3949. return result;
  3950. },
  3951. /**
  3952. * Multiply (mix) two or more colors, MODIFIES FIRST ARGUMENT
  3953. * @param {number[]} color1
  3954. * @param {number[]} color2
  3955. * @returns {number[]}
  3956. */
  3957. multiply_: function(color1, color2) {
  3958. for (var i=0;i<3;i++) {
  3959. for (var j=1;j<arguments.length;j++) {
  3960. color1[i] *= arguments[j][i] / 255;
  3961. }
  3962. color1[i] = Math.round(color1[i]);
  3963. }
  3964. return color1;
  3965. },
  3966. /**
  3967. * Interpolate (blend) two colors with a given factor
  3968. * @param {number[]} color1
  3969. * @param {number[]} color2
  3970. * @param {float} [factor=0.5] 0..1
  3971. * @returns {number[]}
  3972. */
  3973. interpolate: function(color1, color2, factor) {
  3974. if (arguments.length < 3) { factor = 0.5; }
  3975. var result = color1.slice();
  3976. for (var i=0;i<3;i++) {
  3977. result[i] = Math.round(result[i] + factor*(color2[i]-color1[i]));
  3978. }
  3979. return result;
  3980. },
  3981. /**
  3982. * Interpolate (blend) two colors with a given factor in HSL mode
  3983. * @param {number[]} color1
  3984. * @param {number[]} color2
  3985. * @param {float} [factor=0.5] 0..1
  3986. * @returns {number[]}
  3987. */
  3988. interpolateHSL: function(color1, color2, factor) {
  3989. if (arguments.length < 3) { factor = 0.5; }
  3990. var hsl1 = this.rgb2hsl(color1);
  3991. var hsl2 = this.rgb2hsl(color2);
  3992. for (var i=0;i<3;i++) {
  3993. hsl1[i] += factor*(hsl2[i]-hsl1[i]);
  3994. }
  3995. return this.hsl2rgb(hsl1);
  3996. },
  3997. /**
  3998. * Create a new random color based on this one
  3999. * @param {number[]} color
  4000. * @param {number[]} diff Set of standard deviations
  4001. * @returns {number[]}
  4002. */
  4003. randomize: function(color, diff) {
  4004. if (!(diff instanceof Array)) { diff = Math.round(ROT.RNG.getNormal(0, diff)); }
  4005. var result = color.slice();
  4006. for (var i=0;i<3;i++) {
  4007. result[i] += (diff instanceof Array ? Math.round(ROT.RNG.getNormal(0, diff[i])) : diff);
  4008. }
  4009. return result;
  4010. },
  4011. /**
  4012. * Converts an RGB color value to HSL. Expects 0..255 inputs, produces 0..1 outputs.
  4013. * @param {number[]} color
  4014. * @returns {number[]}
  4015. */
  4016. rgb2hsl: function(color) {
  4017. var r = color[0]/255;
  4018. var g = color[1]/255;
  4019. var b = color[2]/255;
  4020. var max = Math.max(r, g, b), min = Math.min(r, g, b);
  4021. var h, s, l = (max + min) / 2;
  4022. if (max == min) {
  4023. h = s = 0; // achromatic
  4024. } else {
  4025. var d = max - min;
  4026. s = (l > 0.5 ? d / (2 - max - min) : d / (max + min));
  4027. switch(max) {
  4028. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  4029. case g: h = (b - r) / d + 2; break;
  4030. case b: h = (r - g) / d + 4; break;
  4031. }
  4032. h /= 6;
  4033. }
  4034. return [h, s, l];
  4035. },
  4036. /**
  4037. * Converts an HSL color value to RGB. Expects 0..1 inputs, produces 0..255 outputs.
  4038. * @param {number[]} color
  4039. * @returns {number[]}
  4040. */
  4041. hsl2rgb: function(color) {
  4042. var l = color[2];
  4043. if (color[1] == 0) {
  4044. l = Math.round(l*255);
  4045. return [l, l, l];
  4046. } else {
  4047. var hue2rgb = function(p, q, t) {
  4048. if (t < 0) t += 1;
  4049. if (t > 1) t -= 1;
  4050. if (t < 1/6) return p + (q - p) * 6 * t;
  4051. if (t < 1/2) return q;
  4052. if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  4053. return p;
  4054. }
  4055. var s = color[1];
  4056. var q = (l < 0.5 ? l * (1 + s) : l + s - l * s);
  4057. var p = 2 * l - q;
  4058. var r = hue2rgb(p, q, color[0] + 1/3);
  4059. var g = hue2rgb(p, q, color[0]);
  4060. var b = hue2rgb(p, q, color[0] - 1/3);
  4061. return [Math.round(r*255), Math.round(g*255), Math.round(b*255)];
  4062. }
  4063. },
  4064. toRGB: function(color) {
  4065. return "rgb(" + this._clamp(color[0]) + "," + this._clamp(color[1]) + "," + this._clamp(color[2]) + ")";
  4066. },
  4067. toHex: function(color) {
  4068. var parts = [];
  4069. for (var i=0;i<3;i++) {
  4070. parts.push(this._clamp(color[i]).toString(16).lpad("0", 2));
  4071. }
  4072. return "#" + parts.join("");
  4073. },
  4074. _clamp: function(num) {
  4075. if (num < 0) {
  4076. return 0;
  4077. } else if (num > 255) {
  4078. return 255;
  4079. } else {
  4080. return num;
  4081. }
  4082. },
  4083. _cache: {
  4084. "black": [0,0,0],
  4085. "navy": [0,0,128],
  4086. "darkblue": [0,0,139],
  4087. "mediumblue": [0,0,205],
  4088. "blue": [0,0,255],
  4089. "darkgreen": [0,100,0],
  4090. "green": [0,128,0],
  4091. "teal": [0,128,128],
  4092. "darkcyan": [0,139,139],
  4093. "deepskyblue": [0,191,255],
  4094. "darkturquoise": [0,206,209],
  4095. "mediumspringgreen": [0,250,154],
  4096. "lime": [0,255,0],
  4097. "springgreen": [0,255,127],
  4098. "aqua": [0,255,255],
  4099. "cyan": [0,255,255],
  4100. "midnightblue": [25,25,112],
  4101. "dodgerblue": [30,144,255],
  4102. "forestgreen": [34,139,34],
  4103. "seagreen": [46,139,87],
  4104. "darkslategray": [47,79,79],
  4105. "darkslategrey": [47,79,79],
  4106. "limegreen": [50,205,50],
  4107. "mediumseagreen": [60,179,113],
  4108. "turquoise": [64,224,208],
  4109. "royalblue": [65,105,225],
  4110. "steelblue": [70,130,180],
  4111. "darkslateblue": [72,61,139],
  4112. "mediumturquoise": [72,209,204],
  4113. "indigo": [75,0,130],
  4114. "darkolivegreen": [85,107,47],
  4115. "cadetblue": [95,158,160],
  4116. "cornflowerblue": [100,149,237],
  4117. "mediumaquamarine": [102,205,170],
  4118. "dimgray": [105,105,105],
  4119. "dimgrey": [105,105,105],
  4120. "slateblue": [106,90,205],
  4121. "olivedrab": [107,142,35],
  4122. "slategray": [112,128,144],
  4123. "slategrey": [112,128,144],
  4124. "lightslategray": [119,136,153],
  4125. "lightslategrey": [119,136,153],
  4126. "mediumslateblue": [123,104,238],
  4127. "lawngreen": [124,252,0],
  4128. "chartreuse": [127,255,0],
  4129. "aquamarine": [127,255,212],
  4130. "maroon": [128,0,0],
  4131. "purple": [128,0,128],
  4132. "olive": [128,128,0],
  4133. "gray": [128,128,128],
  4134. "grey": [128,128,128],
  4135. "skyblue": [135,206,235],
  4136. "lightskyblue": [135,206,250],
  4137. "blueviolet": [138,43,226],
  4138. "darkred": [139,0,0],
  4139. "darkmagenta": [139,0,139],
  4140. "saddlebrown": [139,69,19],
  4141. "darkseagreen": [143,188,143],
  4142. "lightgreen": [144,238,144],
  4143. "mediumpurple": [147,112,216],
  4144. "darkviolet": [148,0,211],
  4145. "palegreen": [152,251,152],
  4146. "darkorchid": [153,50,204],
  4147. "yellowgreen": [154,205,50],
  4148. "sienna": [160,82,45],
  4149. "brown": [165,42,42],
  4150. "darkgray": [169,169,169],
  4151. "darkgrey": [169,169,169],
  4152. "lightblue": [173,216,230],
  4153. "greenyellow": [173,255,47],
  4154. "paleturquoise": [175,238,238],
  4155. "lightsteelblue": [176,196,222],
  4156. "powderblue": [176,224,230],
  4157. "firebrick": [178,34,34],
  4158. "darkgoldenrod": [184,134,11],
  4159. "mediumorchid": [186,85,211],
  4160. "rosybrown": [188,143,143],
  4161. "darkkhaki": [189,183,107],
  4162. "silver": [192,192,192],
  4163. "mediumvioletred": [199,21,133],
  4164. "indianred": [205,92,92],
  4165. "peru": [205,133,63],
  4166. "chocolate": [210,105,30],
  4167. "tan": [210,180,140],
  4168. "lightgray": [211,211,211],
  4169. "lightgrey": [211,211,211],
  4170. "palevioletred": [216,112,147],
  4171. "thistle": [216,191,216],
  4172. "orchid": [218,112,214],
  4173. "goldenrod": [218,165,32],
  4174. "crimson": [220,20,60],
  4175. "gainsboro": [220,220,220],
  4176. "plum": [221,160,221],
  4177. "burlywood": [222,184,135],
  4178. "lightcyan": [224,255,255],
  4179. "lavender": [230,230,250],
  4180. "darksalmon": [233,150,122],
  4181. "violet": [238,130,238],
  4182. "palegoldenrod": [238,232,170],
  4183. "lightcoral": [240,128,128],
  4184. "khaki": [240,230,140],
  4185. "aliceblue": [240,248,255],
  4186. "honeydew": [240,255,240],
  4187. "azure": [240,255,255],
  4188. "sandybrown": [244,164,96],
  4189. "wheat": [245,222,179],
  4190. "beige": [245,245,220],
  4191. "whitesmoke": [245,245,245],
  4192. "mintcream": [245,255,250],
  4193. "ghostwhite": [248,248,255],
  4194. "salmon": [250,128,114],
  4195. "antiquewhite": [250,235,215],
  4196. "linen": [250,240,230],
  4197. "lightgoldenrodyellow": [250,250,210],
  4198. "oldlace": [253,245,230],
  4199. "red": [255,0,0],
  4200. "fuchsia": [255,0,255],
  4201. "magenta": [255,0,255],
  4202. "deeppink": [255,20,147],
  4203. "orangered": [255,69,0],
  4204. "tomato": [255,99,71],
  4205. "hotpink": [255,105,180],
  4206. "coral": [255,127,80],
  4207. "darkorange": [255,140,0],
  4208. "lightsalmon": [255,160,122],
  4209. "orange": [255,165,0],
  4210. "lightpink": [255,182,193],
  4211. "pink": [255,192,203],
  4212. "gold": [255,215,0],
  4213. "peachpuff": [255,218,185],
  4214. "navajowhite": [255,222,173],
  4215. "moccasin": [255,228,181],
  4216. "bisque": [255,228,196],
  4217. "mistyrose": [255,228,225],
  4218. "blanchedalmond": [255,235,205],
  4219. "papayawhip": [255,239,213],
  4220. "lavenderblush": [255,240,245],
  4221. "seashell": [255,245,238],
  4222. "cornsilk": [255,248,220],
  4223. "lemonchiffon": [255,250,205],
  4224. "floralwhite": [255,250,240],
  4225. "snow": [255,250,250],
  4226. "yellow": [255,255,0],
  4227. "lightyellow": [255,255,224],
  4228. "ivory": [255,255,240],
  4229. "white": [255,255,255]
  4230. }
  4231. }
  4232. /**
  4233. * @class Lighting computation, based on a traditional FOV for multiple light sources and multiple passes.
  4234. * @param {function} reflectivityCallback Callback to retrieve cell reflectivity (0..1)
  4235. * @param {object} [options]
  4236. * @param {int} [options.passes=1] Number of passes. 1 equals to simple FOV of all light sources, >1 means a *highly simplified* radiosity-like algorithm.
  4237. * @param {int} [options.emissionThreshold=100] Cells with emissivity > threshold will be treated as light source in the next pass.
  4238. * @param {int} [options.range=10] Max light range
  4239. */
  4240. ROT.Lighting = function(reflectivityCallback, options) {
  4241. this._reflectivityCallback = reflectivityCallback;
  4242. this._options = {
  4243. passes: 1,
  4244. emissionThreshold: 100,
  4245. range: 10
  4246. };
  4247. this._fov = null;
  4248. this._lights = {};
  4249. this._reflectivityCache = {};
  4250. this._fovCache = {};
  4251. this.setOptions(options);
  4252. }
  4253. /**
  4254. * Adjust options at runtime
  4255. * @see ROT.Lighting
  4256. * @param {object} [options]
  4257. */
  4258. ROT.Lighting.prototype.setOptions = function(options) {
  4259. for (var p in options) { this._options[p] = options[p]; }
  4260. if (options && options.range) { this.reset(); }
  4261. return this;
  4262. }
  4263. /**
  4264. * Set the used Field-Of-View algo
  4265. * @param {ROT.FOV} fov
  4266. */
  4267. ROT.Lighting.prototype.setFOV = function(fov) {
  4268. this._fov = fov;
  4269. this._fovCache = {};
  4270. return this;
  4271. }
  4272. /**
  4273. * Set (or remove) a light source
  4274. * @param {int} x
  4275. * @param {int} y
  4276. * @param {null || string || number[3]} color
  4277. */
  4278. ROT.Lighting.prototype.setLight = function(x, y, color) {
  4279. var key = x+","+y;
  4280. if (color) {
  4281. this._lights[key] = (typeof(color) == "string" ? ROT.Color.fromString(color) : color);
  4282. } else {
  4283. delete this._lights[key];
  4284. }
  4285. return this;
  4286. }
  4287. /**
  4288. * Remove all light sources
  4289. */
  4290. ROT.Lighting.prototype.clearLights = function() {
  4291. this._lights = {};
  4292. }
  4293. /**
  4294. * Reset the pre-computed topology values. Call whenever the underlying map changes its light-passability.
  4295. */
  4296. ROT.Lighting.prototype.reset = function() {
  4297. this._reflectivityCache = {};
  4298. this._fovCache = {};
  4299. return this;
  4300. }
  4301. /**
  4302. * Compute the lighting
  4303. * @param {function} lightingCallback Will be called with (x, y, color) for every lit cell
  4304. */
  4305. ROT.Lighting.prototype.compute = function(lightingCallback) {
  4306. var doneCells = {};
  4307. var emittingCells = {};
  4308. var litCells = {};
  4309. for (var key in this._lights) { /* prepare emitters for first pass */
  4310. var light = this._lights[key];
  4311. emittingCells[key] = [0, 0, 0];
  4312. ROT.Color.add_(emittingCells[key], light);
  4313. }
  4314. for (var i=0;i<this._options.passes;i++) { /* main loop */
  4315. this._emitLight(emittingCells, litCells, doneCells);
  4316. if (i+1 == this._options.passes) { continue; } /* not for the last pass */
  4317. emittingCells = this._computeEmitters(litCells, doneCells);
  4318. }
  4319. for (var litKey in litCells) { /* let the user know what and how is lit */
  4320. var parts = litKey.split(",");
  4321. var x = parseInt(parts[0]);
  4322. var y = parseInt(parts[1]);
  4323. lightingCallback(x, y, litCells[litKey]);
  4324. }
  4325. return this;
  4326. }
  4327. /**
  4328. * Compute one iteration from all emitting cells
  4329. * @param {object} emittingCells These emit light
  4330. * @param {object} litCells Add projected light to these
  4331. * @param {object} doneCells These already emitted, forbid them from further calculations
  4332. */
  4333. ROT.Lighting.prototype._emitLight = function(emittingCells, litCells, doneCells) {
  4334. for (var key in emittingCells) {
  4335. var parts = key.split(",");
  4336. var x = parseInt(parts[0]);
  4337. var y = parseInt(parts[1]);
  4338. this._emitLightFromCell(x, y, emittingCells[key], litCells);
  4339. doneCells[key] = 1;
  4340. }
  4341. return this;
  4342. }
  4343. /**
  4344. * Prepare a list of emitters for next pass
  4345. * @param {object} litCells
  4346. * @param {object} doneCells
  4347. * @returns {object}
  4348. */
  4349. ROT.Lighting.prototype._computeEmitters = function(litCells, doneCells) {
  4350. var result = {};
  4351. for (var key in litCells) {
  4352. if (key in doneCells) { continue; } /* already emitted */
  4353. var color = litCells[key];
  4354. if (key in this._reflectivityCache) {
  4355. var reflectivity = this._reflectivityCache[key];
  4356. } else {
  4357. var parts = key.split(",");
  4358. var x = parseInt(parts[0]);
  4359. var y = parseInt(parts[1]);
  4360. var reflectivity = this._reflectivityCallback(x, y);
  4361. this._reflectivityCache[key] = reflectivity;
  4362. }
  4363. if (reflectivity == 0) { continue; } /* will not reflect at all */
  4364. /* compute emission color */
  4365. var emission = [];
  4366. var intensity = 0;
  4367. for (var i=0;i<3;i++) {
  4368. var part = Math.round(color[i]*reflectivity);
  4369. emission[i] = part;
  4370. intensity += part;
  4371. }
  4372. if (intensity > this._options.emissionThreshold) { result[key] = emission; }
  4373. }
  4374. return result;
  4375. }
  4376. /**
  4377. * Compute one iteration from one cell
  4378. * @param {int} x
  4379. * @param {int} y
  4380. * @param {number[]} color
  4381. * @param {object} litCells Cell data to by updated
  4382. */
  4383. ROT.Lighting.prototype._emitLightFromCell = function(x, y, color, litCells) {
  4384. var key = x+","+y;
  4385. if (key in this._fovCache) {
  4386. var fov = this._fovCache[key];
  4387. } else {
  4388. var fov = this._updateFOV(x, y);
  4389. }
  4390. for (var fovKey in fov) {
  4391. var formFactor = fov[fovKey];
  4392. if (fovKey in litCells) { /* already lit */
  4393. var result = litCells[fovKey];
  4394. } else { /* newly lit */
  4395. var result = [0, 0, 0];
  4396. litCells[fovKey] = result;
  4397. }
  4398. for (var i=0;i<3;i++) { result[i] += Math.round(color[i]*formFactor); } /* add light color */
  4399. }
  4400. return this;
  4401. }
  4402. /**
  4403. * Compute FOV ("form factor") for a potential light source at [x,y]
  4404. * @param {int} x
  4405. * @param {int} y
  4406. * @returns {object}
  4407. */
  4408. ROT.Lighting.prototype._updateFOV = function(x, y) {
  4409. var key1 = x+","+y;
  4410. var cache = {};
  4411. this._fovCache[key1] = cache;
  4412. var range = this._options.range;
  4413. var cb = function(x, y, r, vis) {
  4414. var key2 = x+","+y;
  4415. var formFactor = vis * (1-r/range);
  4416. if (formFactor == 0) { return; }
  4417. cache[key2] = formFactor;
  4418. }
  4419. this._fov.compute(x, y, range, cb.bind(this));
  4420. return cache;
  4421. }
  4422. /**
  4423. * @class Abstract pathfinder
  4424. * @param {int} toX Target X coord
  4425. * @param {int} toY Target Y coord
  4426. * @param {function} passableCallback Callback to determine map passability
  4427. * @param {object} [options]
  4428. * @param {int} [options.topology=8]
  4429. */
  4430. ROT.Path = function(toX, toY, passableCallback, options) {
  4431. this._toX = toX;
  4432. this._toY = toY;
  4433. this._fromX = null;
  4434. this._fromY = null;
  4435. this._passableCallback = passableCallback;
  4436. this._options = {
  4437. topology: 8
  4438. }
  4439. for (var p in options) { this._options[p] = options[p]; }
  4440. this._dirs = ROT.DIRS[this._options.topology];
  4441. if (this._options.topology == 8) { /* reorder dirs for more aesthetic result (vertical/horizontal first) */
  4442. this._dirs = [
  4443. this._dirs[0],
  4444. this._dirs[2],
  4445. this._dirs[4],
  4446. this._dirs[6],
  4447. this._dirs[1],
  4448. this._dirs[3],
  4449. this._dirs[5],
  4450. this._dirs[7]
  4451. ]
  4452. }
  4453. }
  4454. /**
  4455. * Compute a path from a given point
  4456. * @param {int} fromX
  4457. * @param {int} fromY
  4458. * @param {function} callback Will be called for every path item with arguments "x" and "y"
  4459. */
  4460. ROT.Path.prototype.compute = function(fromX, fromY, callback) {
  4461. }
  4462. ROT.Path.prototype._getNeighbors = function(cx, cy) {
  4463. var result = [];
  4464. for (var i=0;i<this._dirs.length;i++) {
  4465. var dir = this._dirs[i];
  4466. var x = cx + dir[0];
  4467. var y = cy + dir[1];
  4468. if (!this._passableCallback(x, y)) { continue; }
  4469. result.push([x, y]);
  4470. }
  4471. return result;
  4472. }
  4473. /**
  4474. * @class Simplified Dijkstra's algorithm: all edges have a value of 1
  4475. * @augments ROT.Path
  4476. * @see ROT.Path
  4477. */
  4478. ROT.Path.Dijkstra = function(toX, toY, passableCallback, options) {
  4479. ROT.Path.call(this, toX, toY, passableCallback, options);
  4480. this._computed = {};
  4481. this._todo = [];
  4482. this._add(toX, toY, null);
  4483. }
  4484. ROT.Path.Dijkstra.extend(ROT.Path);
  4485. /**
  4486. * Compute a path from a given point
  4487. * @see ROT.Path#compute
  4488. */
  4489. ROT.Path.Dijkstra.prototype.compute = function(fromX, fromY, callback) {
  4490. var key = fromX+","+fromY;
  4491. if (!(key in this._computed)) { this._compute(fromX, fromY); }
  4492. if (!(key in this._computed)) { return; }
  4493. var item = this._computed[key];
  4494. while (item) {
  4495. callback(item.x, item.y);
  4496. item = item.prev;
  4497. }
  4498. }
  4499. /**
  4500. * Compute a non-cached value
  4501. */
  4502. ROT.Path.Dijkstra.prototype._compute = function(fromX, fromY) {
  4503. while (this._todo.length) {
  4504. var item = this._todo.shift();
  4505. if (item.x == fromX && item.y == fromY) { return; }
  4506. var neighbors = this._getNeighbors(item.x, item.y);
  4507. for (var i=0;i<neighbors.length;i++) {
  4508. var neighbor = neighbors[i];
  4509. var x = neighbor[0];
  4510. var y = neighbor[1];
  4511. var id = x+","+y;
  4512. if (id in this._computed) { continue; } /* already done */
  4513. this._add(x, y, item);
  4514. }
  4515. }
  4516. }
  4517. ROT.Path.Dijkstra.prototype._add = function(x, y, prev) {
  4518. var obj = {
  4519. x: x,
  4520. y: y,
  4521. prev: prev
  4522. }
  4523. this._computed[x+","+y] = obj;
  4524. this._todo.push(obj);
  4525. }
  4526. /**
  4527. * @class Simplified A* algorithm: all edges have a value of 1
  4528. * @augments ROT.Path
  4529. * @see ROT.Path
  4530. */
  4531. ROT.Path.AStar = function(toX, toY, passableCallback, options) {
  4532. ROT.Path.call(this, toX, toY, passableCallback, options);
  4533. this._todo = [];
  4534. this._done = {};
  4535. this._fromX = null;
  4536. this._fromY = null;
  4537. }
  4538. ROT.Path.AStar.extend(ROT.Path);
  4539. /**
  4540. * Compute a path from a given point
  4541. * @see ROT.Path#compute
  4542. */
  4543. ROT.Path.AStar.prototype.compute = function(fromX, fromY, callback) {
  4544. this._todo = [];
  4545. this._done = {};
  4546. this._fromX = fromX;
  4547. this._fromY = fromY;
  4548. this._add(this._toX, this._toY, null);
  4549. while (this._todo.length) {
  4550. var item = this._todo.shift();
  4551. if (item.x == fromX && item.y == fromY) { break; }
  4552. var neighbors = this._getNeighbors(item.x, item.y);
  4553. for (var i=0;i<neighbors.length;i++) {
  4554. var neighbor = neighbors[i];
  4555. var x = neighbor[0];
  4556. var y = neighbor[1];
  4557. var id = x+","+y;
  4558. if (id in this._done) { continue; }
  4559. this._add(x, y, item);
  4560. }
  4561. }
  4562. var item = this._done[fromX+","+fromY];
  4563. if (!item) { return; }
  4564. while (item) {
  4565. callback(item.x, item.y);
  4566. item = item.prev;
  4567. }
  4568. }
  4569. ROT.Path.AStar.prototype._add = function(x, y, prev) {
  4570. var obj = {
  4571. x: x,
  4572. y: y,
  4573. prev: prev,
  4574. g: (prev ? prev.g+1 : 0),
  4575. h: this._distance(x, y)
  4576. }
  4577. this._done[x+","+y] = obj;
  4578. /* insert into priority queue */
  4579. var f = obj.g + obj.h;
  4580. for (var i=0;i<this._todo.length;i++) {
  4581. var item = this._todo[i];
  4582. if (f < item.g + item.h) {
  4583. this._todo.splice(i, 0, obj);
  4584. return;
  4585. }
  4586. }
  4587. this._todo.push(obj);
  4588. }
  4589. ROT.Path.AStar.prototype._distance = function(x, y) {
  4590. switch (this._options.topology) {
  4591. case 4:
  4592. return (Math.abs(x-this._fromX) + Math.abs(y-this._fromY));
  4593. break;
  4594. case 6:
  4595. var dx = Math.abs(x - this._fromX);
  4596. var dy = Math.abs(y - this._fromY);
  4597. return dy + Math.max(0, (dx-dy)/2);
  4598. break;
  4599. case 8:
  4600. return Math.max(Math.abs(x-this._fromX), Math.abs(y-this._fromY));
  4601. break;
  4602. }
  4603. throw new Error("Illegal topology");
  4604. }