parserm.h 243 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087
  1. ! ==============================================================================
  2. ! PARSERM: Core of parser.
  3. !
  4. ! Supplied for use with Inform 6 -- Release 6/12 -- Serial number 151220
  5. !
  6. ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2015
  7. !
  8. ! This code is licensed under either the traditional Inform license as
  9. ! described by the DM4 or the Artistic License version 2.0. See the
  10. ! file COPYING in the distribution archive or at
  11. ! https://github.com/DavidGriffith/inform6lib/
  12. !
  13. ! This file is automatically Included in your game file by "Parser".
  14. ! ------------------------------------------------------------------------------
  15. ! Inclusion of "linklpa" (which defines properties and attributes)
  16. ! Global variables, constants and arrays
  17. ! 1: outside of the parser
  18. ! 2: used within the parser
  19. ! Inclusion of natural language definition file
  20. ! (which creates a compass and direction-objects)
  21. ! Darkness and player objects
  22. ! Definition of grammar token numbering system used by Inform
  23. !
  24. ! The InformParser object
  25. ! keyboard reading
  26. ! level 0: outer shell, conversation, errors
  27. ! 1: grammar lines
  28. ! 2: tokens
  29. ! 3: object lists
  30. ! 4: scope and ambiguity resolving
  31. ! 5: object comparisons
  32. ! 6: word comparisons
  33. ! 7: reading words and moving tables about
  34. ! pronoun management
  35. !
  36. ! The InformLibrary object
  37. ! main game loop
  38. ! action processing
  39. ! end of turn sequence
  40. ! scope looping, before/after sequence, sending messages out
  41. ! timers, daemons, time of day, score notification
  42. ! light and darkness
  43. ! changing player personality
  44. ! tracing code (only present if DEBUG is set)
  45. !
  46. ! Status line printing, menu display
  47. ! Printing object names with articles
  48. ! Miscellaneous utility routines
  49. ! Game banner, "version" verb, run-time errors
  50. ! ==============================================================================
  51. System_file;
  52. #Ifdef MODULE_MODE;
  53. Constant DEBUG;
  54. Constant Grammar__Version 2;
  55. Include "linklpa";
  56. #Endif; ! MODULE_MODE
  57. #Ifdef COLOR;
  58. Constant COLOUR;
  59. #Endif;
  60. #Ifdef COLOUR;
  61. Global clr_on = 1;
  62. #Ifnot;
  63. Global clr_on = 0;
  64. #Endif;
  65. ! ------------------------------------------------------------------------------
  66. ! Global variables and their associated Constant and Array declarations
  67. ! ------------------------------------------------------------------------------
  68. Global location = InformLibrary; ! Must be first global defined
  69. Global sline1; ! Must be second
  70. Global sline2; ! Must be third
  71. ! (for status line display)
  72. ! ------------------------------------------------------------------------------
  73. ! Z-Machine and interpreter issues
  74. ! ------------------------------------------------------------------------------
  75. #Ifdef TARGET_ZCODE;
  76. Global top_object; ! Largest valid number of any tree object
  77. ! ### these globals are not meaningful... well, maybe standard_interpreter,
  78. ! but I'll decide that later (AP).
  79. Constant INDIV_PROP_START 64; ! Equivalent of a Glulx constant
  80. #Endif; ! TARGET_ZCODE
  81. Global standard_interpreter; ! The version number of the Z-Machine Standard which the
  82. ! interpreter claims to support, in form (upper byte).(lower)
  83. Global undo_flag; ! Can the interpreter provide "undo"?
  84. Global just_undone; ! Can't have two successive UNDOs
  85. Global transcript_mode; ! true when game scripting is on
  86. #Ifdef TARGET_ZCODE;
  87. Global xcommsdir; ! true if command recording is on
  88. #Endif; ! TARGET_ZCODE
  89. #Ifdef TARGET_GLULX;
  90. Constant GG_MAINWIN_ROCK 201;
  91. Constant GG_STATUSWIN_ROCK 202;
  92. Constant GG_QUOTEWIN_ROCK 203;
  93. Constant GG_SAVESTR_ROCK 301;
  94. Constant GG_SCRIPTSTR_ROCK 302;
  95. Constant GG_COMMANDWSTR_ROCK 303;
  96. Constant GG_COMMANDRSTR_ROCK 304;
  97. Constant GG_SCRIPTFREF_ROCK 401;
  98. Array gg_event --> 4;
  99. #Ifdef VN_1630;
  100. Array gg_arguments buffer 28;
  101. #Ifnot;
  102. Array gg_arguments --> 8;
  103. #Endif; ! VN_
  104. Global gg_mainwin = 0;
  105. Global gg_statuswin = 0;
  106. Global gg_quotewin = 0;
  107. Global gg_scriptfref = 0;
  108. Global gg_scriptstr = 0;
  109. Global gg_savestr = 0;
  110. Global gg_commandstr = 0;
  111. Global gg_command_reading = 0; ! true if gg_commandstr is being replayed
  112. #Endif; ! TARGET_GLULX
  113. Global gg_statuswin_cursize = 0;
  114. Global gg_statuswin_size = 1;
  115. ! ------------------------------------------------------------------------------
  116. ! Time and score
  117. ! (for linkage reasons, the task_* arrays are created not here but in verblib.h)
  118. ! ------------------------------------------------------------------------------
  119. #Ifndef sys_statusline_flag;
  120. Global sys_statusline_flag = 0; ! non-zero if status line displays time
  121. #Endif;
  122. #Ifndef START_MOVE;
  123. Constant START_MOVE 0; ! Traditionally 0 for Infocom, 1 for Inform
  124. #Endif;
  125. Global turns = START_MOVE; ! Number of turns of play so far
  126. Global the_time = NULL; ! Current time (in minutes since midnight)
  127. Global time_rate = 1; ! How often time is updated
  128. Global time_step; ! By how much
  129. #Ifndef MAX_TIMERS;
  130. Constant MAX_TIMERS 32; ! Max number timers/daemons active at once
  131. #Endif; ! MAX_TIMERS
  132. Array the_timers --> MAX_TIMERS;
  133. Global active_timers; ! Number of timers/daemons actives
  134. Global score; ! The current score
  135. Global last_score; ! Score last turn (for testing for changes)
  136. Global notify_mode = true; ! Score notification
  137. Global places_score; ! Contribution to score made by visiting
  138. Global things_score; ! Contribution made by acquisition
  139. ! ------------------------------------------------------------------------------
  140. ! The player
  141. ! ------------------------------------------------------------------------------
  142. Global player; ! Which object the human is playing through
  143. Global deadflag; ! Normally 0, or false; 1 for dead
  144. ! 2 for victorious, and higher numbers
  145. ! represent exotic forms of death
  146. ! ------------------------------------------------------------------------------
  147. ! Light and room descriptions
  148. ! ------------------------------------------------------------------------------
  149. Global lightflag = true; ! Is there currently light to see by?
  150. Global real_location; ! When in darkness, location = thedark
  151. ! and this holds the real location
  152. Global prev_location; ! The previous value of real_location
  153. Global visibility_ceiling; ! Highest object in tree visible from the
  154. ! player's point of view (usually the room,
  155. ! sometimes darkness, sometimes a closed
  156. ! non-transparent container).
  157. Global lookmode = 2; ! 1=brief, 2=verbose, 3=superbrief
  158. ! room descriptions
  159. Global print_player_flag; ! If set, print something like "(as Fred)"
  160. ! in room descriptions, to reveal whom the
  161. ! player is playing through
  162. Global lastdesc; ! Value of location at time of most recent
  163. ! room description printed out
  164. ! ------------------------------------------------------------------------------
  165. ! List writing (style bits are defined as Constants in "verblibm.h")
  166. ! ------------------------------------------------------------------------------
  167. Global c_style; ! Current list-writer style
  168. Global lt_value; ! Common value of list_together
  169. Global listing_together; ! Object number of one member of a group
  170. ! being listed together
  171. Global listing_size; ! Size of such a group
  172. Global wlf_indent; ! Current level of indentation printed by
  173. ! WriteListFrom()
  174. Global inventory_stage = 1; ! 1 or 2 according to the context in which
  175. ! "invent" routines of objects are called
  176. Global inventory_style; ! List-writer style currently used while
  177. ! printing inventories
  178. ! ------------------------------------------------------------------------------
  179. ! Menus and printing
  180. ! ------------------------------------------------------------------------------
  181. Global pretty_flag = true; ! Use character graphics, or plain text?
  182. Global menu_nesting; ! Level of nesting (0 = root menu)
  183. Global menu_item; ! These are used in communicating
  184. Global item_width = 8; ! with the menu-creating routines
  185. Global item_name = "---";
  186. Global lm_n; ! Parameters used by LibraryMessages
  187. Global lm_o; ! mechanism
  188. Global lm_s;
  189. #Ifdef DEBUG;
  190. Constant DEBUG_MESSAGES $0001;
  191. Constant DEBUG_ACTIONS $0002;
  192. Constant DEBUG_TIMERS $0004;
  193. Constant DEBUG_CHANGES $0008;
  194. Constant DEBUG_VERBOSE $0080;
  195. Global debug_flag; ! Bitmap of flags for tracing actions,
  196. ! calls to object routines, etc.
  197. Global x_scope_count; ! Used in printing a list of everything
  198. #Endif; ! DEBUG ! in scope
  199. ! five for colour control
  200. ! see http://www.inform-fiction.org/patches/L61007.html
  201. ! To enable colour define a constant or Global: COLOR or COLOUR
  202. !Global clr_on; ! has colour been enabled by the player?
  203. #Ifdef COLOUR;
  204. Global clr_fg = 1; ! foreground colour
  205. Global clr_bg = 1; ! background colour
  206. Global clr_fgstatus = 1; ! foreground colour of statusline
  207. Global clr_bgstatus = 1; ! background colour of statusline
  208. #Endif; ! COLOUR
  209. Global statuswin_current; ! if writing to top window
  210. Constant CLR_CURRENT 0;
  211. Constant CLR_DEFAULT 1;
  212. Constant CLR_BLACK 2;
  213. Constant CLR_RED 3;
  214. Constant CLR_GREEN 4;
  215. Constant CLR_YELLOW 5;
  216. Constant CLR_BLUE 6;
  217. Constant CLR_MAGENTA 7;
  218. Constant CLR_CYAN 8;
  219. Constant CLR_WHITE 9;
  220. Constant CLR_PURPLE 7;
  221. Constant CLR_AZURE 8;
  222. Constant WIN_ALL 0;
  223. Constant WIN_STATUS 1;
  224. Constant WIN_MAIN 2;
  225. ! ------------------------------------------------------------------------------
  226. ! Action processing
  227. ! ------------------------------------------------------------------------------
  228. Global action; ! Action currently being asked to perform
  229. Global inp1; ! 0 (nothing), 1 (number) or first noun
  230. Global inp2; ! 0 (nothing), 1 (number) or second noun
  231. Global noun; ! First noun or numerical value
  232. Global second; ! Second noun or numerical value
  233. Global keep_silent; ! If true, attempt to perform the action
  234. ! silently (e.g. for implicit takes,
  235. ! implicit opening of unlocked doors)
  236. Global reason_code; ! Reason for calling a "life" rule
  237. ! (an action or fake such as ##Kiss)
  238. Global receive_action; ! Either ##PutOn or ##Insert, whichever is
  239. ! action being tried when an object's
  240. ! "before" rule is checking "Receive"
  241. Global no_implicit_actions; ! Don't implicitly do things.
  242. ! ==============================================================================
  243. ! Parser variables: first, for communication to the parser
  244. ! ------------------------------------------------------------------------------
  245. Global parser_trace = 0; ! Set this to 1 to make the parser trace
  246. ! tokens and lines
  247. Global parser_action; ! For the use of the parser when calling
  248. Global parser_one; ! user-supplied routines
  249. Global parser_two; !
  250. Array inputobjs --> 16; ! For parser to write its results in
  251. Global parser_inflection; ! A property (usually "name") to find
  252. ! object names in
  253. Global parser_inflection_func; ! Programmer sets this to true when
  254. ! parser_infection is a function
  255. ! ------------------------------------------------------------------------------
  256. ! Parser output
  257. ! ------------------------------------------------------------------------------
  258. Global actor; ! Person asked to do something
  259. Global actors_location; ! Like location, but for the actor
  260. Global meta; ! Verb is a meta-command (such as "save")
  261. #Ifdef INFIX;
  262. Global infix_verb; ! Verb is an Infix command
  263. #Endif;
  264. Array multiple_object --> 64; ! List of multiple parameters
  265. Global multiflag; ! Multiple-object flag passed to actions
  266. ! Also used to prevent misleading MULTI_PE
  267. Global toomany_flag; ! Flag for "multiple match too large"
  268. ! (e.g. if "take all" took over 100 things)
  269. Global special_word; ! Dictionary address for "special" token
  270. Global special_number; ! Number typed for "special" token
  271. Global parsed_number; ! For user-supplied parsing routines
  272. Global consult_from; ! Word that a "consult" topic starts on
  273. Global consult_words; ! ...and number of words in topic
  274. Global asking_player; ! True during disambiguation question
  275. ! ------------------------------------------------------------------------------
  276. ! Implicit taking
  277. ! ------------------------------------------------------------------------------
  278. Global notheld_mode; ! To do with implicit taking
  279. Global onotheld_mode; ! "old copy of notheld_mode", ditto
  280. Global not_holding; ! Object to be automatically taken as an
  281. ! implicit command
  282. Array kept_results --> 16; ! Delayed command (while the take happens)
  283. ! ------------------------------------------------------------------------------
  284. ! Error numbers when parsing a grammar line
  285. ! ------------------------------------------------------------------------------
  286. Global etype; ! Error number on current line
  287. Global best_etype; ! Preferred error number so far
  288. Global nextbest_etype; ! Preferred one, if ASKSCOPE_PE disallowed
  289. Constant STUCK_PE = 1;
  290. Constant UPTO_PE = 2;
  291. Constant NUMBER_PE = 3;
  292. Constant CANTSEE_PE = 4;
  293. Constant TOOLIT_PE = 5;
  294. Constant NOTHELD_PE = 6;
  295. Constant MULTI_PE = 7;
  296. Constant MMULTI_PE = 8;
  297. Constant VAGUE_PE = 9;
  298. Constant EXCEPT_PE = 10;
  299. Constant ANIMA_PE = 11;
  300. Constant VERB_PE = 12;
  301. Constant SCENERY_PE = 13;
  302. Constant ITGONE_PE = 14;
  303. Constant JUNKAFTER_PE = 15;
  304. Constant TOOFEW_PE = 16;
  305. Constant NOTHING_PE = 17;
  306. Constant ASKSCOPE_PE = 18;
  307. ! ------------------------------------------------------------------------------
  308. ! Pattern-matching against a single grammar line
  309. ! ------------------------------------------------------------------------------
  310. Array pattern --> 32; ! For the current pattern match
  311. Global pcount; ! and a marker within it
  312. Array pattern2 --> 32; ! And another, which stores the best match
  313. Global pcount2; ! so far
  314. Constant PATTERN_NULL = $ffff; ! Entry for a token producing no text
  315. Array line_ttype-->32; ! For storing an analysed grammar line
  316. Array line_tdata-->32;
  317. Array line_token-->32;
  318. Global parameters; ! Parameters (objects) entered so far
  319. Global nsns; ! Number of special_numbers entered so far
  320. Global special_number1; ! First number, if one was typed
  321. Global special_number2; ! Second number, if two were typed
  322. ! ------------------------------------------------------------------------------
  323. ! Inferences and looking ahead
  324. ! ------------------------------------------------------------------------------
  325. Global params_wanted; ! Number of parameters needed
  326. ! (which may change in parsing)
  327. Global inferfrom; ! The point from which the rest of the
  328. ! command must be inferred
  329. Global inferword; ! And the preposition inferred
  330. Global dont_infer; ! Another dull flag
  331. Global no_infer_message = false; ! Use in ChooseObjects to suppress
  332. ! an inference message.
  333. Global action_to_be; ! (If the current line were accepted.)
  334. Global action_reversed; ! (Parameters would be reversed in order.)
  335. Global advance_warning; ! What a later-named thing will be
  336. ! ------------------------------------------------------------------------------
  337. ! At the level of individual tokens now
  338. ! ------------------------------------------------------------------------------
  339. Global found_ttype; ! Used to break up tokens into type
  340. Global found_tdata; ! and data (by AnalyseToken)
  341. Global token_filter; ! For noun filtering by user routines
  342. Global length_of_noun; ! Set by NounDomain to no of words in noun
  343. #Ifdef TARGET_ZCODE;
  344. Constant REPARSE_CODE = 10000; ! Signals "reparse the text" as a reply
  345. ! from NounDomain
  346. #Ifnot; ! TARGET_GLULX
  347. Constant REPARSE_CODE = $40000000; ! The parser rather gunkily adds addresses
  348. ! to REPARSE_CODE for some purposes and
  349. ! expects the result to be greater than
  350. ! REPARSE_CODE (signed comparison).
  351. ! So Glulx Inform is limited to a single
  352. ! gigabyte of storage, for the moment.
  353. #Endif; ! TARGET_
  354. Global lookahead; ! The token after the one now being matched
  355. Global multi_mode; ! Multiple mode
  356. Global multi_wanted; ! Number of things needed in multitude
  357. Global multi_had; ! Number of things actually found
  358. Global multi_context; ! What token the multi-obj was accepted for
  359. Global indef_mode; ! "Indefinite" mode - ie, "take a brick"
  360. ! is in this mode
  361. Global indef_type; ! Bit-map holding types of specification
  362. Global indef_wanted; ! Number of items wanted (100 for all)
  363. Global indef_guess_p; ! Plural-guessing flag
  364. Global indef_owner; ! Object which must hold these items
  365. Global indef_cases; ! Possible gender and numbers of them
  366. Global indef_possambig; ! Has a possibly dangerous assumption
  367. ! been made about meaning of a descriptor?
  368. Global indef_nspec_at; ! Word at which a number like "two" was
  369. ! parsed (for backtracking)
  370. Global allow_plurals; ! Whether plurals presently allowed or not
  371. Global take_all_rule; ! Slightly different rules apply to
  372. ! "take all" than other uses of multiple
  373. ! objects, to make adjudication produce
  374. ! more pragmatically useful results
  375. ! (Not a flag: possible values 0, 1, 2)
  376. Global dict_flags_of_noun; ! Of the noun currently being parsed
  377. ! (a bitmap in #dict_par1 format)
  378. Constant DICT_VERB $01;
  379. Constant DICT_META $02;
  380. Constant DICT_PLUR $04;
  381. Constant DICT_PREP $08;
  382. Constant DICT_X654 $70;
  383. Constant DICT_NOUN $80;
  384. Global pronoun_word; ! Records which pronoun ("it", "them", ...)
  385. ! caused an error
  386. Global pronoun_obj; ! And what obj it was thought to refer to
  387. Global pronoun__word; ! Saved value
  388. Global pronoun__obj; ! Saved value
  389. ! ------------------------------------------------------------------------------
  390. ! Searching through scope and parsing "scope=Routine" grammar tokens
  391. ! ------------------------------------------------------------------------------
  392. Constant PARSING_REASON = 0; ! Possible reasons for searching scope
  393. Constant TALKING_REASON = 1;
  394. Constant EACH_TURN_REASON = 2;
  395. Constant REACT_BEFORE_REASON = 3;
  396. Constant REACT_AFTER_REASON = 4;
  397. Constant LOOPOVERSCOPE_REASON = 5;
  398. Constant TESTSCOPE_REASON = 6;
  399. Global scope_reason = PARSING_REASON; ! Current reason for searching scope
  400. Global scope_token; ! For "scope=Routine" grammar tokens
  401. Global scope_error;
  402. Global scope_stage; ! 1, 2 then 3
  403. Global ats_flag = 0; ! For AddToScope routines
  404. Global ats_hls; !
  405. Global placed_in_flag; ! To do with PlaceInScope
  406. ! ------------------------------------------------------------------------------
  407. ! The match list of candidate objects for a given token
  408. ! ------------------------------------------------------------------------------
  409. Constant MATCH_LIST_SIZE = 64;
  410. Array match_list --> MATCH_LIST_SIZE; ! An array of matched objects so far
  411. Array match_classes --> MATCH_LIST_SIZE; ! An array of equivalence classes for them
  412. Array match_scores --> MATCH_LIST_SIZE; ! An array of match scores for them
  413. Global number_matched; ! How many items in it? (0 means none)
  414. Global number_of_classes; ! How many equivalence classes?
  415. Global match_length; ! How many words long are these matches?
  416. Global saved_ml;
  417. Global match_from; ! At what word of the input do they begin?
  418. Global bestguess_score; ! What did the best-guess object score?
  419. ! ------------------------------------------------------------------------------
  420. ! Low level textual manipulation
  421. ! ------------------------------------------------------------------------------
  422. #Ifdef TARGET_ZCODE;
  423. ! 'buffer' holds the input line as typed by the player
  424. !
  425. ! buffer->0 INPUT_BUFFER_LEN - WORDSIZE
  426. ! buffer->1 Number of characters input by player
  427. ! buffer->2 ... buffer->121 The actual characters
  428. ! buffer->122 Spare byte to allow for 'terp bugs
  429. !
  430. ! 'parse' holds the result of parsing that line into dictionary words
  431. !
  432. ! parse->0 MAX_BUFFER_WORDS
  433. ! parse->1 Number of words input by player
  434. !
  435. ! parse-->1 Dictionary addr of first input word
  436. ! parse->4 Number of characters in the word
  437. ! parse->5 Start position in 'buffer' of the word
  438. !
  439. ! parse-->3 parse->8,9 Same data for second input word
  440. ! ...
  441. ! parse-->29 parse->60,61 Same data for MAX_BUFFER_WORDS input word
  442. ! parse->62,63,64 Spare bytes (not sure why)
  443. Constant INPUT_BUFFER_LEN = WORDSIZE + 120; ! 120 is limit on input chars
  444. Constant MAX_BUFFER_WORDS = 15; ! Limit on input words
  445. Array buffer -> INPUT_BUFFER_LEN + 1; ! For main line of input
  446. Array buffer2 -> INPUT_BUFFER_LEN + 1; ! For supplementary questions
  447. Array buffer3 -> INPUT_BUFFER_LEN + 1; ! Retaining input for "AGAIN"
  448. #Ifdef VN_1630;
  449. Array parse buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer'
  450. Array parse2 buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer2'
  451. #Ifnot;
  452. Array parse -> 2 + (MAX_BUFFER_WORDS * 4) + 3;
  453. Array parse2 -> 2 + (MAX_BUFFER_WORDS * 4) + 3;
  454. #Endif; ! VN_
  455. #Ifnot; ! TARGET_GLULX
  456. ! 'buffer' holds the input line as typed by the player
  457. !
  458. ! buffer-->0 Number of characters input by player
  459. ! buffer->4 ... buffer->259 The actual characters
  460. !
  461. ! 'parse' holds the result of parsing that line into dictionary words
  462. !
  463. ! parse-->0 Number of words input by player
  464. !
  465. ! parse-->1 Dictionary addr of first input word
  466. ! parse-->2 Number of characters in the word
  467. ! parse-->3 Start position in 'buffer' of the word
  468. !
  469. ! parse-->4,5,6 Same data for second input word
  470. ! ...
  471. ! parse-->58,59,60 Same data for MAX_BUFFER_WORDS input word
  472. Constant INPUT_BUFFER_LEN = WORDSIZE + 256; ! 256 is limit on input chars
  473. Constant MAX_BUFFER_WORDS = 20; ! Limit on input words
  474. #Ifdef VN_1630;
  475. Array buffer buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For main line of input
  476. Array buffer2 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For supplementary questions
  477. Array buffer3 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! Retaining input for "AGAIN"
  478. #Ifnot;
  479. Array buffer -> INPUT_BUFFER_LEN;
  480. Array buffer2 -> INPUT_BUFFER_LEN;
  481. Array buffer3 -> INPUT_BUFFER_LEN;
  482. #Endif; ! VN_
  483. Array parse --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer'
  484. Array parse2 --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer2'
  485. #Endif; ! TARGET_
  486. Constant comma_word = 'comma,'; ! An "untypeable word" used to substitute
  487. ! for commas in parse buffers
  488. Global wn; ! Word number within "parse" (from 1)
  489. Global num_words; ! Number of words typed
  490. Global num_desc; ! Number of descriptors typed
  491. Global verb_word; ! Verb word (eg, take in "take all" or
  492. ! "dwarf, take all") - address in dict
  493. Global verb_wordnum; ! its number in typing order (eg, 1 or 3)
  494. Global usual_grammar_after; ! Point from which usual grammar is parsed (it may vary from the
  495. ! above if user's routines match multi-word verbs)
  496. Global oops_from; ! The "first mistake" word number
  497. Global saved_oops; ! Used in working this out
  498. Constant OOPS_WORKSPACE_LEN 64; ! Used temporarily by "oops" routine
  499. Array oops_workspace -> OOPS_WORKSPACE_LEN;
  500. Global held_back_mode; ! Flag: is there some input from last time
  501. Global hb_wn; ! left over? (And a save value for wn.)
  502. ! (Used for full stops and "then".)
  503. Global caps_mode; ! Keep track of (The) with 'proper' caps
  504. Global print_anything_result; ! Return value from a PrintAny() routine
  505. Global initial_lookmode; ! Default, or set in Initialise()
  506. Global before_first_turn; ! True until after initial LOOK
  507. ! ----------------------------------------------------------------------------
  508. Array PowersOfTwo_TB ! Used in converting case numbers to case
  509. --> $$100000000000 ! bitmaps
  510. $$010000000000
  511. $$001000000000
  512. $$000100000000
  513. $$000010000000
  514. $$000001000000
  515. $$000000100000
  516. $$000000010000
  517. $$000000001000
  518. $$000000000100
  519. $$000000000010
  520. $$000000000001;
  521. ! ============================================================================
  522. ! Constants, and one variable, needed for the language definition file
  523. ! ----------------------------------------------------------------------------
  524. Constant POSSESS_PK = $100;
  525. Constant DEFART_PK = $101;
  526. Constant INDEFART_PK = $102;
  527. Global short_name_case;
  528. Global dict_start;
  529. Global dict_entry_size;
  530. Global dict_end;
  531. ! ----------------------------------------------------------------------------
  532. Include "language__"; ! The natural language definition, whose filename is taken from
  533. ! the ICL language_name variable
  534. ! ----------------------------------------------------------------------------
  535. #Ifndef LanguageCases;
  536. Constant LanguageCases = 1;
  537. #Endif; ! LanguageCases
  538. ! ------------------------------------------------------------------------------
  539. ! Pronouns support for the cruder (library 6/2 and earlier) version:
  540. ! only needed in English
  541. ! ------------------------------------------------------------------------------
  542. #Ifdef EnglishNaturalLanguage;
  543. Global itobj = NULL; ! The object which is currently "it"
  544. Global himobj = NULL; ! The object which is currently "him"
  545. Global herobj = NULL; ! The object which is currently "her"
  546. Global old_itobj = NULL; ! The object which is currently "it"
  547. Global old_himobj = NULL; ! The object which is currently "him"
  548. Global old_herobj = NULL; ! The object which is currently "her"
  549. #Endif; ! EnglishNaturalLanguage
  550. ! ============================================================================
  551. ! For present and past tenses
  552. ! ----------------------------------------------------------------------------
  553. Constant PRESENT_TENSE 0;
  554. Constant PAST_TENSE 1;
  555. ! ============================================================================
  556. ! For InformLibrary.actor_act() to control what happens when it aborts.
  557. ! ----------------------------------------------------------------------------
  558. Constant ACTOR_ACT_ABORT_NOTUNDERSTOOD 1;
  559. Constant ACTOR_ACT_ABORT_ORDER 2;
  560. ! ============================================================================
  561. ! "Darkness" is not really a place: but it has to be an object so that the
  562. ! location-name on the status line can be "Darkness".
  563. ! ----------------------------------------------------------------------------
  564. Object thedark "(darkness object)"
  565. with initial 0,
  566. short_name DARKNESS__TX,
  567. description [; return L__M(##Miscellany, 17); ];
  568. ! If you want to use the third-person of the narrative voice, you will
  569. ! need to replace this selfobj with your own.
  570. Object selfobj "(self object)"
  571. with name ',a' ',b' ',c' ',d' ',e',
  572. short_name YOURSELF__TX,
  573. description [; return L__M(##Miscellany, 19); ],
  574. before NULL,
  575. after NULL,
  576. life NULL,
  577. each_turn NULL,
  578. time_out NULL,
  579. describe NULL,
  580. article "the",
  581. add_to_scope 0,
  582. capacity 100,
  583. parse_name 0,
  584. orders 0,
  585. number 0,
  586. narrative_voice 2,
  587. narrative_tense PRESENT_TENSE,
  588. nameless true,
  589. posture 0,
  590. before_implicit [;Take: return 2;],
  591. has concealed animate proper transparent;
  592. ! ============================================================================
  593. ! The definition of the token-numbering system used by Inform.
  594. ! ----------------------------------------------------------------------------
  595. Constant ILLEGAL_TT = 0; ! Types of grammar token: illegal
  596. Constant ELEMENTARY_TT = 1; ! (one of those below)
  597. Constant PREPOSITION_TT = 2; ! e.g. 'into'
  598. Constant ROUTINE_FILTER_TT = 3; ! e.g. noun=CagedCreature
  599. Constant ATTR_FILTER_TT = 4; ! e.g. edible
  600. Constant SCOPE_TT = 5; ! e.g. scope=Spells
  601. Constant GPR_TT = 6; ! a general parsing routine
  602. Constant NOUN_TOKEN = 0; ! The elementary grammar tokens, and
  603. Constant HELD_TOKEN = 1; ! the numbers compiled by Inform to
  604. Constant MULTI_TOKEN = 2; ! encode them
  605. Constant MULTIHELD_TOKEN = 3;
  606. Constant MULTIEXCEPT_TOKEN = 4;
  607. Constant MULTIINSIDE_TOKEN = 5;
  608. Constant CREATURE_TOKEN = 6;
  609. Constant SPECIAL_TOKEN = 7;
  610. Constant NUMBER_TOKEN = 8;
  611. Constant TOPIC_TOKEN = 9;
  612. Constant GPR_FAIL = -1; ! Return values from General Parsing
  613. Constant GPR_PREPOSITION = 0; ! Routines
  614. Constant GPR_NUMBER = 1;
  615. Constant GPR_MULTIPLE = 2;
  616. Constant GPR_REPARSE = REPARSE_CODE;
  617. Constant GPR_NOUN = $ff00;
  618. Constant GPR_HELD = $ff01;
  619. Constant GPR_MULTI = $ff02;
  620. Constant GPR_MULTIHELD = $ff03;
  621. Constant GPR_MULTIEXCEPT = $ff04;
  622. Constant GPR_MULTIINSIDE = $ff05;
  623. Constant GPR_CREATURE = $ff06;
  624. Constant ENDIT_TOKEN = 15; ! Value used to mean "end of grammar line"
  625. #Iftrue (Grammar__Version == 1);
  626. [ AnalyseToken token m;
  627. found_tdata = token;
  628. if (token < 0) { found_ttype = ILLEGAL_TT; return; }
  629. if (token <= 8) { found_ttype = ELEMENTARY_TT; return; }
  630. if (token < 15) { found_ttype = ILLEGAL_TT; return; }
  631. if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
  632. if (token < 48) { found_ttype = ROUTINE_FILTER_TT;
  633. found_tdata = token - 16;
  634. return;
  635. }
  636. if (token < 80) { found_ttype = GPR_TT;
  637. found_tdata = #preactions_table-->(token-48);
  638. return;
  639. }
  640. if (token < 128) { found_ttype = SCOPE_TT;
  641. found_tdata = #preactions_table-->(token-80);
  642. return;
  643. }
  644. if (token < 180) { found_ttype = ATTR_FILTER_TT;
  645. found_tdata = token - 128;
  646. return;
  647. }
  648. found_ttype = PREPOSITION_TT;
  649. m = #adjectives_table;
  650. for (::) {
  651. if (token == m-->1) { found_tdata = m-->0; return; }
  652. m = m+4;
  653. }
  654. m = #adjectives_table; RunTimeError(1);
  655. found_tdata = m;
  656. ];
  657. [ UnpackGrammarLine line_address i m;
  658. for (i=0 : i<32 : i++) {
  659. line_token-->i = ENDIT_TOKEN;
  660. line_ttype-->i = ELEMENTARY_TT;
  661. line_tdata-->i = ENDIT_TOKEN;
  662. }
  663. for (i=0 : i<=5 : i++) {
  664. line_token-->i = line_address->(i+1);
  665. AnalyseToken(line_token-->i);
  666. if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
  667. && (m == line_address->0)) {
  668. line_token-->i = ENDIT_TOKEN;
  669. break;
  670. }
  671. line_ttype-->i = found_ttype;
  672. line_tdata-->i = found_tdata;
  673. if (found_ttype ~= PREPOSITION_TT) m++;
  674. }
  675. action_to_be = line_address->7;
  676. action_reversed = false;
  677. params_wanted = line_address->0;
  678. return line_address + 8;
  679. ];
  680. #Ifnot; ! Grammar__Version == 2
  681. [ AnalyseToken token;
  682. if (token == ENDIT_TOKEN) {
  683. found_ttype = ELEMENTARY_TT;
  684. found_tdata = ENDIT_TOKEN;
  685. return;
  686. }
  687. found_ttype = (token->0) & $$1111;
  688. found_tdata = (token+1)-->0;
  689. ];
  690. #Ifdef TARGET_ZCODE;
  691. [ UnpackGrammarLine line_address i;
  692. for (i=0 : i<32 : i++) {
  693. line_token-->i = ENDIT_TOKEN;
  694. line_ttype-->i = ELEMENTARY_TT;
  695. line_tdata-->i = ENDIT_TOKEN;
  696. }
  697. action_to_be = 256*(line_address->0) + line_address->1;
  698. action_reversed = ((action_to_be & $400) ~= 0);
  699. action_to_be = action_to_be & $3ff;
  700. line_address--;
  701. params_wanted = 0;
  702. for (i=0 : : i++) {
  703. line_address = line_address + 3;
  704. if (line_address->0 == ENDIT_TOKEN) break;
  705. line_token-->i = line_address;
  706. AnalyseToken(line_address);
  707. if (found_ttype ~= PREPOSITION_TT) params_wanted++;
  708. line_ttype-->i = found_ttype;
  709. line_tdata-->i = found_tdata;
  710. }
  711. return line_address + 1;
  712. ];
  713. #Ifnot; ! TARGET_GLULX
  714. [ UnpackGrammarLine line_address i;
  715. for (i=0 : i<32 : i++) {
  716. line_token-->i = ENDIT_TOKEN;
  717. line_ttype-->i = ELEMENTARY_TT;
  718. line_tdata-->i = ENDIT_TOKEN;
  719. }
  720. @aloads line_address 0 action_to_be;
  721. action_reversed = (((line_address->2) & 1) ~= 0);
  722. line_address = line_address - 2;
  723. params_wanted = 0;
  724. for (i=0 : : i++) {
  725. line_address = line_address + 5;
  726. if (line_address->0 == ENDIT_TOKEN) break;
  727. line_token-->i = line_address;
  728. AnalyseToken(line_address);
  729. if (found_ttype ~= PREPOSITION_TT) params_wanted++;
  730. line_ttype-->i = found_ttype;
  731. line_tdata-->i = found_tdata;
  732. }
  733. return line_address + 1;
  734. ];
  735. #Endif; ! TARGET_
  736. #Endif; ! Grammar__Version
  737. ! To protect against a bug in early versions of the "Zip" interpreter:
  738. ! Of course, in Glulx, this routine actually performs work.
  739. #Ifdef TARGET_ZCODE;
  740. [ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];
  741. #Ifnot; ! TARGET_GLULX
  742. Array gg_tokenbuf -> DICT_WORD_SIZE;
  743. [ GGWordCompare str1 str2 ix jx;
  744. for (ix=0 : ix<DICT_WORD_SIZE : ix++) {
  745. jx = (str1->ix) - (str2->ix);
  746. if (jx ~= 0) return jx;
  747. }
  748. return 0;
  749. ];
  750. [ Tokenise__ buf tab
  751. cx numwords len bx ix wx wpos wlen val res dictlen entrylen;
  752. len = buf-->0;
  753. buf = buf+WORDSIZE;
  754. ! First, split the buffer up into words. We use the standard Infocom
  755. ! list of word separators (comma, period, double-quote).
  756. cx = 0;
  757. numwords = 0;
  758. while (cx < len) {
  759. while (cx < len && buf->cx == ' ') cx++;
  760. if (cx >= len) break;
  761. bx = cx;
  762. if (buf->cx == '.' or ',' or '"') cx++;
  763. else {
  764. while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"') cx++;
  765. }
  766. tab-->(numwords*3+2) = (cx-bx);
  767. tab-->(numwords*3+3) = WORDSIZE+bx;
  768. numwords++;
  769. if (numwords >= MAX_BUFFER_WORDS) break;
  770. }
  771. tab-->0 = numwords;
  772. ! Now we look each word up in the dictionary.
  773. dictlen = #dictionary_table-->0;
  774. entrylen = DICT_WORD_SIZE + 7;
  775. for (wx=0 : wx<numwords : wx++) {
  776. wlen = tab-->(wx*3+2);
  777. wpos = tab-->(wx*3+3);
  778. ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE
  779. ! characters and lower case.
  780. if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
  781. cx = wpos - WORDSIZE;
  782. for (ix=0 : ix<wlen : ix++) gg_tokenbuf->ix = glk($00A0, buf->(cx+ix));
  783. for (: ix<DICT_WORD_SIZE : ix++) gg_tokenbuf->ix = 0;
  784. val = #dictionary_table + WORDSIZE;
  785. @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
  786. tab-->(wx*3+1) = res;
  787. }
  788. ];
  789. #Endif; ! TARGET_
  790. ! ============================================================================
  791. ! The InformParser object abstracts the front end of the parser.
  792. !
  793. ! InformParser.parse_input(results)
  794. ! returns only when a sensible request has been made, and puts into the
  795. ! "results" buffer:
  796. !
  797. ! --> 0 = The action number
  798. ! --> 1 = Number of parameters
  799. ! --> 2, 3, ... = The parameters (object numbers), but
  800. ! 0 means "put the multiple object list here"
  801. ! 1 means "put one of the special numbers here"
  802. !
  803. ! ----------------------------------------------------------------------------
  804. Object InformParser "(Inform Parser)"
  805. with parse_input [ results; Parser__parse(results); ],
  806. has proper;
  807. ! ----------------------------------------------------------------------------
  808. ! The Keyboard routine actually receives the player's words,
  809. ! putting the words in "a_buffer" and their dictionary addresses in
  810. ! "a_table". It is assumed that the table is the same one on each
  811. ! (standard) call.
  812. !
  813. ! It can also be used by miscellaneous routines in the game to ask
  814. ! yes-no questions and the like, without invoking the rest of the parser.
  815. !
  816. ! Return the number of words typed
  817. ! ----------------------------------------------------------------------------
  818. #Ifdef TARGET_ZCODE;
  819. [ GetNthChar a_buffer n i;
  820. for (i = 0: a_buffer->(2+i) == ' ': i++) {
  821. if (i > a_buffer->(1)) return false;
  822. }
  823. return a_buffer->(2+i+n);
  824. ];
  825. [ KeyboardPrimitive a_buffer a_table;
  826. read a_buffer a_table;
  827. #Iftrue (#version_number == 6);
  828. @output_stream -1;
  829. @loadb a_buffer 1 -> sp;
  830. @add a_buffer 2 -> sp;
  831. @print_table sp sp;
  832. new_line;
  833. @output_stream 1;
  834. #Endif;
  835. ];
  836. [ KeyCharPrimitive win key;
  837. if (win) @set_window win;
  838. @read_char 1 -> key;
  839. return key;
  840. ];
  841. [ KeyTimerInterrupt;
  842. rtrue;
  843. ];
  844. [ KeyDelay tenths key;
  845. @read_char 1 tenths KeyTimerInterrupt -> key;
  846. return key;
  847. ];
  848. #Ifnot; ! TARGET_GLULX
  849. [ GetNthChar a_buffer n i;
  850. for (i = 0: a_buffer->(4+i) == ' ': i++) {
  851. if (i > a_buffer->(1)) return false;
  852. }
  853. return a_buffer->(4+i+n);
  854. ];
  855. [ KeyCharPrimitive win nostat done res ix jx ch;
  856. jx = ch; ! squash compiler warnings
  857. if (win == 0) win = gg_mainwin;
  858. if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
  859. ! get_line_stream
  860. done = glk($0091, gg_commandstr, gg_arguments, 31);
  861. if (done == 0) {
  862. glk($0044, gg_commandstr, 0); ! stream_close
  863. gg_commandstr = 0;
  864. gg_command_reading = false;
  865. ! fall through to normal user input.
  866. }
  867. else {
  868. ! Trim the trailing newline
  869. if (gg_arguments->(done-1) == 10) done = done-1;
  870. res = gg_arguments->0;
  871. if (res == '\') {
  872. res = 0;
  873. for (ix=1 : ix<done : ix++) {
  874. ch = gg_arguments->ix;
  875. if (ch >= '0' && ch <= '9') {
  876. @shiftl res 4 res;
  877. res = res + (ch-'0');
  878. }
  879. else if (ch >= 'a' && ch <= 'f') {
  880. @shiftl res 4 res;
  881. res = res + (ch+10-'a');
  882. }
  883. else if (ch >= 'A' && ch <= 'F') {
  884. @shiftl res 4 res;
  885. res = res + (ch+10-'A');
  886. }
  887. }
  888. }
  889. jump KCPContinue;
  890. }
  891. }
  892. done = false;
  893. glk($00D2, win); ! request_char_event
  894. while (~~done) {
  895. glk($00C0, gg_event); ! select
  896. switch (gg_event-->0) {
  897. 5: ! evtype_Arrange
  898. if (nostat) {
  899. glk($00D3, win); ! cancel_char_event
  900. res = $80000000;
  901. done = true;
  902. break;
  903. }
  904. DrawStatusLine();
  905. 2: ! evtype_CharInput
  906. if (gg_event-->1 == win) {
  907. res = gg_event-->2;
  908. done = true;
  909. }
  910. }
  911. ix = HandleGlkEvent(gg_event, 1, gg_arguments);
  912. if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments);
  913. if (ix == 2) {
  914. res = gg_arguments-->0;
  915. done = true;
  916. }
  917. else if (ix == -1) {
  918. done = false;
  919. }
  920. }
  921. if (gg_commandstr ~= 0 && gg_command_reading == false) {
  922. if (res < 32 || res >= 256 || (res == '\' or ' ')) {
  923. glk($0081, gg_commandstr, '\'); ! put_buffer_char
  924. done = 0;
  925. jx = res;
  926. for (ix=0 : ix<8 : ix++) {
  927. @ushiftr jx 28 ch;
  928. @shiftl jx 4 jx;
  929. ch = ch & $0F;
  930. if (ch ~= 0 || ix == 7) done = 1;
  931. if (done) {
  932. if (ch >= 0 && ch <= 9) ch = ch + '0';
  933. else ch = (ch - 10) + 'A';
  934. glk($0081, gg_commandstr, ch); ! put_buffer_char
  935. }
  936. }
  937. }
  938. else {
  939. glk($0081, gg_commandstr, res); ! put_buffer_char
  940. }
  941. glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
  942. }
  943. .KCPContinue;
  944. return res;
  945. ];
  946. [ KeyDelay tenths key done ix;
  947. glk($00D2, gg_mainwin); ! request_char_event
  948. glk($00D6, tenths*100); ! request_timer_events
  949. while (~~done) {
  950. glk($00C0, gg_event); ! select
  951. ix = HandleGlkEvent(gg_event, 1, gg_arguments);
  952. if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments);
  953. if (ix == 2) {
  954. key = gg_arguments-->0;
  955. done = true;
  956. }
  957. else if (ix >= 0 && gg_event-->0 == 1 or 2) {
  958. key = gg_event-->2;
  959. done = true;
  960. }
  961. }
  962. glk($00D3, gg_mainwin); ! cancel_char_event
  963. glk($00D6, 0); ! request_timer_events
  964. return key;
  965. ];
  966. [ KeyboardPrimitive a_buffer a_table done ix;
  967. if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
  968. ! get_line_stream
  969. done = glk($0091, gg_commandstr, a_buffer+WORDSIZE, (INPUT_BUFFER_LEN-WORDSIZE)-1);
  970. if (done == 0) {
  971. glk($0044, gg_commandstr, 0); ! stream_close
  972. gg_commandstr = 0;
  973. gg_command_reading = false;
  974. ! L__M(##CommandsRead, 5); would come after prompt
  975. ! fall through to normal user input.
  976. }
  977. else {
  978. ! Trim the trailing newline
  979. if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1;
  980. a_buffer-->0 = done;
  981. glk($0086, 8); ! set input style
  982. glk($0084, a_buffer+WORDSIZE, done); ! put_buffer
  983. glk($0086, 0); ! set normal style
  984. print "^";
  985. jump KPContinue;
  986. }
  987. }
  988. done = false;
  989. glk($00D0, gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0); ! request_line_event
  990. while (~~done) {
  991. glk($00C0, gg_event); ! select
  992. switch (gg_event-->0) {
  993. 5: ! evtype_Arrange
  994. DrawStatusLine();
  995. 3: ! evtype_LineInput
  996. if (gg_event-->1 == gg_mainwin) {
  997. a_buffer-->0 = gg_event-->2;
  998. done = true;
  999. }
  1000. }
  1001. ix = HandleGlkEvent(gg_event, 0, a_buffer);
  1002. if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 0, a_buffer);
  1003. if (ix == 2) done = true;
  1004. else if (ix == -1) done = false;
  1005. }
  1006. if (gg_commandstr ~= 0 && gg_command_reading == false) {
  1007. ! put_buffer_stream
  1008. glk($0085, gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0);
  1009. glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
  1010. }
  1011. .KPContinue;
  1012. Tokenise__(a_buffer,a_table);
  1013. ! It's time to close any quote window we've got going.
  1014. if (gg_quotewin) {
  1015. glk($0024, gg_quotewin, 0); ! close_window
  1016. gg_quotewin = 0;
  1017. }
  1018. ];
  1019. #Endif; ! TARGET_
  1020. [ Keyboard a_buffer a_table nw i w w2 x1 x2;
  1021. DisplayStatus();
  1022. .FreshInput;
  1023. ! Save the start of the buffer, in case "oops" needs to restore it
  1024. ! to the previous time's buffer
  1025. for (i=0 : i<OOPS_WORKSPACE_LEN : i++) oops_workspace->i = a_buffer->i;
  1026. ! In case of an array entry corruption that shouldn't happen, but would be
  1027. ! disastrous if it did:
  1028. #Ifdef TARGET_ZCODE;
  1029. a_buffer->0 = INPUT_BUFFER_LEN - WORDSIZE;
  1030. a_table->0 = MAX_BUFFER_WORDS; ! Allow to split input into this many words
  1031. #Endif; ! TARGET_
  1032. ! Print the prompt, and read in the words and dictionary addresses
  1033. L__M(##Prompt);
  1034. if (AfterPrompt() == 0) LibraryExtensions.RunAll(ext_afterprompt);
  1035. #IfV5;
  1036. DrawStatusLine();
  1037. #Endif; ! V5
  1038. KeyboardPrimitive(a_buffer, a_table);
  1039. nw = NumberWords(a_table);
  1040. ! If the line was blank, get a fresh line
  1041. if (nw == 0) {
  1042. L__M(##Miscellany, 10);
  1043. jump FreshInput;
  1044. }
  1045. ! Unless the opening word was "oops", return
  1046. ! Conveniently, a_table-->1 is the first word in both ZCODE and GLULX.
  1047. w = a_table-->1;
  1048. if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;
  1049. if (a_buffer->WORDSIZE == COMMENT_CHARACTER) {
  1050. #Ifdef TARGET_ZCODE;
  1051. if ((HDR_GAMEFLAGS-->0) & $0001 || xcommsdir)
  1052. L__M(##Miscellany, 54);
  1053. else L__M(##Miscellany, 55);
  1054. #Ifnot; ! TARGET_GLULX
  1055. if (gg_scriptstr || gg_commandstr) L__M(##Miscellany, 54);
  1056. else L__M(##Miscellany, 55);
  1057. #Endif; ! TARGET_
  1058. jump FreshInput;
  1059. }
  1060. #IfV5;
  1061. ! Undo handling
  1062. if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) {
  1063. i = PerformUndo();
  1064. if (i == 0) jump FreshInput;
  1065. }
  1066. #Ifdef TARGET_ZCODE;
  1067. @save_undo i;
  1068. #Ifnot; ! TARGET_GLULX
  1069. @saveundo i;
  1070. if (i == -1) {
  1071. GGRecoverObjects();
  1072. i = 2;
  1073. }
  1074. else i = (~~i);
  1075. #Endif; ! TARGET_
  1076. just_undone = 0;
  1077. undo_flag = 2;
  1078. if (i == -1) undo_flag = 0;
  1079. if (i == 0) undo_flag = 1;
  1080. if (i == 2) {
  1081. RestoreColours();
  1082. #Ifdef TARGET_ZCODE;
  1083. style bold;
  1084. #Ifnot; ! TARGET_GLULX
  1085. glk($0086, 4); ! set subheader style
  1086. #Endif; ! TARGET_
  1087. print (name) location, "^";
  1088. #Ifdef TARGET_ZCODE;
  1089. style roman;
  1090. #Ifnot; ! TARGET_GLULX
  1091. glk($0086, 0); ! set normal style
  1092. #Endif; ! TARGET_
  1093. L__M(##Miscellany, 13);
  1094. just_undone = 1;
  1095. jump FreshInput;
  1096. }
  1097. #Endif; ! V5
  1098. return nw;
  1099. .DoOops;
  1100. if (oops_from == 0) {
  1101. L__M(##Miscellany, 14);
  1102. jump FreshInput;
  1103. }
  1104. if (nw == 1) {
  1105. L__M(##Miscellany, 15);
  1106. jump FreshInput;
  1107. }
  1108. if (nw > 2) {
  1109. L__M(##Miscellany, 16);
  1110. jump FreshInput;
  1111. }
  1112. ! So now we know: there was a previous mistake, and the player has
  1113. ! attempted to correct a single word of it.
  1114. for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;
  1115. #Ifdef TARGET_ZCODE;
  1116. x1 = a_table->9; ! Start of word following "oops"
  1117. x2 = a_table->8; ! Length of word following "oops"
  1118. #Ifnot; ! TARGET_GLULX
  1119. x1 = a_table-->6; ! Start of word following "oops"
  1120. x2 = a_table-->5; ! Length of word following "oops"
  1121. #Endif; ! TARGET_
  1122. ! Repair the buffer to the text that was in it before the "oops"
  1123. ! was typed:
  1124. for (i=0 : i < OOPS_WORKSPACE_LEN : i++) a_buffer->i = oops_workspace->i;
  1125. Tokenise__(a_buffer, a_table);
  1126. ! Work out the position in the buffer of the word to be corrected:
  1127. #Ifdef TARGET_ZCODE;
  1128. w = a_table->(4*oops_from + 1); ! Start of word to go
  1129. w2 = a_table->(4*oops_from); ! Length of word to go
  1130. #Ifnot; ! TARGET_GLULX
  1131. w = a_table-->(3*oops_from); ! Start of word to go
  1132. w2 = a_table-->(3*oops_from - 1); ! Length of word to go
  1133. #Endif; ! TARGET_
  1134. #IfDef OOPS_CHECK;
  1135. print "[~";
  1136. for (i=0 : i<w2 : i++) for (i=0 : i<w2 : i++) print (char)a_buffer->(i+w);
  1137. print "~ --> ~";
  1138. #Endif;
  1139. ! Write spaces over the word to be corrected:
  1140. for (i=0 : i<w2 : i++) a_buffer->(i+w) = ' ';
  1141. if (w2 < x2) {
  1142. ! If the replacement is longer than the original, move up...
  1143. for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--)
  1144. a_buffer->i = a_buffer->(i-x2+w2);
  1145. ! ...increasing buffer size accordingly.
  1146. SetKeyBufLength(GetKeyBufLength(a_buffer) + (x2-w2), a_buffer);
  1147. }
  1148. ! Write the correction in:
  1149. for (i=0 : i<x2 : i++) {
  1150. a_buffer->(i+w) = buffer2->(i+x1);
  1151. #IfDef OOPS_CHECK;
  1152. print (char) buffer2->(i+x1);
  1153. #Endif;
  1154. }
  1155. #IfDef OOPS_CHECK;
  1156. print "~]^^";
  1157. #Endif;
  1158. Tokenise__(a_buffer, a_table);
  1159. nw=NumberWords(a_table);
  1160. ! saved_ml = 0;
  1161. return nw;
  1162. ]; ! end of Keyboard
  1163. [ PerformUndo i;
  1164. if (turns == START_MOVE) { L__M(##Miscellany, 11); return 0; }
  1165. if (undo_flag == 0) { L__M(##Miscellany, 6); return 0; }
  1166. if (undo_flag == 1) { L__M(##Miscellany, 7); return 0; }
  1167. #Ifdef TARGET_ZCODE;
  1168. @restore_undo i;
  1169. #Ifnot; ! TARGET_GLULX
  1170. @restoreundo i;
  1171. i = (~~i);
  1172. #Endif; ! TARGET_
  1173. if (i == 0) { L__M(##Miscellany, 7); return 0; }
  1174. L__M(##Miscellany, 1);
  1175. return 1;
  1176. ];
  1177. ! ----------------------------------------------------------------------------
  1178. ! To simplify the picture a little, a rough map of the main routine:
  1179. !
  1180. ! (A) Get the input, do "oops" and "again"
  1181. ! (B) Is it a direction, and so an implicit "go"? If so go to (K)
  1182. ! (C) Is anyone being addressed?
  1183. ! (D) Get the verb: try all the syntax lines for that verb
  1184. ! (E) Break down a syntax line into analysed tokens
  1185. ! (F) Look ahead for advance warning for multiexcept/multiinside
  1186. ! (G) Parse each token in turn (calling ParseToken to do most of the work)
  1187. ! (H) Cheaply parse otherwise unrecognised conversation and return
  1188. ! (I) Print best possible error message
  1189. ! (J) Retry the whole lot
  1190. ! (K) Last thing: check for "then" and further instructions(s), return.
  1191. !
  1192. ! The strategic points (A) to (K) are marked in the commentary.
  1193. !
  1194. ! Note that there are three different places where a return can happen.
  1195. ! ----------------------------------------------------------------------------
  1196. [ Parser__parse results syntax line num_lines line_address i j k
  1197. token l m line_etype vw;
  1198. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1199. !
  1200. ! A: Get the input, do "oops" and "again"
  1201. !
  1202. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1203. ! Firstly, in "not held" mode, we still have a command left over from last
  1204. ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
  1205. ! last time, with "eat biscuit" tucked away until now). So we return that.
  1206. if (notheld_mode == 1) {
  1207. for (i=0 : i<8 : i++) results-->i = kept_results-->i;
  1208. notheld_mode = 0;
  1209. rtrue;
  1210. }
  1211. if (held_back_mode ~= 0) {
  1212. held_back_mode = 0;
  1213. Tokenise__(buffer, parse);
  1214. jump ReParse;
  1215. }
  1216. .ReType;
  1217. Keyboard(buffer, parse);
  1218. #Ifdef INFIX;
  1219. ! An Infix verb is a special kind of meta verb. We mark them here.
  1220. if (GetNthChar(buffer, 0) == ';')
  1221. infix_verb = true;
  1222. else
  1223. infix_verb = false;
  1224. #Endif;
  1225. .ReParse;
  1226. parser_inflection = name;
  1227. parser_inflection_func = false;
  1228. ! Initially assume the command is aimed at the player, and the verb
  1229. ! is the first word
  1230. num_words = NumberWords();
  1231. wn = 1;
  1232. #Ifdef LanguageToInformese;
  1233. LanguageToInformese();
  1234. #IfV5;
  1235. ! Re-tokenise:
  1236. Tokenise__(buffer,parse);
  1237. #Endif; ! V5
  1238. #Endif; ! LanguageToInformese
  1239. if (BeforeParsing() == false) {
  1240. LibraryExtensions.ext_number_1 = wn; ! Set "between calls" functionality to restore wn each pass
  1241. LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN;
  1242. LibraryExtensions.RunWhile(ext_beforeparsing, false);
  1243. LibraryExtensions.BetweenCalls = 0; ! Turn off "between calls" functionality
  1244. }
  1245. num_words = NumberWords();
  1246. k=0;
  1247. #Ifdef DEBUG;
  1248. if (parser_trace >= 2) {
  1249. print "[ ";
  1250. for (i=0 : i<num_words : i++) {
  1251. j = WordValue(i+1);
  1252. k = WordAddress(i+1);
  1253. l = WordLength(i+1);
  1254. print "~"; for (m=0 : m<l : m++) print (char) k->m; print "~ ";
  1255. if (j == 0) print "?";
  1256. else {
  1257. #Ifdef TARGET_ZCODE;
  1258. if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 &&
  1259. UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0)
  1260. print (address) j;
  1261. else print j;
  1262. #Ifnot; ! TARGET_GLULX
  1263. if (j->0 == $60) print (address) j;
  1264. else print j;
  1265. #Endif; ! TARGET_
  1266. }
  1267. if (i ~= num_words-1) print " / ";
  1268. }
  1269. print " ]^";
  1270. }
  1271. #Endif; ! DEBUG
  1272. verb_wordnum = 1;
  1273. actor = player;
  1274. actors_location = ScopeCeiling(player);
  1275. usual_grammar_after = 0;
  1276. .AlmostReParse;
  1277. scope_token = 0;
  1278. action_to_be = NULL;
  1279. ! Begin from what we currently think is the verb word
  1280. .BeginCommand;
  1281. wn = verb_wordnum;
  1282. verb_word = NextWordStopped();
  1283. ! If there's no input here, we must have something like "person,".
  1284. if (verb_word == -1) {
  1285. best_etype = STUCK_PE;
  1286. jump GiveError;
  1287. }
  1288. ! Now try for "again" or "g", which are special cases: don't allow "again" if nothing
  1289. ! has previously been typed; simply copy the previous text across
  1290. if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;
  1291. if (verb_word == AGAIN1__WD) {
  1292. if (actor ~= player) {
  1293. L__M(##Miscellany, 20);
  1294. jump ReType;
  1295. }
  1296. if (GetKeyBufLength(buffer3) == 0) {
  1297. L__M(##Miscellany, 21);
  1298. jump ReType;
  1299. }
  1300. if (WordAddress(verb_wordnum) == buffer + WORDSIZE) { ! not held back
  1301. ! splice rest of buffer onto end of buffer3
  1302. i = GetKeyBufLength(buffer3);
  1303. while (buffer3 -> (i + WORDSIZE - 1) == ' ' or '.')
  1304. i--;
  1305. j = i - WordLength(verb_wordnum); ! amount to move buffer up by
  1306. if (j > 0) {
  1307. for (m=INPUT_BUFFER_LEN-1 : m>=WORDSIZE+j : m--)
  1308. buffer->m = buffer->(m-j);
  1309. SetKeyBufLength(GetKeyBufLength()+j);
  1310. }
  1311. for (m=WORDSIZE : m<WORDSIZE+i : m++) buffer->m = buffer3->m;
  1312. if (j < 0) for (:m<WORDSIZE+i-j : m++) buffer->m = ' ';
  1313. }
  1314. else
  1315. for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->i;
  1316. jump ReParse;
  1317. }
  1318. ! Save the present input in case of an "again" next time
  1319. if (verb_word ~= AGAIN1__WD)
  1320. for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->i;
  1321. if (usual_grammar_after == 0) {
  1322. j = verb_wordnum;
  1323. i = RunRoutines(actor, grammar);
  1324. #Ifdef DEBUG;
  1325. if (parser_trace >= 2 && actor.grammar ~= 0 or NULL)
  1326. print " [Grammar property returned ", i, "]^";
  1327. #Endif; ! DEBUG
  1328. #Ifdef TARGET_ZCODE;
  1329. if ((i ~= 0 or 1) &&
  1330. (UnsignedCompare(i, dict_start) < 0 ||
  1331. UnsignedCompare(i, dict_end) >= 0 ||
  1332. (i - dict_start) % dict_entry_size ~= 0)) {
  1333. usual_grammar_after = j;
  1334. i=-i;
  1335. }
  1336. #Ifnot; ! TARGET_GLULX
  1337. if (i < 0) { usual_grammar_after = verb_wordnum; i=-i; }
  1338. #Endif;
  1339. if (i == 1) {
  1340. results-->0 = action;
  1341. results-->1 = 2; ! Number of parameters
  1342. results-->2 = noun;
  1343. results-->3 = second;
  1344. rtrue;
  1345. }
  1346. if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }
  1347. else { wn = verb_wordnum; verb_word = NextWord(); }
  1348. }
  1349. else usual_grammar_after = 0;
  1350. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1351. !
  1352. ! B: Is it a direction, and so an implicit "go"? If so go to (K)
  1353. !
  1354. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1355. #Ifdef LanguageIsVerb;
  1356. if (verb_word == 0) {
  1357. i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
  1358. wn = i;
  1359. }
  1360. #Endif; ! LanguageIsVerb
  1361. ! If the first word is not listed as a verb, it must be a direction
  1362. ! or the name of someone to talk to
  1363. if (verb_word == 0 || ((verb_word->#dict_par1) & DICT_VERB) == 0) {
  1364. ! So is the first word an object contained in the special object "compass"
  1365. ! (i.e., a direction)? This needs use of NounDomain, a routine which
  1366. ! does the object matching, returning the object number, or 0 if none found,
  1367. ! or REPARSE_CODE if it has restructured the parse table so the whole parse
  1368. ! must be begun again...
  1369. wn = verb_wordnum; indef_mode = false; token_filter = 0;
  1370. l = NounDomain(compass, 0, NOUN_TOKEN);
  1371. if (l == REPARSE_CODE) jump ReParse;
  1372. ! If it is a direction, send back the results:
  1373. ! action=GoSub, no of arguments=1, argument 1=the direction.
  1374. if (l ~= 0) {
  1375. results-->0 = ##Go;
  1376. action_to_be = ##Go;
  1377. results-->1 = 1;
  1378. results-->2 = l;
  1379. jump LookForMore;
  1380. }
  1381. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1382. !
  1383. ! C: Is anyone being addressed?
  1384. !
  1385. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1386. ! Only check for a comma (a "someone, do something" command) if we are
  1387. ! not already in the middle of one. (This simplification stops us from
  1388. ! worrying about "robot, wizard, you are an idiot", telling the robot to
  1389. ! tell the wizard that she is an idiot.)
  1390. if (actor == player) {
  1391. for (j=2 : j<=num_words : j++) {
  1392. i=NextWord();
  1393. if (i == comma_word) jump Conversation;
  1394. }
  1395. }
  1396. vw = verb_word;
  1397. verb_word = UnknownVerb(vw);
  1398. if (verb_word == false) verb_word = LibraryExtensions.RunWhile(ext_unknownverb, false, vw);
  1399. if (verb_word) jump VerbAccepted;
  1400. best_etype = VERB_PE;
  1401. jump GiveError;
  1402. ! NextWord nudges the word number wn on by one each time, so we've now
  1403. ! advanced past a comma. (A comma is a word all on its own in the table.)
  1404. .Conversation;
  1405. j = wn - 1;
  1406. if (j == 1) {
  1407. L__M(##Miscellany, 22);
  1408. jump ReType;
  1409. }
  1410. ! Use NounDomain (in the context of "animate creature") to see if the
  1411. ! words make sense as the name of someone held or nearby
  1412. wn = 1; lookahead = HELD_TOKEN;
  1413. scope_reason = TALKING_REASON;
  1414. l = NounDomain(player,actors_location,CREATURE_TOKEN);
  1415. scope_reason = PARSING_REASON;
  1416. if (l == REPARSE_CODE) jump ReParse;
  1417. if (l == 0) {
  1418. L__M(##Miscellany, 23);
  1419. jump ReType;
  1420. }
  1421. .Conversation2;
  1422. ! The object addressed must at least be "talkable" if not actually "animate"
  1423. ! (the distinction allows, for instance, a microphone to be spoken to,
  1424. ! without the parser thinking that the microphone is human).
  1425. if (l hasnt animate && l hasnt talkable) {
  1426. L__M(##Miscellany, 24, l);
  1427. jump ReType;
  1428. }
  1429. ! Check that there aren't any mystery words between the end of the person's
  1430. ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
  1431. if (wn ~= j) {
  1432. L__M(##Miscellany, 25);
  1433. jump ReType;
  1434. }
  1435. ! The player has now successfully named someone. Adjust "him", "her", "it":
  1436. PronounNotice(l);
  1437. ! Set the global variable "actor", adjust the number of the first word,
  1438. ! and begin parsing again from there.
  1439. verb_wordnum = j + 1;
  1440. ! Stop things like "me, again":
  1441. if (l == player) {
  1442. wn = verb_wordnum;
  1443. if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
  1444. L__M(##Miscellany, 20);
  1445. jump ReType;
  1446. }
  1447. }
  1448. actor = l;
  1449. actors_location = ScopeCeiling(l);
  1450. #Ifdef DEBUG;
  1451. if (parser_trace >= 1)
  1452. print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";
  1453. #Endif; ! DEBUG
  1454. jump BeginCommand;
  1455. } ! end of first-word-not-a-verb
  1456. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1457. !
  1458. ! D: Get the verb: try all the syntax lines for that verb
  1459. !
  1460. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1461. .VerbAccepted;
  1462. ! We now definitely have a verb, not a direction, whether we got here by the
  1463. ! "take ..." or "person, take ..." method. Get the meta flag for this verb:
  1464. meta = (verb_word->#dict_par1) & DICT_META;
  1465. ! You can't order other people to "full score" for you, and so on...
  1466. if (meta && actor ~= player) {
  1467. best_etype = VERB_PE;
  1468. meta = false;
  1469. jump GiveError;
  1470. }
  1471. ! Now let i be the corresponding verb number, stored in the dictionary entry
  1472. ! (in a peculiar 255-n fashion for traditional Infocom reasons)...
  1473. i = $ff-(verb_word->#dict_par2);
  1474. ! ...then look up the i-th entry in the verb table, whose address is at word
  1475. ! 7 in the Z-machine (in the header), so as to get the address of the syntax
  1476. ! table for the given verb...
  1477. #Ifdef TARGET_ZCODE;
  1478. syntax = (HDR_STATICMEMORY-->0)-->i;
  1479. #Ifnot; ! TARGET_GLULX
  1480. syntax = (#grammar_table)-->(i+1);
  1481. #Endif; ! TARGET_
  1482. ! ...and then see how many lines (ie, different patterns corresponding to the
  1483. ! same verb) are stored in the parse table...
  1484. num_lines = (syntax->0) - 1;
  1485. ! ...and now go through them all, one by one.
  1486. ! To prevent pronoun_word 0 being misunderstood,
  1487. pronoun_word = NULL; pronoun_obj = NULL;
  1488. #Ifdef DEBUG;
  1489. if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^";
  1490. #Endif; ! DEBUG
  1491. best_etype = STUCK_PE; nextbest_etype = STUCK_PE;
  1492. multiflag = false; saved_oops = 0;
  1493. ! "best_etype" is the current failure-to-match error - it is by default
  1494. ! the least informative one, "don't understand that sentence".
  1495. ! "nextbest_etype" remembers the best alternative to having to ask a
  1496. ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
  1497. ! multiflag is used here to prevent inappropriate MULTI_PE errors
  1498. ! in addition to its unrelated duties passing information to action routines
  1499. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1500. !
  1501. ! E: Break down a syntax line into analysed tokens
  1502. !
  1503. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1504. line_address = syntax + 1;
  1505. for (line=0 : line<=num_lines : line++) {
  1506. for (i=0 : i<32 : i++) {
  1507. line_token-->i = ENDIT_TOKEN;
  1508. line_ttype-->i = ELEMENTARY_TT;
  1509. line_tdata-->i = ENDIT_TOKEN;
  1510. }
  1511. ! Unpack the syntax line from Inform format into three arrays; ensure that
  1512. ! the sequence of tokens ends in an ENDIT_TOKEN.
  1513. line_address = UnpackGrammarLine(line_address);
  1514. #Ifdef DEBUG;
  1515. if (parser_trace >= 1) {
  1516. if (parser_trace >= 2) new_line;
  1517. print "[line ", line; DebugGrammarLine();
  1518. print "]^";
  1519. }
  1520. #Endif; ! DEBUG
  1521. ! We aren't in "not holding" or inferring modes, and haven't entered
  1522. ! any parameters on the line yet, or any special numbers; the multiple
  1523. ! object is still empty.
  1524. token_filter = 0;
  1525. not_holding = 0;
  1526. inferfrom = 0;
  1527. parameters = 0;
  1528. nsns = 0; special_word = 0; special_number = 0;
  1529. multiple_object-->0 = 0;
  1530. multi_context = 0;
  1531. etype = STUCK_PE; line_etype = 100;
  1532. ! Put the word marker back to just after the verb
  1533. wn = verb_wordnum+1;
  1534. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1535. !
  1536. ! F: Look ahead for advance warning for multiexcept/multiinside
  1537. !
  1538. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1539. ! There are two special cases where parsing a token now has to be
  1540. ! affected by the result of parsing another token later, and these
  1541. ! two cases (multiexcept and multiinside tokens) are helped by a quick
  1542. ! look ahead, to work out the future token now. We can only carry this
  1543. ! out in the simple (but by far the most common) case:
  1544. !
  1545. ! multiexcept <one or more prepositions> noun
  1546. !
  1547. ! and similarly for multiinside.
  1548. advance_warning = NULL; indef_mode = false;
  1549. for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
  1550. scope_token = 0;
  1551. if (line_ttype-->pcount ~= PREPOSITION_TT) i++;
  1552. if (line_ttype-->pcount == ELEMENTARY_TT) {
  1553. if (line_tdata-->pcount == MULTI_TOKEN) m = true;
  1554. if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN && i == 1) {
  1555. ! First non-preposition is "multiexcept" or
  1556. ! "multiinside", so look ahead.
  1557. #Ifdef DEBUG;
  1558. if (parser_trace >= 2) print " [Trying look-ahead]^";
  1559. #Endif; ! DEBUG
  1560. ! We need this to be followed by 1 or more prepositions.
  1561. pcount++;
  1562. if (line_ttype-->pcount == PREPOSITION_TT) {
  1563. ! skip ahead to a preposition word in the input
  1564. do {
  1565. l = NextWord();
  1566. } until ((wn > num_words) ||
  1567. (l && (l->#dict_par1) & DICT_PREP ~= 0));
  1568. if (wn > num_words) {
  1569. #Ifdef DEBUG;
  1570. if (parser_trace >= 2)
  1571. print " [Look-ahead aborted: prepositions missing]^";
  1572. #Endif;
  1573. jump EmptyLine;
  1574. }
  1575. do {
  1576. if (PrepositionChain(l, pcount) ~= -1) {
  1577. ! advance past the chain
  1578. if ((line_token-->pcount)->0 & $20 ~= 0) {
  1579. pcount++;
  1580. while ((line_token-->pcount ~= ENDIT_TOKEN) &&
  1581. ((line_token-->pcount)->0 & $10 ~= 0))
  1582. pcount++;
  1583. } else {
  1584. pcount++;
  1585. }
  1586. } else {
  1587. ! try to find another preposition word
  1588. do {
  1589. l = NextWord();
  1590. } until ((wn >= num_words) ||
  1591. (l && (l->#dict_par1) & 8 ~= 0));
  1592. if (l && (l->#dict_par1) & 8) continue;
  1593. ! lookahead failed
  1594. #Ifdef DEBUG;
  1595. if (parser_trace >= 2)
  1596. print " [Look-ahead aborted: prepositions don't match]^";
  1597. #Endif;
  1598. jump LineFailed;
  1599. }
  1600. l = NextWord();
  1601. } until (line_ttype-->pcount ~= PREPOSITION_TT);
  1602. .EmptyLine;
  1603. ! put back the non-preposition we just read
  1604. wn--;
  1605. if ((line_ttype-->pcount == ELEMENTARY_TT) && (line_tdata-->pcount == NOUN_TOKEN)) {
  1606. l = Descriptors(); ! skip past THE etc
  1607. if (l~=0) etype=l; ! don't allow multiple objects
  1608. l = NounDomain(actors_location, actor, NOUN_TOKEN);
  1609. #Ifdef DEBUG;
  1610. if (parser_trace >= 2) {
  1611. print " [Advanced to ~noun~ token: ";
  1612. if (l == REPARSE_CODE) print "re-parse request]^";
  1613. if (l == 1) print "but multiple found]^";
  1614. if (l == 0) print "error ", etype, "]^";
  1615. if (l >= 2) print (the) l, "]^";
  1616. }
  1617. #Endif; ! DEBUG
  1618. if (l == REPARSE_CODE) jump ReParse;
  1619. if (l >= 2) advance_warning = l;
  1620. }
  1621. }
  1622. break;
  1623. }
  1624. }
  1625. }
  1626. ! Slightly different line-parsing rules will apply to "take multi", to
  1627. ! prevent "take all" behaving correctly but misleadingly when there's
  1628. ! nothing to take.
  1629. take_all_rule = 0;
  1630. if (m && params_wanted == 1 && action_to_be == ##Take)
  1631. take_all_rule = 1;
  1632. ! And now start again, properly, forearmed or not as the case may be.
  1633. ! As a precaution, we clear all the variables again (they may have been
  1634. ! disturbed by the call to NounDomain, which may have called outside
  1635. ! code, which may have done anything!).
  1636. not_holding = 0;
  1637. inferfrom = 0;
  1638. inferword = 0;
  1639. parameters = 0;
  1640. nsns = 0; special_word = 0; special_number = 0;
  1641. multiple_object-->0 = 0;
  1642. etype = STUCK_PE; line_etype = 100;
  1643. wn = verb_wordnum+1;
  1644. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1645. !
  1646. ! G: Parse each token in turn (calling ParseToken to do most of the work)
  1647. !
  1648. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1649. ! "Pattern" gradually accumulates what has been recognised so far,
  1650. ! so that it may be reprinted by the parser later on
  1651. for (pcount=1 : : pcount++) {
  1652. pattern-->pcount = PATTERN_NULL; scope_token = 0;
  1653. token = line_token-->(pcount-1);
  1654. lookahead = line_token-->pcount;
  1655. #Ifdef DEBUG;
  1656. if (parser_trace >= 2)
  1657. print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token,
  1658. "]^";
  1659. #Endif; ! DEBUG
  1660. if (token ~= ENDIT_TOKEN) {
  1661. scope_reason = PARSING_REASON;
  1662. parser_inflection = name;
  1663. parser_inflection_func = false;
  1664. AnalyseToken(token);
  1665. if (action_to_be == ##AskTo && found_ttype == ELEMENTARY_TT &&
  1666. found_tdata == TOPIC_TOKEN && line_etype == 100) {
  1667. if (actor ~= player) {
  1668. best_etype = VERB_PE; jump GiveError;
  1669. }
  1670. l = inputobjs-->2;
  1671. wn--;
  1672. j = wn;
  1673. jump Conversation2;
  1674. }
  1675. l = ParseToken__(found_ttype, found_tdata, pcount-1, token);
  1676. while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256);
  1677. scope_reason = PARSING_REASON;
  1678. if (l == GPR_PREPOSITION) {
  1679. if (found_ttype~=PREPOSITION_TT && (found_ttype~=ELEMENTARY_TT ||
  1680. found_tdata~=TOPIC_TOKEN)) params_wanted--;
  1681. l = true;
  1682. }
  1683. else
  1684. if (l < 0) l = false;
  1685. else
  1686. if (l ~= GPR_REPARSE) {
  1687. if (l == GPR_NUMBER) {
  1688. if (nsns == 0) special_number1 = parsed_number;
  1689. else special_number2 = parsed_number;
  1690. nsns++; l = 1;
  1691. }
  1692. if (l == GPR_MULTIPLE) l = 0;
  1693. results-->(parameters+2) = l;
  1694. parameters++;
  1695. pattern-->pcount = l;
  1696. l = true;
  1697. }
  1698. #Ifdef DEBUG;
  1699. if (parser_trace >= 3) {
  1700. print " [token resulted in ";
  1701. if (l == REPARSE_CODE) print "re-parse request]^";
  1702. if (l == 0) print "failure with error type ", etype, "]^";
  1703. if (l == 1) print "success]^";
  1704. }
  1705. #Endif; ! DEBUG
  1706. if (l == REPARSE_CODE) jump ReParse;
  1707. if (l == false) {
  1708. if (etype < line_etype) line_etype = etype;
  1709. if (etype == STUCK_PE || wn >= num_words) break;
  1710. }
  1711. }
  1712. else {
  1713. ! If the player has entered enough already but there's still
  1714. ! text to wade through: store the pattern away so as to be able to produce
  1715. ! a decent error message if this turns out to be the best we ever manage,
  1716. ! and in the mean time give up on this line
  1717. ! However, if the superfluous text begins with a comma or "then" then
  1718. ! take that to be the start of another instruction
  1719. if (line_etype < 100) break;
  1720. if (wn <= num_words) {
  1721. l = NextWord();
  1722. if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) {
  1723. held_back_mode = 1; hb_wn = wn-1;
  1724. }
  1725. else {
  1726. for (m=0 : m<32 : m++) pattern2-->m = pattern-->m;
  1727. pcount2 = pcount;
  1728. etype = UPTO_PE;
  1729. break;
  1730. }
  1731. }
  1732. ! Now, we may need to revise the multiple object because of the single one
  1733. ! we now know (but didn't when the list was drawn up).
  1734. if (parameters >= 1 && results-->2 == 0) {
  1735. l = ReviseMulti(results-->3);
  1736. if (l ~= 0) { etype = l; results-->0 = action_to_be; break; }
  1737. }
  1738. if (parameters >= 2 && results-->3 == 0) {
  1739. l = ReviseMulti(results-->2);
  1740. if (l ~= 0) { etype = l; break; }
  1741. }
  1742. ! To trap the case of "take all" inferring only "yourself" when absolutely
  1743. ! nothing else is in the vicinity...
  1744. if (take_all_rule == 2 && results-->2 == actor) {
  1745. best_etype = NOTHING_PE;
  1746. jump GiveError;
  1747. }
  1748. #Ifdef DEBUG;
  1749. if (parser_trace >= 1) print "[Line successfully parsed]^";
  1750. #Endif; ! DEBUG
  1751. ! The line has successfully matched the text. Declare the input error-free...
  1752. oops_from = 0;
  1753. ! ...explain any inferences made (using the pattern)...
  1754. if (inferfrom ~= 0 && no_infer_message == false) {
  1755. print "("; PrintCommand(inferfrom); print ")^";
  1756. }
  1757. no_infer_message = false;
  1758. ! ...copy the action number, and the number of parameters...
  1759. results-->0 = action_to_be;
  1760. results-->1 = parameters;
  1761. ! ...reverse first and second parameters if need be...
  1762. if (action_reversed && parameters == 2) {
  1763. i = results-->2; results-->2 = results-->3;
  1764. results-->3 = i;
  1765. if (nsns == 2) {
  1766. i = special_number1; special_number1 = special_number2;
  1767. special_number2 = i;
  1768. }
  1769. }
  1770. ! ...and to reset "it"-style objects to the first of these parameters, if
  1771. ! there is one (and it really is an object)...
  1772. if (parameters > 0 && results-->2 >= 2)
  1773. PronounNotice(results-->2);
  1774. ! ...and worry about the case where an object was allowed as a parameter
  1775. ! even though the player wasn't holding it and should have been: in this
  1776. ! event, keep the results for next time round, go into "not holding" mode,
  1777. ! and for now tell the player what's happening and return a "take" request
  1778. ! instead...
  1779. if (not_holding ~= 0 && actor == player) {
  1780. action = ##Take;
  1781. i = RunRoutines(not_holding, before_implicit);
  1782. ! i = 0: Take the object, tell the player (default)
  1783. ! i = 1: Take the object, don't tell the player
  1784. ! i = 2: don't Take the object, continue
  1785. ! i = 3: don't Take the object, don't continue
  1786. if (i > 2 || no_implicit_actions) { best_etype = NOTHELD_PE; jump GiveError; }
  1787. ! perform the implicit Take
  1788. if (i < 2) {
  1789. if (i ~= 1) ! and tell the player
  1790. L__M(##Miscellany, 26, not_holding);
  1791. notheld_mode = 1;
  1792. for (i=0 : i<8 : i++) kept_results-->i = results-->i;
  1793. results-->0 = ##Take;
  1794. results-->1 = 1;
  1795. results-->2 = not_holding;
  1796. }
  1797. }
  1798. ! (Notice that implicit takes are only generated for the player, and not
  1799. ! for other actors. This avoids entirely logical, but misleading, text
  1800. ! being printed.)
  1801. ! ...and return from the parser altogether, having successfully matched
  1802. ! a line.
  1803. if (held_back_mode == 1) {
  1804. wn=hb_wn;
  1805. jump LookForMore;
  1806. }
  1807. rtrue;
  1808. } ! end of if(token ~= ENDIT_TOKEN) else
  1809. } ! end of for(pcount++)
  1810. .LineFailed;
  1811. ! The line has failed to match.
  1812. ! We continue the outer "for" loop, trying the next line in the grammar.
  1813. if (line_etype < 100) etype = line_etype;
  1814. if (etype > best_etype) best_etype = etype;
  1815. if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype;
  1816. ! ...unless the line was something like "take all" which failed because
  1817. ! nothing matched the "all", in which case we stop and give an error now.
  1818. if (take_all_rule == 2 && etype==NOTHING_PE) break;
  1819. } ! end of for(line++)
  1820. ! The grammar is exhausted: every line has failed to match.
  1821. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1822. !
  1823. ! H: Cheaply parse otherwise unrecognised conversation and return
  1824. !
  1825. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1826. .GiveError;
  1827. etype = best_etype;
  1828. ! Errors are handled differently depending on who was talking.
  1829. ! If the command was addressed to somebody else (eg, "dwarf, sfgh") then
  1830. ! it is taken as conversation which the parser has no business in disallowing.
  1831. if (actor ~= player) {
  1832. if (usual_grammar_after ~= 0) {
  1833. verb_wordnum = usual_grammar_after;
  1834. jump AlmostReParse;
  1835. }
  1836. wn = verb_wordnum;
  1837. special_word = NextWord();
  1838. if (special_word == comma_word) {
  1839. special_word = NextWord();
  1840. verb_wordnum++;
  1841. }
  1842. special_number = TryNumber(verb_wordnum);
  1843. results-->0 = ##NotUnderstood;
  1844. results-->1 = 2;
  1845. results-->2 = 1; special_number1 = special_word;
  1846. results-->3 = actor;
  1847. consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
  1848. rtrue;
  1849. }
  1850. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1851. !
  1852. ! I: Print best possible error message
  1853. !
  1854. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1855. ! If the player was the actor (eg, in "take dfghh") the error must be
  1856. ! printed, and fresh input called for. In four cases the oops word
  1857. ! must be jiggled (where oops_from is set to something).
  1858. if (ParserError(etype)) jump ReType;
  1859. if (LibraryExtensions.RunWhile(ext_parsererror, false, etype)) jump ReType;
  1860. pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;
  1861. if (etype == STUCK_PE) { L__M(##Miscellany, 27); oops_from = 1; }
  1862. if (etype == UPTO_PE) { L__M(##Miscellany, 28);
  1863. for (m=0 : m<32 : m++) pattern-->m = pattern2-->m;
  1864. pcount = pcount2; PrintCommand(0); L__M(##Miscellany, 56);
  1865. oops_from = wn-1;
  1866. }
  1867. if (etype == NUMBER_PE) L__M(##Miscellany, 29);
  1868. if (etype == CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops; }
  1869. if (etype == TOOLIT_PE) L__M(##Miscellany, 31);
  1870. if (etype == NOTHELD_PE) { L__M(##Miscellany, 32, not_holding); oops_from=saved_oops; }
  1871. if (etype == MULTI_PE) L__M(##Miscellany, 33);
  1872. if (etype == MMULTI_PE) L__M(##Miscellany, 34);
  1873. if (etype == VAGUE_PE) L__M(##Miscellany, 35, pronoun_word);
  1874. if (etype == EXCEPT_PE) L__M(##Miscellany, 36);
  1875. if (etype == ANIMA_PE) L__M(##Miscellany, 37);
  1876. if (etype == VERB_PE) L__M(##Miscellany, 38);
  1877. if (etype == SCENERY_PE) L__M(##Miscellany, 39);
  1878. if (etype == ITGONE_PE) {
  1879. if (pronoun_obj == NULL)
  1880. L__M(##Miscellany, 35, pronoun_word);
  1881. else L__M(##Miscellany, 40, pronoun_word, pronoun_obj);
  1882. }
  1883. if (etype == JUNKAFTER_PE) L__M(##Miscellany, 41);
  1884. if (etype == TOOFEW_PE) L__M(##Miscellany, 42, multi_had);
  1885. if (etype == NOTHING_PE) {
  1886. if (results-->0 == ##Remove && results-->3 ofclass Object) {
  1887. noun = results-->3; ! ensure valid for messages
  1888. if (noun has animate) L__M(##Take, 6, noun);
  1889. else if (noun hasnt container or supporter) L__M(##Insert, 2, noun);
  1890. else if (noun has container && noun hasnt open) L__M(##Take, 9, noun);
  1891. else if (children(noun)==0) L__M(##Search, 6, noun);
  1892. else results-->0 = 0;
  1893. }
  1894. if (results-->0 ~= ##Remove) {
  1895. if (multi_wanted == 100) L__M(##Miscellany, 43);
  1896. else {
  1897. #Ifdef NO_TAKE_ALL;
  1898. if (take_all_rule == 2) L__M(##Miscellany, 59);
  1899. else L__M(##Miscellany, 44, verb_word);
  1900. #Ifnot;
  1901. L__M(##Miscellany, 44, verb_word);
  1902. #Endif; ! NO_TAKE_ALL
  1903. }
  1904. }
  1905. }
  1906. if (etype == ASKSCOPE_PE) {
  1907. scope_stage = 3;
  1908. if (scope_error() == -1) {
  1909. best_etype = nextbest_etype;
  1910. jump GiveError;
  1911. }
  1912. }
  1913. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1914. !
  1915. ! J: Retry the whole lot
  1916. !
  1917. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1918. ! And go (almost) right back to square one...
  1919. jump ReType;
  1920. ! ...being careful not to go all the way back, to avoid infinite repetition
  1921. ! of a deferred command causing an error.
  1922. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1923. !
  1924. ! K: Last thing: check for "then" and further instructions(s), return.
  1925. !
  1926. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1927. ! At this point, the return value is all prepared, and we are only looking
  1928. ! to see if there is a "then" followed by subsequent instruction(s).
  1929. .LookForMore;
  1930. if (wn > num_words) rtrue;
  1931. i = NextWord();
  1932. if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) {
  1933. if (wn > num_words) {
  1934. held_back_mode = false;
  1935. return;
  1936. }
  1937. i = WordAddress(verb_wordnum);
  1938. j = WordAddress(wn);
  1939. for (: i<j : i++) i->0 = ' ';
  1940. i = NextWord();
  1941. if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
  1942. ! Delete the words "then again" from the again buffer,
  1943. ! in which we have just realised that it must occur:
  1944. ! prevents an infinite loop on "i. again"
  1945. i = WordAddress(wn-2)-buffer;
  1946. if (wn > num_words) j = INPUT_BUFFER_LEN-1;
  1947. else j = WordAddress(wn)-buffer;
  1948. for (: i<j : i++) buffer3->i = ' ';
  1949. }
  1950. Tokenise__(buffer,parse);
  1951. held_back_mode = true;
  1952. return;
  1953. }
  1954. best_etype = UPTO_PE;
  1955. jump GiveError;
  1956. ]; ! end of Parser__parse
  1957. [ ScopeCeiling person act;
  1958. act = parent(person);
  1959. if (act == 0) return person;
  1960. if (person == player && location == thedark) return thedark;
  1961. while (parent(act)~=0 && (act has transparent || act has supporter ||
  1962. (act has container && act has open)))
  1963. act = parent(act);
  1964. return act;
  1965. ];
  1966. ! ----------------------------------------------------------------------------
  1967. ! Descriptors()
  1968. !
  1969. ! Handles descriptive words like "my", "his", "another" and so on.
  1970. ! Skips "the", and leaves wn pointing to the first misunderstood word.
  1971. !
  1972. ! Allowed to set up for a plural only if allow_p is set
  1973. !
  1974. ! Returns error number, or 0 if no error occurred
  1975. ! ----------------------------------------------------------------------------
  1976. Constant OTHER_BIT = 1; ! These will be used in Adjudicate()
  1977. Constant MY_BIT = 2; ! to disambiguate choices
  1978. Constant THAT_BIT = 4;
  1979. Constant PLURAL_BIT = 8;
  1980. Constant LIT_BIT = 16;
  1981. Constant UNLIT_BIT = 32;
  1982. [ ResetDescriptors;
  1983. indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0;
  1984. indef_possambig = false;
  1985. indef_owner = nothing;
  1986. indef_cases = $$111111111111;
  1987. indef_nspec_at = 0;
  1988. ];
  1989. [ Descriptors allows_multiple o x flag cto type m n;
  1990. ResetDescriptors();
  1991. if (wn > num_words) return 0;
  1992. m = wn;
  1993. for (flag=true : flag :) {
  1994. o = NextWordStopped(); flag = false;
  1995. for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
  1996. if (o == LanguageDescriptors-->x) {
  1997. flag = true;
  1998. type = LanguageDescriptors-->(x+2);
  1999. if (type ~= DEFART_PK) indef_mode = true;
  2000. indef_possambig = true;
  2001. indef_cases = indef_cases & (LanguageDescriptors-->(x+1));
  2002. if (type == POSSESS_PK) {
  2003. cto = LanguageDescriptors-->(x+3);
  2004. switch (cto) {
  2005. 0: indef_type = indef_type | MY_BIT;
  2006. 1: indef_type = indef_type | THAT_BIT;
  2007. default:
  2008. indef_owner = PronounValue(cto);
  2009. if (indef_owner == NULL) indef_owner = InformParser;
  2010. }
  2011. }
  2012. if (type == light) indef_type = indef_type | LIT_BIT;
  2013. if (type == -light) indef_type = indef_type | UNLIT_BIT;
  2014. }
  2015. if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
  2016. indef_mode = 1; flag = 1;
  2017. indef_type = indef_type | OTHER_BIT;
  2018. }
  2019. if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
  2020. indef_mode = 1; flag = 1; indef_wanted = 100;
  2021. if (take_all_rule == 1) take_all_rule = 2;
  2022. indef_type = indef_type | PLURAL_BIT;
  2023. }
  2024. if (allow_plurals && allows_multiple) {
  2025. n = TryNumber(wn-1);
  2026. if (n == 1) { indef_mode = 1; flag = 1; indef_wanted = 1; }
  2027. if (n > 1) {
  2028. indef_guess_p = 1;
  2029. indef_mode = 1; flag = 1; indef_wanted = n;
  2030. indef_nspec_at = wn-1;
  2031. indef_type = indef_type | PLURAL_BIT;
  2032. }
  2033. }
  2034. if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
  2035. wn--; ! Skip 'of' after these
  2036. }
  2037. wn--;
  2038. num_desc = wn - m;
  2039. return 0;
  2040. ];
  2041. ! ----------------------------------------------------------------------------
  2042. ! CreatureTest: Will this person do for a "creature" token?
  2043. ! ----------------------------------------------------------------------------
  2044. [ CreatureTest obj;
  2045. if (actor ~= player) rtrue;
  2046. if (obj has animate) rtrue;
  2047. if (obj hasnt talkable) rfalse;
  2048. if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue;
  2049. rfalse;
  2050. ];
  2051. [ PrepositionChain wd index;
  2052. if (line_tdata-->index == wd) return wd;
  2053. if ((line_token-->index)->0 & $20 == 0) return -1;
  2054. do {
  2055. if (line_tdata-->index == wd) return wd;
  2056. index++;
  2057. } until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $10) == 0));
  2058. return -1;
  2059. ];
  2060. ! ----------------------------------------------------------------------------
  2061. ! ParseToken(type, data):
  2062. ! Parses the given token, from the current word number wn, with exactly
  2063. ! the specification of a general parsing routine.
  2064. ! (Except that for "topic" tokens and prepositions, you need to supply
  2065. ! a position in a valid grammar line as third argument.)
  2066. !
  2067. ! Returns:
  2068. ! GPR_REPARSE for "reconstructed input, please re-parse from scratch"
  2069. ! GPR_PREPOSITION for "token accepted with no result"
  2070. ! $ff00 + x for "please parse ParseToken(ELEMENTARY_TT, x) instead"
  2071. ! 0 for "token accepted, result is the multiple object list"
  2072. ! 1 for "token accepted, result is the number in parsed_number"
  2073. ! object num for "token accepted with this object as result"
  2074. ! -1 for "token rejected"
  2075. !
  2076. ! (A) Analyse the token; handle all tokens not involving
  2077. ! object lists and break down others into elementary tokens
  2078. ! (B) Begin parsing an object list
  2079. ! (C) Parse descriptors (articles, pronouns, etc.) in the list
  2080. ! (D) Parse an object name
  2081. ! (E) Parse connectives ("and", "but", etc.) and go back to (C)
  2082. ! (F) Return the conclusion of parsing an object list
  2083. ! ----------------------------------------------------------------------------
  2084. [ ParseToken given_ttype given_tdata token_n x y;
  2085. x = lookahead; lookahead = NOUN_TOKEN;
  2086. y = ParseToken__(given_ttype,given_tdata,token_n);
  2087. if (y == GPR_REPARSE) Tokenise__(buffer,parse);
  2088. lookahead = x; return y;
  2089. ];
  2090. [ ParseToken__ given_ttype given_tdata token_n
  2091. token l o i j k and_parity single_object desc_wn many_flag
  2092. token_allows_multiple prev_indef_wanted;
  2093. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2094. !
  2095. ! A: Analyse token; handle all not involving object lists, break down others
  2096. !
  2097. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2098. token_filter = 0;
  2099. switch (given_ttype) {
  2100. ELEMENTARY_TT:
  2101. switch (given_tdata) {
  2102. SPECIAL_TOKEN:
  2103. l = TryNumber(wn);
  2104. special_word = NextWord();
  2105. #Ifdef DEBUG;
  2106. if (l ~= -1000)
  2107. if (parser_trace >= 3) print " [Read special as the number ", l, "]^";
  2108. #Endif; ! DEBUG
  2109. if (l == -1000) {
  2110. #Ifdef DEBUG;
  2111. if (parser_trace >= 3) print " [Read special word at word number ", wn, "]^";
  2112. #Endif; ! DEBUG
  2113. l = special_word;
  2114. }
  2115. parsed_number = l; return GPR_NUMBER;
  2116. NUMBER_TOKEN:
  2117. l=TryNumber(wn++);
  2118. if (l == -1000) { etype = NUMBER_PE; return GPR_FAIL; }
  2119. #Ifdef DEBUG;
  2120. if (parser_trace>=3) print " [Read number as ", l, "]^";
  2121. #Endif; ! DEBUG
  2122. parsed_number = l; return GPR_NUMBER;
  2123. CREATURE_TOKEN:
  2124. if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell)
  2125. scope_reason = TALKING_REASON;
  2126. TOPIC_TOKEN:
  2127. consult_from = wn;
  2128. if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) &&
  2129. (line_token-->(token_n+1) ~= ENDIT_TOKEN))
  2130. RunTimeError(13);
  2131. do o = NextWordStopped();
  2132. until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);
  2133. wn--;
  2134. consult_words = wn-consult_from;
  2135. if (consult_words == 0) return GPR_FAIL;
  2136. if (action_to_be == ##Ask or ##Answer or ##Tell) {
  2137. o = wn; wn = consult_from; parsed_number = NextWord();
  2138. #Ifdef EnglishNaturalLanguage;
  2139. if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord();
  2140. #Endif; ! EnglishNaturalLanguage
  2141. wn = o; return 1;
  2142. }
  2143. if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
  2144. return GPR_FAIL; ! don't infer if required preposition is absent
  2145. return GPR_PREPOSITION;
  2146. }
  2147. PREPOSITION_TT:
  2148. #Iffalse (Grammar__Version == 1);
  2149. ! Is it an unnecessary alternative preposition, when a previous choice
  2150. ! has already been matched?
  2151. if ((token->0) & $10) return GPR_PREPOSITION;
  2152. #Endif; ! Grammar__Version
  2153. ! If we've run out of the player's input, but still have parameters to
  2154. ! specify, we go into "infer" mode, remembering where we are and the
  2155. ! preposition we are inferring...
  2156. if (wn > num_words) {
  2157. if (inferfrom==0 && parameters<params_wanted) {
  2158. inferfrom = pcount; inferword = token;
  2159. pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
  2160. }
  2161. ! If we are not inferring, then the line is wrong...
  2162. if (inferfrom == 0) return -1;
  2163. ! If not, then the line is right but we mark in the preposition...
  2164. pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
  2165. return GPR_PREPOSITION;
  2166. }
  2167. o = NextWord();
  2168. pattern-->pcount = REPARSE_CODE + Dword__No(o);
  2169. ! Whereas, if the player has typed something here, see if it is the
  2170. ! required preposition... if it's wrong, the line must be wrong,
  2171. ! but if it's right, the token is passed (jump to finish this token).
  2172. if (o == given_tdata) return GPR_PREPOSITION;
  2173. #Iffalse (Grammar__Version == 1);
  2174. if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION;
  2175. #Endif; ! Grammar__Version
  2176. return -1;
  2177. GPR_TT:
  2178. l = given_tdata();
  2179. #Ifdef DEBUG;
  2180. if (parser_trace >= 3) print " [Outside parsing routine returned ", l, "]^";
  2181. #Endif; ! DEBUG
  2182. return l;
  2183. SCOPE_TT:
  2184. scope_token = given_tdata;
  2185. scope_stage = 1;
  2186. l = scope_token();
  2187. #Ifdef DEBUG;
  2188. if (parser_trace >= 3) print " [Scope routine returned multiple-flag of ", l, "]^";
  2189. #Endif; ! DEBUG
  2190. if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;
  2191. ATTR_FILTER_TT:
  2192. token_filter = 1 + given_tdata;
  2193. given_tdata = NOUN_TOKEN;
  2194. ROUTINE_FILTER_TT:
  2195. token_filter = given_tdata;
  2196. given_tdata = NOUN_TOKEN;
  2197. } ! end of switch(given_ttype)
  2198. token = given_tdata;
  2199. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2200. !
  2201. ! B: Begin parsing an object list
  2202. !
  2203. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2204. ! There are now three possible ways we can be here:
  2205. ! parsing an elementary token other than "special" or "number";
  2206. ! parsing a scope token;
  2207. ! parsing a noun-filter token (either by routine or attribute).
  2208. !
  2209. ! In each case, token holds the type of elementary parse to
  2210. ! perform in matching one or more objects, and
  2211. ! token_filter is 0 (default), an attribute + 1 for an attribute filter
  2212. ! or a routine address for a routine filter.
  2213. token_allows_multiple = false;
  2214. if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
  2215. token_allows_multiple = true;
  2216. many_flag = false; and_parity = true; dont_infer = false;
  2217. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2218. !
  2219. ! C: Parse descriptors (articles, pronouns, etc.) in the list
  2220. !
  2221. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2222. ! We expect to find a list of objects next in what the player's typed.
  2223. .ObjectList;
  2224. #Ifdef DEBUG;
  2225. if (parser_trace >= 3) print " [Object list from word ", wn, "]^";
  2226. #Endif; ! DEBUG
  2227. ! Take an advance look at the next word: if it's "it" or "them", and these
  2228. ! are unset, set the appropriate error number and give up on the line
  2229. ! (if not, these are still parsed in the usual way - it is not assumed
  2230. ! that they still refer to something in scope)
  2231. o = NextWord(); wn--;
  2232. pronoun_word = NULL; pronoun_obj = NULL;
  2233. l = PronounValue(o);
  2234. if (l ~= 0) {
  2235. pronoun_word = o; pronoun_obj = l;
  2236. if (l == NULL) {
  2237. ! Don't assume this is a use of an unset pronoun until the
  2238. ! descriptors have been checked, because it might be an
  2239. ! article (or some such) instead
  2240. for (l=1 : l<=LanguageDescriptors-->0 : l=l+4)
  2241. if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
  2242. pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
  2243. etype = VAGUE_PE; return GPR_FAIL;
  2244. }
  2245. }
  2246. .AssumeDescriptor;
  2247. if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; }
  2248. allow_plurals = true;
  2249. desc_wn = wn;
  2250. .TryAgain;
  2251. ! First, we parse any descriptive words (like "the", "five" or "every"):
  2252. l = Descriptors(token_allows_multiple);
  2253. if (l ~= 0) { etype = l; return GPR_FAIL; }
  2254. .TryAgain2;
  2255. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2256. !
  2257. ! D: Parse an object name
  2258. !
  2259. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2260. ! This is an actual specified object, and is therefore where a typing error
  2261. ! is most likely to occur, so we set:
  2262. oops_from = wn;
  2263. ! So, two cases. Case 1: token not equal to "held"
  2264. ! but we may well be dealing with multiple objects
  2265. ! In either case below we use NounDomain, giving it the token number as
  2266. ! context, and two places to look: among the actor's possessions, and in the
  2267. ! present location. (Note that the order depends on which is likeliest.)
  2268. if (token ~= HELD_TOKEN) {
  2269. i = multiple_object-->0;
  2270. #Ifdef DEBUG;
  2271. if (parser_trace >= 3) print " [Calling NounDomain on location and actor]^";
  2272. #Endif; ! DEBUG
  2273. l = NounDomain(actors_location, actor, token);
  2274. if (l == REPARSE_CODE) return l; ! Reparse after Q&A
  2275. if (l ~= nothing && l ~= 1 && l notin actor && token == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
  2276. if (ImplicitTake(l)) {
  2277. etype = NOTHELD_PE;
  2278. jump FailToken;
  2279. }
  2280. }
  2281. if (indef_wanted == 100 && l == 0 && number_matched == 0)
  2282. l = 1; ! ReviseMulti if TAKE ALL FROM empty container
  2283. if (token_allows_multiple && ~~multiflag) {
  2284. if (best_etype==MULTI_PE) best_etype=STUCK_PE;
  2285. multiflag = true;
  2286. }
  2287. if (l == 0) {
  2288. if (indef_possambig) {
  2289. saved_ml = match_length;
  2290. ResetDescriptors();
  2291. wn = desc_wn;
  2292. jump TryAgain2;
  2293. }
  2294. if (etype ~=TOOFEW_PE && (multiflag || etype ~= MULTI_PE))
  2295. etype = CantSee();
  2296. jump FailToken;
  2297. } ! Choose best error
  2298. #Ifdef DEBUG;
  2299. if (parser_trace >= 3) {
  2300. if (l > 1) print " [NounDomain returned ", (the) l, "]^";
  2301. else {
  2302. print " [NounDomain appended to the multiple object list:^";
  2303. k = multiple_object-->0;
  2304. for (j=i+1 : j<=k : j++)
  2305. print " Entry ", j, ": ", (The) multiple_object-->j,
  2306. " (", multiple_object-->j, ")^";
  2307. print " List now has size ", k, "]^";
  2308. }
  2309. }
  2310. #Endif; ! DEBUG
  2311. if (l == 1) {
  2312. if (~~many_flag) many_flag = true;
  2313. else { ! Merge with earlier ones
  2314. k = multiple_object-->0; ! (with either parity)
  2315. multiple_object-->0 = i;
  2316. for (j=i+1 : j<=k : j++) {
  2317. if (and_parity) MultiAdd(multiple_object-->j);
  2318. else MultiSub(multiple_object-->j);
  2319. }
  2320. #Ifdef DEBUG;
  2321. if (parser_trace >= 3) print " [Merging ", k-i, " new objects to the ", i, " old ones]^";
  2322. #Endif; ! DEBUG
  2323. }
  2324. }
  2325. else {
  2326. ! A single object was indeed found
  2327. if (match_length == 0 && indef_possambig) {
  2328. ! So the answer had to be inferred from no textual data,
  2329. ! and we know that there was an ambiguity in the descriptor
  2330. ! stage (such as a word which could be a pronoun being
  2331. ! parsed as an article or possessive). It's worth having
  2332. ! another go.
  2333. ResetDescriptors();
  2334. wn = desc_wn;
  2335. jump TryAgain2;
  2336. }
  2337. if (token == CREATURE_TOKEN && CreatureTest(l) == 0) {
  2338. etype = ANIMA_PE;
  2339. jump FailToken;
  2340. } ! Animation is required
  2341. if (~~many_flag) single_object = l;
  2342. else {
  2343. if (and_parity) MultiAdd(l); else MultiSub(l);
  2344. #Ifdef DEBUG;
  2345. if (parser_trace >= 3) print " [Combining ", (the) l, " with list]^";
  2346. #Endif; ! DEBUG
  2347. }
  2348. }
  2349. } else {
  2350. ! Case 2: token is "held" (which fortunately can't take multiple objects)
  2351. ! and may generate an implicit take
  2352. l = NounDomain(actor,actors_location,token); ! Same as above...
  2353. if (l == REPARSE_CODE) return GPR_REPARSE;
  2354. if (l == 0) {
  2355. if (indef_possambig) {
  2356. ResetDescriptors();
  2357. wn = desc_wn;
  2358. jump TryAgain2;
  2359. }
  2360. etype = CantSee(); jump FailToken; ! Choose best error
  2361. }
  2362. ! ...until it produces something not held by the actor. Then an implicit
  2363. ! take must be tried. If this is already happening anyway, things are too
  2364. ! confused and we have to give up (but saving the oops marker so as to get
  2365. ! it on the right word afterwards).
  2366. ! The point of this last rule is that a sequence like
  2367. !
  2368. ! > read newspaper
  2369. ! (taking the newspaper first)
  2370. ! The dwarf unexpectedly prevents you from taking the newspaper!
  2371. !
  2372. ! should not be allowed to go into an infinite repeat - read becomes
  2373. ! take then read, but take has no effect, so read becomes take then read...
  2374. ! Anyway for now all we do is record the number of the object to take.
  2375. o = parent(l);
  2376. if (o ~= actor) {
  2377. if (notheld_mode == 1) {
  2378. saved_oops = oops_from;
  2379. etype = NOTHELD_PE;
  2380. jump FailToken;
  2381. }
  2382. not_holding = l;
  2383. #Ifdef DEBUG;
  2384. if (parser_trace >= 3) print " [Allowing object ", (the) l, " for now]^";
  2385. #Endif; ! DEBUG
  2386. }
  2387. single_object = l;
  2388. } ! end of if (token ~= HELD_TOKEN) else
  2389. ! The following moves the word marker to just past the named object...
  2390. wn = oops_from + match_length;
  2391. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2392. !
  2393. ! E: Parse connectives ("and", "but", etc.) and go back to (C)
  2394. !
  2395. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2396. ! Object(s) specified now: is that the end of the list, or have we reached
  2397. ! "and", "but" and so on? If so, create a multiple-object list if we
  2398. ! haven't already (and are allowed to).
  2399. .NextInList;
  2400. o = NextWord();
  2401. if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) {
  2402. #Ifdef DEBUG;
  2403. if (parser_trace >= 3) print " [Read connective '", (address) o, "']^";
  2404. #Endif; ! DEBUG
  2405. k = NextWord();
  2406. if (k ~= AND1__WD) wn--; ! allow Serial commas in input
  2407. if (k > 0 && (k->#dict_par1) & (DICT_NOUN+DICT_VERB) == DICT_VERB) {
  2408. wn--; ! player meant 'THEN'
  2409. jump PassToken;
  2410. }
  2411. if (~~token_allows_multiple) {
  2412. if (multiflag) jump PassToken; ! give UPTO_PE error
  2413. etype=MULTI_PE;
  2414. jump FailToken;
  2415. }
  2416. if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;
  2417. if (~~many_flag) {
  2418. multiple_object-->0 = 1;
  2419. multiple_object-->1 = single_object;
  2420. many_flag = true;
  2421. #Ifdef DEBUG;
  2422. if (parser_trace >= 3) print " [Making new list from ", (the) single_object, "]^";
  2423. #Endif; ! DEBUG
  2424. }
  2425. dont_infer = true; inferfrom=0; ! Don't print (inferences)
  2426. jump ObjectList; ! And back around
  2427. }
  2428. wn--; ! Word marker back to first not-understood word
  2429. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2430. !
  2431. ! F: Return the conclusion of parsing an object list
  2432. !
  2433. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2434. ! Happy or unhappy endings:
  2435. .PassToken;
  2436. if (many_flag) {
  2437. single_object = GPR_MULTIPLE;
  2438. multi_context = token;
  2439. }
  2440. else {
  2441. if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
  2442. if (indef_wanted < 100 && indef_wanted > 1) {
  2443. multi_had = 1; multi_wanted = indef_wanted;
  2444. etype = TOOFEW_PE;
  2445. jump FailToken;
  2446. }
  2447. }
  2448. }
  2449. return single_object;
  2450. .FailToken;
  2451. ! If we were only guessing about it being a plural, try again but only
  2452. ! allowing singulars (so that words like "six" are not swallowed up as
  2453. ! Descriptors)
  2454. if (allow_plurals && indef_guess_p == 1) {
  2455. #Ifdef DEBUG;
  2456. if (parser_trace >= 4) print " [Retrying singulars after failure ", etype, "]^";
  2457. #Endif;
  2458. prev_indef_wanted = indef_wanted;
  2459. allow_plurals = false;
  2460. wn = desc_wn;
  2461. jump TryAgain;
  2462. }
  2463. if ((indef_wanted > 0 || prev_indef_wanted > 0) && (~~multiflag)) etype = MULTI_PE;
  2464. return GPR_FAIL;
  2465. ]; ! end of ParseToken__
  2466. ! ----------------------------------------------------------------------------
  2467. ! NounDomain does the most substantial part of parsing an object name.
  2468. !
  2469. ! It is given two "domains" - usually a location and then the actor who is
  2470. ! looking - and a context (i.e. token type), and returns:
  2471. !
  2472. ! 0 if no match at all could be made,
  2473. ! 1 if a multiple object was made,
  2474. ! k if object k was the one decided upon,
  2475. ! REPARSE_CODE if it asked a question of the player and consequently rewrote
  2476. ! the player's input, so that the whole parser should start again
  2477. ! on the rewritten input.
  2478. !
  2479. ! In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
  2480. ! length_of_noun to the number of words in the input text matched to the
  2481. ! noun.
  2482. ! In the case k=1, the multiple objects are added to multiple_object by
  2483. ! hand (not by MultiAdd, because we want to allow duplicates).
  2484. ! ----------------------------------------------------------------------------
  2485. [ NounDomain domain1 domain2 context first_word i j k l
  2486. answer_words;
  2487. #Ifdef DEBUG;
  2488. if (parser_trace >= 4) {
  2489. print " [NounDomain called at word ", wn, "]^";
  2490. print " ";
  2491. if (indef_mode) {
  2492. print "seeking indefinite object: ";
  2493. if (indef_type & OTHER_BIT) print "other ";
  2494. if (indef_type & MY_BIT) print "my ";
  2495. if (indef_type & THAT_BIT) print "that ";
  2496. if (indef_type & PLURAL_BIT) print "plural ";
  2497. if (indef_type & LIT_BIT) print "lit ";
  2498. if (indef_type & UNLIT_BIT) print "unlit ";
  2499. if (indef_owner ~= 0) print "owner:", (name) indef_owner;
  2500. new_line;
  2501. print " number wanted: ";
  2502. if (indef_wanted == 100) print "all"; else print indef_wanted;
  2503. new_line;
  2504. print " most likely GNAs of names: ", indef_cases, "^";
  2505. }
  2506. else print "seeking definite object^";
  2507. }
  2508. #Endif; ! DEBUG
  2509. match_length = 0; number_matched = 0; match_from = wn; placed_in_flag = 0;
  2510. SearchScope(domain1, domain2, context);
  2511. #Ifdef DEBUG;
  2512. if (parser_trace >= 4) print " [NounDomain made ", number_matched, " matches]^";
  2513. #Endif; ! DEBUG
  2514. wn = match_from+match_length;
  2515. ! If nothing worked at all, leave with the word marker skipped past the
  2516. ! first unmatched word...
  2517. if (number_matched == 0) { wn++; rfalse; }
  2518. ! Suppose that there really were some words being parsed (i.e., we did
  2519. ! not just infer). If so, and if there was only one match, it must be
  2520. ! right and we return it...
  2521. if (match_from <= num_words) {
  2522. if (number_matched == 1) {
  2523. i=match_list-->0;
  2524. if (indef_mode) {
  2525. if ((indef_type & LIT_BIT) && i hasnt light) rfalse;
  2526. if ((indef_type & UNLIT_BIT) && i has light) rfalse;
  2527. }
  2528. return i;
  2529. }
  2530. ! ...now suppose that there was more typing to come, i.e. suppose that
  2531. ! the user entered something beyond this noun. If nothing ought to follow,
  2532. ! then there must be a mistake, (unless what does follow is just a full
  2533. ! stop, and or comma)
  2534. if (wn <= num_words) {
  2535. i = NextWord(); wn--;
  2536. if (i ~= AND1__WD or AND2__WD or AND3__WD or comma_word
  2537. or THEN1__WD or THEN2__WD or THEN3__WD
  2538. or BUT1__WD or BUT2__WD or BUT3__WD) {
  2539. if (lookahead == ENDIT_TOKEN) rfalse;
  2540. }
  2541. }
  2542. }
  2543. ! Now look for a good choice, if there's more than one choice...
  2544. number_of_classes = 0;
  2545. if (match_length == 0 && indef_mode && indef_wanted ~= 100)
  2546. number_matched = 0; ! ask question for 'take three'
  2547. if (number_matched == 1) i = match_list-->0;
  2548. if (number_matched > 1) {
  2549. i = Adjudicate(context);
  2550. if (i == -1) rfalse;
  2551. if (i == 1) rtrue; ! Adjudicate has made a multiple
  2552. ! object, and we pass it on
  2553. }
  2554. ! If i is non-zero here, one of two things is happening: either
  2555. ! (a) an inference has been successfully made that object i is
  2556. ! the intended one from the user's specification, or
  2557. ! (b) the user finished typing some time ago, but we've decided
  2558. ! on i because it's the only possible choice.
  2559. ! In either case we have to keep the pattern up to date,
  2560. ! note that an inference has been made and return.
  2561. ! (Except, we don't note which of a pile of identical objects.)
  2562. if (i ~= 0) {
  2563. if (dont_infer) return i;
  2564. if (inferfrom == 0) inferfrom=pcount;
  2565. pattern-->pcount = i;
  2566. return i;
  2567. }
  2568. ! If we get here, there was no obvious choice of object to make. If in
  2569. ! fact we've already gone past the end of the player's typing (which
  2570. ! means the match list must contain every object in scope, regardless
  2571. ! of its name), then it's foolish to give an enormous list to choose
  2572. ! from - instead we go and ask a more suitable question...
  2573. if (match_from > num_words) jump Incomplete;
  2574. return AskPlayer(context);
  2575. ! Now we come to the question asked when the input has run out
  2576. ! and can't easily be guessed (eg, the player typed "take" and there
  2577. ! were plenty of things which might have been meant).
  2578. .Incomplete;
  2579. if (best_etype == NOTHING_PE && pattern-->1 == 0) rfalse; ! for DROP when empty-handed
  2580. if (context == CREATURE_TOKEN) L__M(##Miscellany, 48);
  2581. else L__M(##Miscellany, 49, actor);
  2582. #Ifdef TARGET_ZCODE;
  2583. for (i=2 : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
  2584. #Endif; ! TARGET_ZCODE
  2585. answer_words = Keyboard(buffer2, parse2);
  2586. first_word = WordValue(1, parse2);
  2587. #Ifdef LanguageIsVerb;
  2588. if (first_word == 0) {
  2589. j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
  2590. }
  2591. #Endif; ! LanguageIsVerb
  2592. ! Once again, if the reply looks like a command, give it to the
  2593. ! parser to get on with and forget about the question...
  2594. ! Once again, if the reply looks like a command
  2595. ! (that is, VERB ... or XXX,VERB ...), give it to the parser to get
  2596. ! on with and forget about the question...
  2597. if (first_word) {
  2598. if ((first_word->#dict_par1) & DICT_VERB) {
  2599. CopyBuffer(buffer, buffer2);
  2600. return REPARSE_CODE;
  2601. }
  2602. if (NumberWords(parse2) > 2) {
  2603. j = WordValue(2, parse2);
  2604. k = WordValue(3, parse2);
  2605. if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) {
  2606. CopyBuffer(buffer, buffer2);
  2607. return REPARSE_CODE;
  2608. }
  2609. }
  2610. }
  2611. ! ...but if we have a genuine answer, then:
  2612. !
  2613. ! (1) we must glue in text suitable for anything that's been inferred.
  2614. if (inferfrom ~= 0) {
  2615. for (j=inferfrom : j<pcount : j++) {
  2616. if (pattern-->j == PATTERN_NULL) continue;
  2617. i = WORDSIZE + GetKeyBufLength();
  2618. SetKeyBufLength(i-WORDSIZE + 1);
  2619. buffer->(i++) = ' ';
  2620. #Ifdef DEBUG;
  2621. if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^";
  2622. #Endif; ! DEBUG
  2623. ! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
  2624. parse2-->1 = 0;
  2625. ! An inferred object. Best we can do is glue in a pronoun.
  2626. ! (This is imperfect, but it's very seldom needed anyway.)
  2627. if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) {
  2628. ! was the inference made from some noun words?
  2629. ! In which case, we can infer again.
  2630. if ((WordValue(NumberWords())->#dict_par1) & DICT_NOUN) continue;
  2631. PronounNotice(pattern-->j);
  2632. for (k=1 : k<=LanguagePronouns-->0 : k=k+3)
  2633. if (pattern-->j == LanguagePronouns-->(k+2)) {
  2634. parse2-->1 = LanguagePronouns-->k;
  2635. #Ifdef DEBUG;
  2636. if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^";
  2637. #Endif; ! DEBUG
  2638. break;
  2639. }
  2640. }
  2641. else {
  2642. ! An inferred preposition.
  2643. parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
  2644. #Ifdef DEBUG;
  2645. if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^";
  2646. #Endif; ! DEBUG
  2647. }
  2648. ! parse2-->1 now holds the dictionary address of the word to glue in.
  2649. if (parse2-->1 ~= 0) {
  2650. k = buffer + i;
  2651. #Ifdef TARGET_ZCODE;
  2652. @output_stream 3 k;
  2653. print (address) parse2-->1;
  2654. @output_stream -3;
  2655. k = k-->0;
  2656. for (l=i : l<i+k : l++) buffer->l = buffer->(l+2);
  2657. #Ifnot; ! TARGET_GLULX
  2658. k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1);
  2659. l=l; ! suppress compiler warning
  2660. #Endif; ! TARGET_
  2661. i = i + k; SetKeyBufLength(i-WORDSIZE);
  2662. }
  2663. }
  2664. }
  2665. ! (2) we must glue the newly-typed text onto the end.
  2666. i = WORDSIZE + GetKeyBufLength();
  2667. buffer->(i++) = ' ';
  2668. SetKeyBufLength(GetKeyBufLength()+1);
  2669. for (j=0 : j<GetKeyBufLength(buffer2) : i++,j++) {
  2670. buffer->i = buffer2->(j+WORDSIZE);
  2671. SetKeyBufLength(GetKeyBufLength()+1);
  2672. if (i-WORDSIZE == INPUT_BUFFER_LEN-1) break;
  2673. }
  2674. ! (3) we fill up the buffer with spaces, which is unnecessary, but may
  2675. ! help incorrectly-written interpreters to cope.
  2676. #Ifdef TARGET_ZCODE;
  2677. for (: i<INPUT_BUFFER_LEN : i++) buffer->i = ' ';
  2678. #Endif; ! TARGET_ZCODE
  2679. return REPARSE_CODE;
  2680. ]; ! end of NounDomain
  2681. [ AskPlayer context i j k l first_word answer_words marker;
  2682. ! Now we print up the question, using the equivalence classes as worked
  2683. ! out by Adjudicate() so as not to repeat ourselves on plural objects...
  2684. asking_player = true;
  2685. if (context == CREATURE_TOKEN) L__M(##Miscellany, 45);
  2686. else L__M(##Miscellany, 46);
  2687. j = number_of_classes; marker = 0;
  2688. for (i=1 : i<=number_of_classes : i++) {
  2689. while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++;
  2690. k = match_list-->marker;
  2691. if (match_classes-->marker > 0) print (the) k; else print (a) k;
  2692. if (i < j-1) print (string) COMMA__TX;
  2693. if (i == j-1) print (SerialComma) j, (string) OR__TX;
  2694. }
  2695. L__M(##Miscellany, 57);
  2696. ! ...and get an answer:
  2697. .WhichOne;
  2698. #Ifdef TARGET_ZCODE;
  2699. for (i=WORDSIZE : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
  2700. #Endif; ! TARGET_ZCODE
  2701. answer_words = Keyboard(buffer2, parse2);
  2702. first_word = WordValue(1, parse2);
  2703. asking_player = false;
  2704. ! Take care of "all", because that does something too clever here to do
  2705. ! later on:
  2706. if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
  2707. if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
  2708. l = multiple_object-->0;
  2709. for (i=0 : i<number_matched && l+i<63 : i++) {
  2710. k = match_list-->i;
  2711. multiple_object-->(i+1+l) = k;
  2712. }
  2713. multiple_object-->0 = i+l;
  2714. rtrue;
  2715. }
  2716. L__M(##Miscellany, 47);
  2717. jump WhichOne;
  2718. }
  2719. ! If the first word of the reply can be interpreted as a verb, then
  2720. ! assume that the player has ignored the question and given a new
  2721. ! command altogether.
  2722. ! (This is one time when it's convenient that the directions are
  2723. ! not themselves verbs - thus, "north" as a reply to "Which, the north
  2724. ! or south door" is not treated as a fresh command but as an answer.)
  2725. #Ifdef LanguageIsVerb;
  2726. if (first_word == 0) {
  2727. j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
  2728. }
  2729. #Endif; ! LanguageIsVerb
  2730. if (first_word) {
  2731. if (((first_word->#dict_par1) & DICT_VERB) && ~~LanguageVerbMayBeName(first_word)) {
  2732. CopyBuffer(buffer, buffer2);
  2733. return REPARSE_CODE;
  2734. }
  2735. if (NumberWords(parse2) > 2) {
  2736. j = WordValue(2, parse2);
  2737. k = WordValue(3, parse2);
  2738. if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) {
  2739. CopyBuffer(buffer, buffer2);
  2740. return REPARSE_CODE;
  2741. }
  2742. }
  2743. }
  2744. ! Now we insert the answer into the original typed command, as
  2745. ! words additionally describing the same object
  2746. ! (eg, > take red button
  2747. ! Which one, ...
  2748. ! > music
  2749. ! becomes "take music red button". The parser will thus have three
  2750. ! words to work from next time, not two.)
  2751. k = WordAddress(match_from) - buffer;
  2752. l = GetKeyBufLength(buffer2) +1;
  2753. for (j=buffer + INPUT_BUFFER_LEN - 1 : j>=buffer+k+l : j--) j->0 = j->(-l);
  2754. for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(WORDSIZE+i);
  2755. buffer->(k+l-1) = ' ';
  2756. SetKeyBufLength(GetKeyBufLength() + l);
  2757. ! Having reconstructed the input, we warn the parser accordingly
  2758. ! and get out.
  2759. return REPARSE_CODE;
  2760. ];
  2761. ! ----------------------------------------------------------------------------
  2762. ! The Adjudicate routine tries to see if there is an obvious choice, when
  2763. ! faced with a list of objects (the match_list) each of which matches the
  2764. ! player's specification equally well.
  2765. !
  2766. ! To do this it makes use of the context (the token type being worked on).
  2767. ! It counts up the number of obvious choices for the given context
  2768. ! (all to do with where a candidate is, except for 6 (animate) which is to
  2769. ! do with whether it is animate or not);
  2770. !
  2771. ! if only one obvious choice is found, that is returned;
  2772. !
  2773. ! if we are in indefinite mode (don't care which) one of the obvious choices
  2774. ! is returned, or if there is no obvious choice then an unobvious one is
  2775. ! made;
  2776. !
  2777. ! at this stage, we work out whether the objects are distinguishable from
  2778. ! each other or not: if they are all indistinguishable from each other,
  2779. ! then choose one, it doesn't matter which;
  2780. !
  2781. ! otherwise, 0 (meaning, unable to decide) is returned (but remember that
  2782. ! the equivalence classes we've just worked out will be needed by other
  2783. ! routines to clear up this mess, so we can't economise on working them
  2784. ! out).
  2785. !
  2786. ! Returns -1 if an error occurred
  2787. ! ----------------------------------------------------------------------------
  2788. Constant SCORE__CHOOSEOBJ = 1000;
  2789. Constant SCORE__IFGOOD = 500;
  2790. Constant SCORE__UNCONCEALED = 100;
  2791. Constant SCORE__BESTLOC = 60;
  2792. Constant SCORE__NEXTBESTLOC = 40;
  2793. Constant SCORE__NOTCOMPASS = 20;
  2794. Constant SCORE__NOTSCENERY = 10;
  2795. Constant SCORE__NOTACTOR = 5;
  2796. Constant SCORE__GNA = 1;
  2797. Constant SCORE__DIVISOR = 20;
  2798. [ Adjudicate context i j k good_flag good_ones last n flag offset sovert;
  2799. #Ifdef DEBUG;
  2800. if (parser_trace >= 4) {
  2801. print " [Adjudicating match list of size ", number_matched, " in context ", context, "]^";
  2802. print " ";
  2803. if (indef_mode) {
  2804. print "indefinite type: ";
  2805. if (indef_type & OTHER_BIT) print "other ";
  2806. if (indef_type & MY_BIT) print "my ";
  2807. if (indef_type & THAT_BIT) print "that ";
  2808. if (indef_type & PLURAL_BIT) print "plural ";
  2809. if (indef_type & LIT_BIT) print "lit ";
  2810. if (indef_type & UNLIT_BIT) print "unlit ";
  2811. if (indef_owner ~= 0) print "owner:", (name) indef_owner;
  2812. new_line;
  2813. print " number wanted: ";
  2814. if (indef_wanted == 100) print "all"; else print indef_wanted;
  2815. new_line;
  2816. print " most likely GNAs of names: ", indef_cases, "^";
  2817. }
  2818. else print "definite object^";
  2819. }
  2820. #Endif; ! DEBUG
  2821. j = number_matched-1; good_ones = 0; last = match_list-->0;
  2822. for (i=0 : i<=j : i++) {
  2823. n = match_list-->i;
  2824. match_scores-->i = 0;
  2825. good_flag = false;
  2826. switch (context) {
  2827. HELD_TOKEN, MULTIHELD_TOKEN:
  2828. if (parent(n) == actor) good_flag = true;
  2829. MULTIEXCEPT_TOKEN:
  2830. if (advance_warning == -1) {
  2831. good_flag = true;
  2832. }
  2833. else {
  2834. if (n ~= advance_warning) good_flag = true;
  2835. }
  2836. MULTIINSIDE_TOKEN:
  2837. if (advance_warning == -1) {
  2838. if (parent(n) ~= actor) good_flag = true;
  2839. }
  2840. else {
  2841. if (n in advance_warning) good_flag = true;
  2842. }
  2843. CREATURE_TOKEN:
  2844. if (CreatureTest(n) == 1) good_flag = true;
  2845. default:
  2846. good_flag = true;
  2847. }
  2848. if (good_flag) {
  2849. match_scores-->i = SCORE__IFGOOD;
  2850. good_ones++; last = n;
  2851. }
  2852. }
  2853. if (good_ones == 1) return last;
  2854. ! If there is ambiguity about what was typed, but it definitely wasn't
  2855. ! animate as required, then return anything; higher up in the parser
  2856. ! a suitable error will be given. (This prevents a question being asked.)
  2857. if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0;
  2858. if (indef_mode == 0) indef_type=0;
  2859. ScoreMatchL(context);
  2860. if (number_matched == 0) return -1;
  2861. if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
  2862. if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
  2863. or MULTIINSIDE_TOKEN) {
  2864. etype = MULTI_PE;
  2865. return -1;
  2866. }
  2867. i = 0; offset = multiple_object-->0; sovert = -1;
  2868. for (j=BestGuess() : j~=-1 && i<indef_wanted && i+offset<63 : j=BestGuess()) {
  2869. flag = 1;
  2870. if (j has concealed && j has worn) flag = 0;
  2871. if (sovert == -1) sovert = bestguess_score/SCORE__DIVISOR;
  2872. else {
  2873. if (indef_wanted == 100 && bestguess_score/SCORE__DIVISOR < sovert)
  2874. flag = 0;
  2875. }
  2876. if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN && parent(j) ~= actor)
  2877. flag = 0;
  2878. #Ifdef TRADITIONAL_TAKE_ALL;
  2879. if (action_to_be == ##Take or ##Remove && parent(j) == actor)
  2880. flag = 0;
  2881. #Ifnot;
  2882. if (action_to_be == ##Take or ##Remove &&
  2883. (j has animate or scenery or static || parent(j) == actor))
  2884. flag = 0;
  2885. #Endif; ! TRADITIONAL_TAKE_ALL
  2886. #Ifdef NO_TAKE_ALL;
  2887. if (take_all_rule == 2 && match_length == 0) flag = 0;
  2888. #Endif; ! NO_TAKE_ALL
  2889. n = ChooseObjects(j, flag);
  2890. if (n == 0) n = LibraryExtensions.RunWhile(ext_chooseobjects, 0, j, flag);
  2891. switch (n) {
  2892. 2: flag = 0; ! forcing rejection
  2893. 1: flag = 1; ! forcing acceptance
  2894. !0: ! going with parser's decision
  2895. }
  2896. if (flag == 1) {
  2897. i++; multiple_object-->(i+offset) = j;
  2898. #Ifdef DEBUG;
  2899. if (parser_trace >= 4) print " Accepting it^";
  2900. #Endif; ! DEBUG
  2901. }
  2902. else {
  2903. i = i;
  2904. #Ifdef DEBUG;
  2905. if (parser_trace >= 4) print " Rejecting it^";
  2906. #Endif; ! DEBUG
  2907. }
  2908. }
  2909. if (i < indef_wanted && indef_wanted < 100) {
  2910. etype = TOOFEW_PE; multi_wanted = indef_wanted;
  2911. multi_had=i;
  2912. return -1;
  2913. }
  2914. multiple_object-->0 = i+offset;
  2915. multi_context = context;
  2916. #Ifdef DEBUG;
  2917. if (parser_trace >= 4)
  2918. print " Made multiple object of size ", i, "]^";
  2919. #Endif; ! DEBUG
  2920. return 1;
  2921. }
  2922. for (i=0 : i<number_matched : i++) match_classes-->i = 0;
  2923. n = 1;
  2924. for (i=0 : i<number_matched : i++)
  2925. if (match_classes-->i == 0) {
  2926. match_classes-->i = n++; flag = 0;
  2927. for (j=i+1 : j<number_matched : j++)
  2928. if (match_classes-->j == 0 && Identical(match_list-->i, match_list-->j) == 1) {
  2929. flag=1;
  2930. match_classes-->j = match_classes-->i;
  2931. }
  2932. if (flag == 1) match_classes-->i = 1-n;
  2933. }
  2934. n--; number_of_classes = n;
  2935. #Ifdef DEBUG;
  2936. if (parser_trace >= 4) {
  2937. print " Grouped into ", n, " possibilities by name:^";
  2938. for (i=0 : i<number_matched : i++)
  2939. if (match_classes-->i > 0)
  2940. print " ", (The) match_list-->i, " (", match_list-->i, ") --- group ",
  2941. match_classes-->i, "^";
  2942. }
  2943. #Endif; ! DEBUG
  2944. if (n == 1) dont_infer = true;
  2945. if (indef_mode == 0) {
  2946. ! Is there now a single highest-scoring object?
  2947. i = SingleBestGuess();
  2948. if (i >= 0) {
  2949. #Ifdef DEBUG;
  2950. if (parser_trace >= 4) print " Single best-scoring object returned.]^";
  2951. #Endif; ! DEBUG
  2952. return i;
  2953. }
  2954. }
  2955. if (indef_mode == 0) {
  2956. if (n > 1) {
  2957. k = -1;
  2958. for (i=0 : i<number_matched : i++) {
  2959. if (match_scores-->i > k) {
  2960. k = match_scores-->i;
  2961. j = match_classes-->i; j = j*j;
  2962. flag = 0;
  2963. }
  2964. else
  2965. if (match_scores-->i == k) {
  2966. if ((match_classes-->i) * (match_classes-->i) ~= j)
  2967. flag = 1;
  2968. }
  2969. }
  2970. if (flag) {
  2971. #Ifdef DEBUG;
  2972. if (parser_trace >= 4) print " Unable to choose best group, so ask player.]^";
  2973. #Endif; ! DEBUG
  2974. return 0;
  2975. }
  2976. #Ifdef DEBUG;
  2977. if (parser_trace >= 4) print " Best choices are all from the same group.^";
  2978. #Endif; ! DEBUG
  2979. }
  2980. }
  2981. ! When the player is really vague, or there's a single collection of
  2982. ! indistinguishable objects to choose from, choose the one the player
  2983. ! most recently acquired, or if the player has none of them, then
  2984. ! the one most recently put where it is.
  2985. return BestGuess();
  2986. ]; ! Adjudicate
  2987. ! ----------------------------------------------------------------------------
  2988. ! ReviseMulti revises the multiple object which already exists, in the
  2989. ! light of information which has come along since then (i.e., the second
  2990. ! parameter). It returns a parser error number, or else 0 if all is well.
  2991. ! This only ever throws things out, never adds new ones.
  2992. ! ----------------------------------------------------------------------------
  2993. [ ReviseMulti second_p i low;
  2994. #Ifdef DEBUG;
  2995. if (parser_trace >= 4) print " Revising multiple object list of size ", multiple_object-->0,
  2996. " with 2nd ", (name) second_p, "^";
  2997. #Endif; ! DEBUG
  2998. if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
  2999. for (i=1,low=0 : i<=multiple_object-->0 : i++) {
  3000. if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) ||
  3001. (multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) {
  3002. low++;
  3003. multiple_object-->low = multiple_object-->i;
  3004. }
  3005. }
  3006. multiple_object-->0 = low;
  3007. }
  3008. if (multi_context == MULTI_TOKEN && action_to_be == ##Take) {
  3009. for (i=1,low=0 : i<=multiple_object-->0 : i++)
  3010. if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor)) low++;
  3011. #Ifdef DEBUG;
  3012. if (parser_trace >= 4) print " Token 2 plural case: number with actor ", low, "^";
  3013. #Endif; ! DEBUG
  3014. if (take_all_rule == 2 || low > 0) {
  3015. for (i=1,low=0 : i<=multiple_object-->0 : i++) {
  3016. if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) {
  3017. low++;
  3018. multiple_object-->low = multiple_object-->i;
  3019. }
  3020. }
  3021. multiple_object-->0 = low;
  3022. }
  3023. }
  3024. i = multiple_object-->0;
  3025. #Ifdef DEBUG;
  3026. if (parser_trace >= 4) print " Done: new size ", i, "^";
  3027. #Endif; ! DEBUG
  3028. if (i == 0) return NOTHING_PE;
  3029. return 0;
  3030. ];
  3031. ! ----------------------------------------------------------------------------
  3032. ! ScoreMatchL scores the match list for quality in terms of what the
  3033. ! player has vaguely asked for. Points are awarded for conforming with
  3034. ! requirements like "my", and so on. Remove from the match list any
  3035. ! entries which fail the basic requirements of the descriptors.
  3036. ! ----------------------------------------------------------------------------
  3037. [ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;
  3038. ! if (indef_type & OTHER_BIT ~= 0) threshold++;
  3039. if (indef_type & MY_BIT ~= 0) threshold++;
  3040. if (indef_type & THAT_BIT ~= 0) threshold++;
  3041. if (indef_type & LIT_BIT ~= 0) threshold++;
  3042. if (indef_type & UNLIT_BIT ~= 0) threshold++;
  3043. if (indef_owner ~= nothing) threshold++;
  3044. #Ifdef DEBUG;
  3045. if (parser_trace >= 4) print " Scoring match list: indef mode ", indef_mode, " type ",
  3046. indef_type, ", satisfying ", threshold, " requirements:^";
  3047. #Endif; ! DEBUG
  3048. if (action_to_be ~= ##Take)
  3049. a_s = SCORE__NEXTBESTLOC;
  3050. l_s = SCORE__BESTLOC;
  3051. if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
  3052. a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
  3053. }
  3054. for (i=0 : i<number_matched : i++) {
  3055. obj = match_list-->i; its_owner = parent(obj); its_score=0; met=0;
  3056. ! if (indef_type & OTHER_BIT ~= 0
  3057. ! && obj ~= itobj or himobj or herobj) met++;
  3058. if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++;
  3059. if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++;
  3060. if (indef_type & LIT_BIT ~= 0 && obj has light) met++;
  3061. if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++;
  3062. if (indef_owner ~= 0 && its_owner == indef_owner) met++;
  3063. if (met < threshold) {
  3064. #Ifdef DEBUG;
  3065. if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i, ") in ",
  3066. (the) its_owner, " is rejected (doesn't match descriptors)^";
  3067. #Endif; ! DEBUG
  3068. match_list-->i = -1;
  3069. }
  3070. else {
  3071. its_score = 0;
  3072. if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;
  3073. if (its_owner == actor) its_score = its_score + a_s;
  3074. else
  3075. if (its_owner == actors_location) its_score = its_score + l_s;
  3076. else {
  3077. #Ifdef TRADITIONAL_TAKE_ALL;
  3078. if (its_owner ~= compass) its_score = its_score + SCORE__NOTCOMPASS;
  3079. #Ifnot;
  3080. if (its_owner ~= compass)
  3081. if (take_all_rule && its_owner &&
  3082. its_owner has static or scenery &&
  3083. (its_owner has supporter ||
  3084. (its_owner has container && its_owner has open)))
  3085. its_score = its_score + l_s;
  3086. else
  3087. its_score = its_score + SCORE__NOTCOMPASS;
  3088. #Endif; ! TRADITIONAL_TAKE_ALL
  3089. }
  3090. j = ChooseObjects(obj, 2);
  3091. if (j == 0) j = LibraryExtensions.RunAll(ext_chooseobjects, obj, 2);
  3092. its_score = its_score + SCORE__CHOOSEOBJ * j;
  3093. if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
  3094. if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;
  3095. ! A small bonus for having the correct GNA,
  3096. ! for sorting out ambiguous articles and the like.
  3097. if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
  3098. its_score = its_score + SCORE__GNA;
  3099. match_scores-->i = match_scores-->i + its_score;
  3100. #Ifdef DEBUG;
  3101. if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i,
  3102. ") in ", (the) its_owner, " : ", match_scores-->i, " points^";
  3103. #Endif; ! DEBUG
  3104. }
  3105. }
  3106. for (i=0 : i<number_matched : i++) {
  3107. while (match_list-->i == -1) {
  3108. if (i == number_matched-1) { number_matched--; break; }
  3109. for (j=i : j<number_matched-1 : j++) {
  3110. match_list-->j = match_list-->(j+1);
  3111. match_scores-->j = match_scores-->(j+1);
  3112. }
  3113. number_matched--;
  3114. }
  3115. }
  3116. ];
  3117. ! ----------------------------------------------------------------------------
  3118. ! BestGuess makes the best guess it can out of the match list, assuming that
  3119. ! everything in the match list is textually as good as everything else;
  3120. ! however it ignores items marked as -1, and so marks anything it chooses.
  3121. ! It returns -1 if there are no possible choices.
  3122. ! ----------------------------------------------------------------------------
  3123. [ BestGuess earliest its_score best i;
  3124. earliest = 0; best = -1;
  3125. for (i=0 : i<number_matched : i++) {
  3126. if (match_list-->i >= 0) {
  3127. its_score = match_scores-->i;
  3128. if (its_score > best) { best = its_score; earliest = i; }
  3129. }
  3130. }
  3131. #Ifdef DEBUG;
  3132. if (parser_trace >= 4)
  3133. if (best < 0) print " Best guess ran out of choices^";
  3134. else print " Best guess ", (the) match_list-->earliest, " (", match_list-->earliest, ")^";
  3135. #Endif; ! DEBUG
  3136. if (best < 0) return -1;
  3137. i = match_list-->earliest;
  3138. match_list-->earliest = -1;
  3139. bestguess_score = best;
  3140. return i;
  3141. ];
  3142. ! ----------------------------------------------------------------------------
  3143. ! SingleBestGuess returns the highest-scoring object in the match list
  3144. ! if it is the clear winner, or returns -1 if there is no clear winner
  3145. ! ----------------------------------------------------------------------------
  3146. [ SingleBestGuess earliest its_score best i;
  3147. earliest = -1; best = -1000;
  3148. for (i=0 : i<number_matched : i++) {
  3149. its_score = match_scores-->i;
  3150. if (its_score == best) earliest = -1;
  3151. if (its_score > best) { best = its_score; earliest = match_list-->i; }
  3152. }
  3153. bestguess_score = best;
  3154. return earliest;
  3155. ];
  3156. ! ----------------------------------------------------------------------------
  3157. ! Identical decides whether or not two objects can be distinguished from
  3158. ! each other by anything the player can type. If not, it returns true.
  3159. ! ----------------------------------------------------------------------------
  3160. [ Identical o1 o2 p1 p2 n1 n2 i j flag;
  3161. if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side
  3162. if (o1 == 0 || o2 == 0) rfalse; ! Similarly
  3163. if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time
  3164. ! What complicates things is that o1 or o2 might have a parsing routine,
  3165. ! so the parser can't know from here whether they are or aren't the same.
  3166. ! If they have different parsing routines, we simply assume they're
  3167. ! different. If they have the same routine (which they probably got from
  3168. ! a class definition) then the decision process is as follows:
  3169. !
  3170. ! the routine is called (with self being o1, not that it matters)
  3171. ! with noun and second being set to o1 and o2, and action being set
  3172. ! to the fake action TheSame. If it returns -1, they are found
  3173. ! identical; if -2, different; and if >=0, then the usual method
  3174. ! is used instead.
  3175. if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
  3176. if (o1.parse_name ~= o2.parse_name) rfalse;
  3177. parser_action = ##TheSame; parser_one = o1; parser_two = o2;
  3178. j = wn; i = RunRoutines(o1,parse_name); wn = j;
  3179. if (i == -1) rtrue;
  3180. if (i == -2) rfalse;
  3181. }
  3182. ! This is the default algorithm: do they have the same words in their
  3183. ! "name" (i.e. property no. 1) properties. (Note that the following allows
  3184. ! for repeated words and words in different orders.)
  3185. p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
  3186. p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;
  3187. ! for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
  3188. ! for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;
  3189. for (i=0 : i<n1 : i++) {
  3190. flag = 0;
  3191. for (j=0 : j<n2 : j++)
  3192. if (p1-->i == p2-->j) flag = 1;
  3193. if (flag == 0) rfalse;
  3194. }
  3195. for (j=0 : j<n2 : j++) {
  3196. flag = 0;
  3197. for (i=0 : i<n1 : i++)
  3198. if (p1-->i == p2-->j) flag = 1;
  3199. if (flag == 0) rfalse;
  3200. }
  3201. ! print "Which are identical!^";
  3202. rtrue;
  3203. ];
  3204. ! ----------------------------------------------------------------------------
  3205. ! PrintCommand reconstructs the command as it presently reads, from
  3206. ! the pattern which has been built up
  3207. !
  3208. ! If from is 0, it starts with the verb: then it goes through the pattern.
  3209. ! The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
  3210. ! if 1, it goes up to pcount-1.
  3211. !
  3212. ! Note that verbs and prepositions are printed out of the dictionary:
  3213. ! and that since the dictionary may only preserve the first six characters
  3214. ! of a word (in a V3 game), we have to hand-code the longer words needed.
  3215. !
  3216. ! (Recall that pattern entries are 0 for "multiple object", 1 for "special
  3217. ! word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
  3218. ! preposition n)
  3219. ! ----------------------------------------------------------------------------
  3220. [ PrintCommand from i k spacing_flag;
  3221. #Ifdef LanguageCommand;
  3222. LanguageCommand(from);
  3223. i = k = spacing_flag = 0; ! suppress warning
  3224. #Ifnot;
  3225. if (from == 0) {
  3226. i = verb_word;
  3227. if (LanguageVerb(i) == 0 && PrintVerb(i) == false && LibraryExtensions.RunWhile(ext_printverb, false, i) == 0)
  3228. print (address) i;
  3229. from++; spacing_flag = true;
  3230. }
  3231. for (k=from : k<pcount : k++) {
  3232. i = pattern-->k;
  3233. if (i == PATTERN_NULL) continue;
  3234. if (spacing_flag) print (char) ' ';
  3235. if (i == 0) { print (string) THOSET__TX; jump TokenPrinted; }
  3236. if (i == 1) { print (string) THAT__TX; jump TokenPrinted; }
  3237. if (i >= REPARSE_CODE)
  3238. print (address) No__Dword(i-REPARSE_CODE);
  3239. else
  3240. if (i in compass && LanguageVerbLikesAdverb(verb_word))
  3241. LanguageDirection (i.door_dir); ! the direction name as adverb
  3242. else
  3243. print (the) i;
  3244. .TokenPrinted;
  3245. spacing_flag = true;
  3246. }
  3247. #Endif; ! LanguageCommand
  3248. ];
  3249. ! ----------------------------------------------------------------------------
  3250. ! The CantSee routine returns a good error number for the situation where
  3251. ! the last word looked at didn't seem to refer to any object in context.
  3252. !
  3253. ! The idea is that: if the actor is in a location (but not inside something
  3254. ! like, for instance, a tank which is in that location) then an attempt to
  3255. ! refer to one of the words listed as meaningful-but-irrelevant there
  3256. ! will cause "you don't need to refer to that in this game" rather than
  3257. ! "no such thing" or "what's 'it'?".
  3258. ! (The advantage of not having looked at "irrelevant" local nouns until now
  3259. ! is that it stops them from clogging up the ambiguity-resolving process.
  3260. ! Thus game objects always triumph over scenery.)
  3261. ! ----------------------------------------------------------------------------
  3262. [ CantSee i w e;
  3263. if (scope_token ~= 0) {
  3264. scope_error = scope_token;
  3265. return ASKSCOPE_PE;
  3266. }
  3267. wn--; w = NextWord();
  3268. e = CANTSEE_PE;
  3269. if (w == pronoun_word) {
  3270. pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
  3271. e = ITGONE_PE;
  3272. }
  3273. i = actor; while (parent(i) ~= 0) i = parent(i);
  3274. wn--;
  3275. if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
  3276. else {
  3277. Descriptors(); ! skip past THE etc
  3278. if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
  3279. }
  3280. if (saved_ml)
  3281. saved_oops = num_desc + match_from + saved_ml;
  3282. else
  3283. saved_oops = num_desc + match_from + match_length;
  3284. wn++;
  3285. return e;
  3286. ];
  3287. ! ----------------------------------------------------------------------------
  3288. ! The MultiAdd routine adds object "o" to the multiple-object-list.
  3289. !
  3290. ! This is only allowed to hold 63 objects at most, at which point it ignores
  3291. ! any new entries (and sets a global flag so that a warning may later be
  3292. ! printed if need be).
  3293. ! ----------------------------------------------------------------------------
  3294. [ MultiAdd o i j;
  3295. i = multiple_object-->0;
  3296. if (i == 63) { toomany_flag = 1; rtrue; }
  3297. for (j=1 : j<=i : j++)
  3298. if (o == multiple_object-->j) rtrue;
  3299. i++;
  3300. multiple_object-->i = o;
  3301. multiple_object-->0 = i;
  3302. ];
  3303. ! ----------------------------------------------------------------------------
  3304. ! The MultiSub routine deletes object "o" from the multiple-object-list.
  3305. !
  3306. ! It returns 0 if the object was there in the first place, and 9 (because
  3307. ! this is the appropriate error number in Parser()) if it wasn't.
  3308. ! ----------------------------------------------------------------------------
  3309. [ MultiSub o i j k et;
  3310. i = multiple_object-->0; et = 0;
  3311. for (j=1 : j<=i : j++)
  3312. if (o == multiple_object-->j) {
  3313. for (k=j : k<=i : k++)
  3314. multiple_object-->k = multiple_object-->(k+1);
  3315. multiple_object-->0 = --i;
  3316. return et;
  3317. }
  3318. et = 9; return et;
  3319. ];
  3320. ! ----------------------------------------------------------------------------
  3321. ! The MultiFilter routine goes through the multiple-object-list and throws
  3322. ! out anything without the given attribute "attr" set.
  3323. ! ----------------------------------------------------------------------------
  3324. [ MultiFilter attr i j o;
  3325. .MFiltl;
  3326. i = multiple_object-->0;
  3327. for (j=1 : j<=i : j++) {
  3328. o = multiple_object-->j;
  3329. if (o hasnt attr) {
  3330. MultiSub(o);
  3331. jump Mfiltl;
  3332. }
  3333. }
  3334. ];
  3335. ! ----------------------------------------------------------------------------
  3336. ! The UserFilter routine consults the user's filter (or checks on attribute)
  3337. ! to see what already-accepted nouns are acceptable
  3338. ! ----------------------------------------------------------------------------
  3339. [ UserFilter obj;
  3340. if (token_filter > 0 && token_filter < 49) {
  3341. if (obj has (token_filter-1)) rtrue;
  3342. rfalse;
  3343. }
  3344. noun = obj;
  3345. return token_filter();
  3346. ];
  3347. ! ----------------------------------------------------------------------------
  3348. ! MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
  3349. ! (the main parse buffer)
  3350. ! ----------------------------------------------------------------------------
  3351. #Ifdef TARGET_ZCODE;
  3352. [ MoveWord at1 b2 at2 x y;
  3353. x = at1*2-1; y = at2*2-1;
  3354. parse-->x++ = b2-->y++;
  3355. parse-->x = b2-->y;
  3356. ];
  3357. #Ifnot; ! TARGET_GLULX
  3358. [ MoveWord at1 b2 at2 x y;
  3359. x = at1*3-2; y = at2*3-2;
  3360. parse-->x++ = b2-->y++;
  3361. parse-->x++ = b2-->y++;
  3362. parse-->x = b2-->y;
  3363. ];
  3364. #Endif; ! TARGET_
  3365. ! ----------------------------------------------------------------------------
  3366. ! SearchScope domain1 domain2 context
  3367. !
  3368. ! Works out what objects are in scope (possibly asking an outside routine),
  3369. ! but does not look at anything the player has typed.
  3370. ! ----------------------------------------------------------------------------
  3371. [ SearchScope domain1 domain2 context i is;
  3372. i = 0;
  3373. ! Everything is in scope to the debugging commands
  3374. #Ifdef DEBUG;
  3375. if (scope_reason == PARSING_REASON
  3376. && LanguageVerbIsDebugging(verb_word)) {
  3377. #Ifdef TARGET_ZCODE;
  3378. for (i=selfobj : i<=top_object : i++)
  3379. if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
  3380. PlaceInScope(i);
  3381. #Ifnot; ! TARGET_GLULX
  3382. objectloop (i)
  3383. if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
  3384. PlaceInScope(i);
  3385. #Endif; ! TARGET_
  3386. rtrue;
  3387. }
  3388. #Endif; ! DEBUG
  3389. ! First, a scope token gets priority here:
  3390. if (scope_token ~= 0) {
  3391. scope_stage = 2;
  3392. if (scope_token()) rtrue;
  3393. }
  3394. ! Pick up everything in the location except the actor's possessions;
  3395. ! then go through those. (This ensures the actor's possessions are in
  3396. ! scope even in Darkness.)
  3397. if (context == MULTIINSIDE_TOKEN && advance_warning ~= -1) {
  3398. if (IsSeeThrough(advance_warning) == 1)
  3399. ScopeWithin(advance_warning, 0, context);
  3400. }
  3401. else {
  3402. ! Next, call any user-supplied routine adding things to the scope,
  3403. ! which may circumvent the usual routines altogether
  3404. ! if they return true:
  3405. if (actor == domain1 or domain2) {
  3406. is = InScope(actor);
  3407. if (is == false) is = LibraryExtensions.RunWhile(ext_inscope, false, actor);
  3408. if (is) rtrue;
  3409. }
  3410. if (domain1 ~= 0 && domain1 has supporter or container)
  3411. ScopeWithin_O(domain1, domain1, context);
  3412. ScopeWithin(domain1, domain2, context);
  3413. if (domain2 ~= 0 && domain2 has supporter or container)
  3414. ScopeWithin_O(domain2, domain2, context);
  3415. ScopeWithin(domain2, 0, context);
  3416. }
  3417. ! A special rule applies:
  3418. ! in Darkness as in light, the actor is always in scope to himself.
  3419. if (thedark == domain1 or domain2) {
  3420. ScopeWithin_O(actor, actor, context);
  3421. if (parent(actor) has supporter or container)
  3422. ScopeWithin_O(parent(actor), parent(actor), context);
  3423. }
  3424. ];
  3425. ! ----------------------------------------------------------------------------
  3426. ! IsSeeThrough is used at various places: roughly speaking, it determines
  3427. ! whether o being in scope means that the contents of o are in scope.
  3428. ! ----------------------------------------------------------------------------
  3429. [ IsSeeThrough o;
  3430. if (o has supporter or transparent ||
  3431. (o has container && o has open))
  3432. rtrue;
  3433. rfalse;
  3434. ];
  3435. ! ----------------------------------------------------------------------------
  3436. ! PlaceInScope is provided for routines outside the library, and is not
  3437. ! called within the parser (except for debugging purposes).
  3438. ! ----------------------------------------------------------------------------
  3439. [ PlaceInScope thing;
  3440. if (scope_reason~=PARSING_REASON or TALKING_REASON) {
  3441. DoScopeAction(thing); rtrue;
  3442. }
  3443. wn = match_from; TryGivenObject(thing); placed_in_flag = 1;
  3444. ];
  3445. ! ----------------------------------------------------------------------------
  3446. ! DoScopeAction
  3447. ! ----------------------------------------------------------------------------
  3448. [ DoScopeAction thing s p1;
  3449. s = scope_reason; p1 = parser_one;
  3450. #Ifdef DEBUG;
  3451. if (parser_trace >= 6)
  3452. print "[DSA on ", (the) thing, " with reason = ", scope_reason,
  3453. " p1 = ", parser_one, " p2 = ", parser_two, "]^";
  3454. #Endif; ! DEBUG
  3455. switch (scope_reason) {
  3456. REACT_BEFORE_REASON:
  3457. if (thing.react_before == 0 or NULL) return;
  3458. #Ifdef DEBUG;
  3459. if (parser_trace >= 2)
  3460. print "[Considering react_before for ", (the) thing, "]^";
  3461. #Endif; ! DEBUG
  3462. if (parser_one == 0) parser_one = RunRoutines(thing, react_before);
  3463. REACT_AFTER_REASON:
  3464. if (thing.react_after == 0 or NULL) return;
  3465. #Ifdef DEBUG;
  3466. if (parser_trace >= 2)
  3467. print "[Considering react_after for ", (the) thing, "]^";
  3468. #Endif; ! DEBUG
  3469. if (parser_one == 0) parser_one = RunRoutines(thing, react_after);
  3470. EACH_TURN_REASON:
  3471. if (thing.each_turn == 0) return;
  3472. #Ifdef DEBUG;
  3473. if (parser_trace >= 2)
  3474. print "[Considering each_turn for ", (the) thing, "]^";
  3475. #Endif; ! DEBUG
  3476. PrintOrRun(thing, each_turn);
  3477. TESTSCOPE_REASON:
  3478. if (thing == parser_one) parser_two = 1;
  3479. LOOPOVERSCOPE_REASON:
  3480. parser_one(thing);
  3481. parser_one=p1;
  3482. }
  3483. scope_reason = s;
  3484. ];
  3485. ! ----------------------------------------------------------------------------
  3486. ! ScopeWithin looks for objects in the domain which make textual sense
  3487. ! and puts them in the match list. (However, it does not recurse through
  3488. ! the second argument.)
  3489. ! ----------------------------------------------------------------------------
  3490. [ ScopeWithin domain nosearch context x y;
  3491. if (domain == 0) rtrue;
  3492. ! Special rule: the directions (interpreted as the 12 walls of a room) are
  3493. ! always in context. (So, e.g., "examine north wall" is always legal.)
  3494. ! (Unless we're parsing something like "all", because it would just slow
  3495. ! things down then, or unless the context is "creature".)
  3496. if (indef_mode==0 && domain==actors_location
  3497. && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
  3498. ScopeWithin(compass);
  3499. ! Look through the objects in the domain, avoiding "objectloop" in case
  3500. ! movements occur, e.g. when trying each_turn.
  3501. x = child(domain);
  3502. while (x ~= 0) {
  3503. y = sibling(x);
  3504. ScopeWithin_O(x, nosearch, context);
  3505. x = y;
  3506. }
  3507. ];
  3508. [ ScopeWithin_O domain nosearch context i ad n;
  3509. ! If the scope reason is unusual, don't parse.
  3510. if (scope_reason ~= PARSING_REASON or TALKING_REASON) {
  3511. DoScopeAction(domain);
  3512. jump DontAccept;
  3513. }
  3514. ! "it" or "them" matches to the it-object only. (Note that (1) this means
  3515. ! that "it" will only be understood if the object in question is still
  3516. ! in context, and (2) only one match can ever be made in this case.)
  3517. if (match_from <= num_words) { ! If there's any text to match, that is
  3518. wn = match_from;
  3519. i = NounWord();
  3520. if (i == 1 && player == domain) MakeMatch(domain, 1);
  3521. if (i >= 2 && i < 128 && (LanguagePronouns-->i == domain)) MakeMatch(domain, 1);
  3522. }
  3523. ! Construing the current word as the start of a noun, can it refer to the
  3524. ! object?
  3525. wn = match_from;
  3526. if (TryGivenObject(domain) > 0)
  3527. if (indef_nspec_at > 0 && match_from ~= indef_nspec_at) {
  3528. ! This case arises if the player has typed a number in
  3529. ! which is hypothetically an indefinite descriptor:
  3530. ! e.g. "take two clubs". We have just checked the object
  3531. ! against the word "clubs", in the hope of eventually finding
  3532. ! two such objects. But we also backtrack and check it
  3533. ! against the words "two clubs", in case it turns out to
  3534. ! be the 2 of Clubs from a pack of cards, say. If it does
  3535. ! match against "two clubs", we tear up our original
  3536. ! assumption about the meaning of "two" and lapse back into
  3537. ! definite mode.
  3538. wn = indef_nspec_at;
  3539. if (TryGivenObject(domain) > 0) {
  3540. match_from = indef_nspec_at;
  3541. ResetDescriptors();
  3542. }
  3543. wn = match_from;
  3544. }
  3545. .DontAccept;
  3546. ! Shall we consider the possessions of the current object, as well?
  3547. ! Only if it's a container (so, for instance, if a dwarf carries a
  3548. ! sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
  3549. ! will).
  3550. ! Also, only if there are such possessions.
  3551. !
  3552. ! Notice that the parser can see "into" anything flagged as
  3553. ! transparent - such as a dwarf whose sword you can get at.
  3554. if (child(domain) ~= 0 && domain ~= nosearch && IsSeeThrough(domain) == 1)
  3555. ScopeWithin(domain,nosearch,context);
  3556. ! Drag any extras into context
  3557. ad = domain.&add_to_scope;
  3558. if (ad ~= 0) {
  3559. ! Test if the property value is not an object.
  3560. #Ifdef TARGET_ZCODE;
  3561. i = (UnsignedCompare(ad-->0, top_object) > 0);
  3562. #Ifnot; ! TARGET_GLULX
  3563. i = (((ad-->0)->0) ~= $70);
  3564. #Endif; ! TARGET_
  3565. if (i) {
  3566. ats_flag = 2+context;
  3567. RunRoutines(domain, add_to_scope);
  3568. ats_flag = 0;
  3569. }
  3570. else {
  3571. n = domain.#add_to_scope;
  3572. for (i=0 : (WORDSIZE*i)<n : i++)
  3573. if (ad-->i)
  3574. ScopeWithin_O(ad-->i, 0, context);
  3575. }
  3576. }
  3577. ];
  3578. [ AddToScope obj;
  3579. if (ats_flag >= 2)
  3580. ScopeWithin_O(obj, 0, ats_flag-2);
  3581. if (ats_flag == 1) {
  3582. if (HasLightSource(obj)==1) ats_hls = 1;
  3583. }
  3584. ];
  3585. ! ----------------------------------------------------------------------------
  3586. ! MakeMatch looks at how good a match is. If it's the best so far, then
  3587. ! wipe out all the previous matches and start a new list with this one.
  3588. ! If it's only as good as the best so far, add it to the list.
  3589. ! If it's worse, ignore it altogether.
  3590. !
  3591. ! The idea is that "red panic button" is better than "red button" or "panic".
  3592. !
  3593. ! number_matched (the number of words matched) is set to the current level
  3594. ! of quality.
  3595. !
  3596. ! We never match anything twice, and keep at most 64 equally good items.
  3597. ! ----------------------------------------------------------------------------
  3598. [ MakeMatch obj quality i;
  3599. #Ifdef DEBUG;
  3600. if (parser_trace >= 6) print " Match with quality ",quality,"^";
  3601. #Endif; ! DEBUG
  3602. if (token_filter ~= 0 && UserFilter(obj) == 0) {
  3603. #Ifdef DEBUG;
  3604. if (parser_trace >= 6) print " Match filtered out: token filter ", token_filter, "^";
  3605. #Endif; ! DEBUG
  3606. rtrue;
  3607. }
  3608. if (quality < match_length) rtrue;
  3609. if (quality > match_length) { match_length = quality; number_matched = 0; }
  3610. else {
  3611. if (number_matched >= MATCH_LIST_SIZE) rtrue;
  3612. for (i=0 : i<number_matched : i++)
  3613. if (match_list-->i == obj) rtrue;
  3614. }
  3615. match_list-->number_matched++ = obj;
  3616. #Ifdef DEBUG;
  3617. if (parser_trace >= 6) print " Match added to list^";
  3618. #Endif; ! DEBUG
  3619. ];
  3620. ! ----------------------------------------------------------------------------
  3621. ! TryGivenObject tries to match as many words as possible in what has been
  3622. ! typed to the given object, obj. If it manages any words matched at all,
  3623. ! it calls MakeMatch to say so, then returns the number of words (or 1
  3624. ! if it was a match because of inadequate input).
  3625. ! ----------------------------------------------------------------------------
  3626. [ TryGivenObject obj threshold k w j;
  3627. #Ifdef DEBUG;
  3628. if (parser_trace >= 5) print " Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
  3629. #Endif; ! DEBUG
  3630. dict_flags_of_noun = 0;
  3631. ! If input has run out then always match, with only quality 0 (this saves
  3632. ! time).
  3633. if (wn > num_words) {
  3634. if (indef_mode ~= 0)
  3635. dict_flags_of_noun = DICT_X654; ! Reject "plural" bit
  3636. MakeMatch(obj,0);
  3637. #Ifdef DEBUG;
  3638. if (parser_trace >= 5) print " Matched (0)^";
  3639. #Endif; ! DEBUG
  3640. return 1;
  3641. }
  3642. ! Ask the object to parse itself if necessary, sitting up and taking notice
  3643. ! if it says the plural was used:
  3644. if (obj.parse_name~=0) {
  3645. parser_action = NULL; j=wn;
  3646. k = RunRoutines(obj,parse_name);
  3647. if (k > 0) {
  3648. wn=j+k;
  3649. .MMbyPN;
  3650. if (parser_action == ##PluralFound)
  3651. dict_flags_of_noun = dict_flags_of_noun | DICT_PLUR;
  3652. if (dict_flags_of_noun & DICT_PLUR) {
  3653. if (~~allow_plurals) k = 0;
  3654. else {
  3655. if (indef_mode == 0) {
  3656. indef_mode = 1; indef_type = 0; indef_wanted = 0;
  3657. }
  3658. indef_type = indef_type | PLURAL_BIT;
  3659. if (indef_wanted == 0) indef_wanted = 100;
  3660. }
  3661. }
  3662. #Ifdef DEBUG;
  3663. if (parser_trace >= 5) print " Matched (", k, ")^";
  3664. #Endif; ! DEBUG
  3665. MakeMatch(obj,k);
  3666. return k;
  3667. }
  3668. if (k == 0) jump NoWordsMatch;
  3669. wn = j;
  3670. }
  3671. ! The default algorithm is simply to count up how many words pass the
  3672. ! Refers test:
  3673. parser_action = NULL;
  3674. w = NounWord();
  3675. if (w == 1 && player == obj) { k=1; jump MMbyPN; }
  3676. if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; }
  3677. j = --wn;
  3678. threshold = ParseNoun(obj);
  3679. if (threshold == -1) {
  3680. LibraryExtensions.ext_number_1 = wn; ! Set the "between calls" functionality to
  3681. LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN;
  3682. threshold = LibraryExtensions.RunWhile(ext_parsenoun, -1, obj);
  3683. LibraryExtensions.BetweenCalls = 0; ! Turn off the "between calls" functionality
  3684. }
  3685. #Ifdef DEBUG;
  3686. if (threshold >= 0 && parser_trace >= 5) print " ParseNoun returned ", threshold, "^";
  3687. #Endif; ! DEBUG
  3688. if (threshold < 0) wn++;
  3689. if (threshold > 0) { k = threshold; jump MMbyPN; }
  3690. if (threshold == 0 || Refers(obj,wn-1) == 0) {
  3691. .NoWordsMatch;
  3692. if (indef_mode ~= 0) {
  3693. k = 0; parser_action = NULL;
  3694. jump MMbyPN;
  3695. }
  3696. rfalse;
  3697. }
  3698. if (threshold < 0) {
  3699. threshold = 1;
  3700. dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100;
  3701. w = NextWord();
  3702. while (Refers(obj, wn-1)) {
  3703. threshold++;
  3704. if (w)
  3705. dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & (DICT_X654+DICT_PLUR));
  3706. w = NextWord();
  3707. }
  3708. }
  3709. k = threshold;
  3710. jump MMbyPN;
  3711. ];
  3712. ! ----------------------------------------------------------------------------
  3713. ! Refers works out whether the word at number wnum can refer to the object
  3714. ! obj, returning true or false. The standard method is to see if the
  3715. ! word is listed under "name" for the object, but this is more complex
  3716. ! in languages other than English.
  3717. ! ----------------------------------------------------------------------------
  3718. [ Refers obj wnum wd k l m;
  3719. if (obj == 0) rfalse;
  3720. #Ifdef LanguageRefers;
  3721. k = LanguageRefers(obj,wnum); if (k >= 0) return k;
  3722. #Endif; ! LanguageRefers
  3723. k = wn; wn = wnum; wd = NextWordStopped(); wn = k;
  3724. if (parser_inflection_func) {
  3725. k = parser_inflection(obj, wd);
  3726. if (k >= 0) return k;
  3727. m = -k;
  3728. }
  3729. else
  3730. m = parser_inflection;
  3731. k = obj.&m; l = (obj.#m)/WORDSIZE-1;
  3732. for (m=0 : m<=l : m++)
  3733. if (wd == k-->m) rtrue;
  3734. rfalse;
  3735. ];
  3736. [ WordInProperty wd obj prop k l m;
  3737. k = obj.&prop; l = (obj.#prop)/WORDSIZE-1;
  3738. for (m=0 : m<=l : m++)
  3739. if (wd == k-->m) rtrue;
  3740. rfalse;
  3741. ];
  3742. [ DictionaryLookup b l i;
  3743. for (i=0 : i<l : i++) buffer2->(WORDSIZE+i) = b->i;
  3744. SetKeyBufLength(l, buffer2);
  3745. Tokenise__(buffer2, parse2);
  3746. return parse2-->1;
  3747. ];
  3748. ! ----------------------------------------------------------------------------
  3749. ! NounWord (which takes no arguments) returns:
  3750. !
  3751. ! 0 if the next word is unrecognised or does not carry the "noun" bit in
  3752. ! its dictionary entry,
  3753. ! 1 if a word meaning "me",
  3754. ! the index in the pronoun table (plus 2) of the value field of a pronoun,
  3755. ! if the word is a pronoun,
  3756. ! the address in the dictionary if it is a recognised noun.
  3757. !
  3758. ! The "current word" marker moves on one.
  3759. ! ----------------------------------------------------------------------------
  3760. [ NounWord i j s;
  3761. i = NextWord();
  3762. if (i == 0) rfalse;
  3763. if (i == ME1__WD or ME2__WD or ME3__WD) return 1;
  3764. s = LanguagePronouns-->0;
  3765. for (j=1 : j<=s : j=j+3)
  3766. if (i == LanguagePronouns-->j)
  3767. return j+2;
  3768. if ((i->#dict_par1) & DICT_NOUN == 0) rfalse;
  3769. return i;
  3770. ];
  3771. ! ----------------------------------------------------------------------------
  3772. ! NextWord (which takes no arguments) returns:
  3773. !
  3774. ! 0 if the next word is unrecognised,
  3775. ! comma_word if a comma
  3776. ! THEN1__WD if a full stop
  3777. ! or the dictionary address if it is recognised.
  3778. ! The "current word" marker is moved on.
  3779. !
  3780. ! NextWordStopped does the same, but returns -1 when input has run out
  3781. ! ----------------------------------------------------------------------------
  3782. #Ifdef TARGET_ZCODE;
  3783. [ NextWord i j;
  3784. if (wn > parse->1) { wn++; rfalse; }
  3785. i = wn*2-1; wn++;
  3786. j = parse-->i;
  3787. if (j == ',//') j = comma_word;
  3788. if (j == './/') j = THEN1__WD;
  3789. return j;
  3790. ];
  3791. [ NextWordStopped;
  3792. if (wn > parse->1) { wn++; return -1; }
  3793. return NextWord();
  3794. ];
  3795. [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer
  3796. if (p==0) p=parse;
  3797. if (b==0) b=buffer;
  3798. return b + p->(wordnum*4+1);
  3799. ];
  3800. [ WordLength wordnum p; ! Length of 'wordnum' string in buffer
  3801. if (p==0) p=parse;
  3802. return p->(wordnum*4);
  3803. ];
  3804. [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer
  3805. if (p==0) p=parse;
  3806. return p-->(wordnum*2-1);
  3807. ];
  3808. [ NumberWords p; ! Number of parsed strings in buffer
  3809. if (p==0) p=parse;
  3810. return p->1;
  3811. ];
  3812. [ GetKeyBufLength b; ! Number of typed chars in buffer
  3813. if (b==0) b=buffer;
  3814. return b->1;
  3815. ];
  3816. [ SetKeyBufLength n b; ! Update number of typed chars in buffer
  3817. if (b==0) b=buffer;
  3818. if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE;
  3819. b->1 = n;
  3820. ];
  3821. #Ifnot; ! TARGET_GLULX
  3822. [ NextWord i j;
  3823. if (wn > parse-->0) { wn++; rfalse; }
  3824. i = wn*3-2; wn++;
  3825. j = parse-->i;
  3826. if (j == ',//') j=comma_word;
  3827. if (j == './/') j=THEN1__WD;
  3828. return j;
  3829. ];
  3830. [ NextWordStopped;
  3831. if (wn > parse-->0) {
  3832. wn++;
  3833. return -1;
  3834. }
  3835. return NextWord();
  3836. ];
  3837. [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer
  3838. if (p==0) p=parse;
  3839. if (b==0) b=buffer;
  3840. return b + p-->(wordnum*3);
  3841. ];
  3842. [ WordLength wordnum p; ! Length of 'wordnum' string in buffer
  3843. if (p==0) p=parse;
  3844. return p-->(wordnum*3-1);
  3845. ];
  3846. [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer
  3847. if (p==0) p=parse;
  3848. return p-->(wordnum*3-2);
  3849. ];
  3850. [ NumberWords p; ! Number of parsed strings in buffer
  3851. if (p==0) p=parse;
  3852. return p-->0;
  3853. ];
  3854. [ GetKeyBufLength b; ! Number of typed chars in buffer
  3855. if (b==0) b=buffer;
  3856. return b-->0;
  3857. ];
  3858. [ SetKeyBufLength n b; ! Update number of typed chars in buffer
  3859. if (b==0) b=buffer;
  3860. if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE;
  3861. b-->0 = n;
  3862. ];
  3863. #Endif; ! TARGET_
  3864. ! ----------------------------------------------------------------------------
  3865. ! TryNumber is the only routine which really does any character-level
  3866. ! parsing, since that's normally left to the Z-machine.
  3867. ! It takes word number "wordnum" and tries to parse it as an (unsigned)
  3868. ! decimal number, returning
  3869. !
  3870. ! -1000 if it is not a number
  3871. ! the number if it has between 1 and 4 digits
  3872. ! 10000 if it has 5 or more digits.
  3873. !
  3874. ! (The danger of allowing 5 digits is that Z-machine integers are only
  3875. ! 16 bits long, and anyway this isn't meant to be perfect.)
  3876. !
  3877. ! Using NumberWord, it also catches "one" up to "twenty".
  3878. !
  3879. ! Note that a game can provide a ParseNumber routine which takes priority,
  3880. ! to enable parsing of odder numbers ("x45y12", say).
  3881. ! ----------------------------------------------------------------------------
  3882. [ TryNumber wordnum i j c num len mul tot d digit;
  3883. i = wn; wn = wordnum; j = NextWord(); wn = i;
  3884. j = NumberWord(j);
  3885. if (j >= 1) return j;
  3886. num = WordAddress(wordnum); len = WordLength(wordnum);
  3887. tot = ParseNumber(num, len);
  3888. if (tot == 0) tot = LibraryExtensions.RunWhile(ext_parsenumber, 0, num, len);
  3889. if (tot ~= 0) return tot;
  3890. if (len >= 4) mul=1000;
  3891. if (len == 3) mul=100;
  3892. if (len == 2) mul=10;
  3893. if (len == 1) mul=1;
  3894. tot = 0; c = 0;
  3895. for (c = 0 : c < len : c++) {
  3896. digit=num->c;
  3897. if (digit == '0') { d = 0; jump digok; }
  3898. if (digit == '1') { d = 1; jump digok; }
  3899. if (digit == '2') { d = 2; jump digok; }
  3900. if (digit == '3') { d = 3; jump digok; }
  3901. if (digit == '4') { d = 4; jump digok; }
  3902. if (digit == '5') { d = 5; jump digok; }
  3903. if (digit == '6') { d = 6; jump digok; }
  3904. if (digit == '7') { d = 7; jump digok; }
  3905. if (digit == '8') { d = 8; jump digok; }
  3906. if (digit == '9') { d = 9; jump digok; }
  3907. return -1000;
  3908. .digok;
  3909. tot = tot+mul*d; mul = mul/10;
  3910. }
  3911. if (len > 4) tot=10000;
  3912. return tot;
  3913. ];
  3914. ! ----------------------------------------------------------------------------
  3915. ! AnyNumber is a general parsing routine which accepts binary, hexadecimal
  3916. ! and decimal numbers up to the full Zcode/Glulx ranges.
  3917. ! ----------------------------------------------------------------------------
  3918. #Ifdef TARGET_ZCODE; ! decimal range is -32768 to 32767
  3919. Constant MAX_DECIMAL_SIZE 5;
  3920. Constant MAX_DECIMAL_BASE 3276;
  3921. #Ifnot; ! TARGET_GLULX ! decimal range is -2147483648 to 2147483647
  3922. Constant MAX_DECIMAL_SIZE 10;
  3923. Constant MAX_DECIMAL_BASE 214748364;
  3924. #Endif; ! TARGET_
  3925. [ AnyNumber
  3926. wa we sign base digit digit_count num;
  3927. wa = WordAddress(wn); we = wa + WordLength(wn);
  3928. sign = 1; base = 10;
  3929. if (wa->0 == '-') { sign = -1; wa++; }
  3930. else {
  3931. if (wa->0 == '$') { base = 16; wa++; }
  3932. if (wa->0 == '$') { base = 2; wa++; }
  3933. }
  3934. if (wa >= we) return GPR_FAIL; ! no digits after -/$
  3935. while (wa->0 == '0') wa++; ! skip leading zeros
  3936. for (num=0,digit_count=1 : wa<we : wa++,digit_count++) {
  3937. switch (wa->0) {
  3938. '0' to '9': digit = wa->0 - '0';
  3939. 'A' to 'F': digit = wa->0 - 'A' + 10;
  3940. 'a' to 'f': digit = wa->0 - 'a' + 10;
  3941. default: return GPR_FAIL;
  3942. }
  3943. if (digit >= base) return GPR_FAIL;
  3944. digit_count++;
  3945. switch (base) {
  3946. 16: if (digit_count > 2*WORDSIZE) return GPR_FAIL;
  3947. 2: if (digit_count > 8*WORDSIZE) return GPR_FAIL;
  3948. 10:
  3949. if (digit_count > MAX_DECIMAL_SIZE) return GPR_FAIL;
  3950. if (digit_count == MAX_DECIMAL_SIZE) {
  3951. if (num > MAX_DECIMAL_BASE) return GPR_FAIL;
  3952. if (num == MAX_DECIMAL_BASE) {
  3953. if (sign == 1 && digit > 7) return GPR_FAIL;
  3954. if (sign == -1 && digit > 8) return GPR_FAIL;
  3955. }
  3956. }
  3957. }
  3958. num = base*num + digit;
  3959. }
  3960. parsed_number = num * sign;
  3961. wn++;
  3962. return GPR_NUMBER;
  3963. ];
  3964. ! ----------------------------------------------------------------------------
  3965. ! GetGender returns 0 if the given animate object is female, and 1 if male
  3966. ! (not all games will want such a simple decision function!)
  3967. ! ----------------------------------------------------------------------------
  3968. [ GetGender person;
  3969. if (person hasnt female) rtrue;
  3970. rfalse;
  3971. ];
  3972. [ GetGNAOfObject obj case gender;
  3973. if (obj hasnt animate) case = 6;
  3974. if (obj has male) gender = male;
  3975. if (obj has female) gender = female;
  3976. if (obj has neuter) gender = neuter;
  3977. if (gender == 0) {
  3978. if (case == 0) gender = LanguageAnimateGender;
  3979. else gender = LanguageInanimateGender;
  3980. }
  3981. if (gender == female) case = case + 1;
  3982. if (gender == neuter) case = case + 2;
  3983. if (obj has pluralname) case = case + 3;
  3984. return case;
  3985. ];
  3986. ! ----------------------------------------------------------------------------
  3987. ! Converting between dictionary addresses and entry numbers
  3988. ! ----------------------------------------------------------------------------
  3989. #Ifdef TARGET_ZCODE;
  3990. [ Dword__No w; return (w-(HDR_DICTIONARY-->0 + 7))/9; ];
  3991. [ No__Dword n; return HDR_DICTIONARY-->0 + 7 + 9*n; ];
  3992. #Ifnot; ! TARGET_GLULX
  3993. ! In Glulx, dictionary entries *are* addresses.
  3994. [ Dword__No w; return w; ];
  3995. [ No__Dword n; return n; ];
  3996. #Endif; ! TARGET_
  3997. ! ----------------------------------------------------------------------------
  3998. ! For copying buffers
  3999. ! ----------------------------------------------------------------------------
  4000. #Ifdef TARGET_ZCODE;
  4001. [ CopyBuffer bto bfrom i size;
  4002. size = bto->0;
  4003. for (i=1 : i<=size : i++) bto->i = bfrom->i;
  4004. ];
  4005. #Ifnot; ! TARGET_GLULX
  4006. [ CopyBuffer bto bfrom i;
  4007. for (i=0 : i<INPUT_BUFFER_LEN : i++) bto->i = bfrom->i;
  4008. ];
  4009. #Endif; ! TARGET_
  4010. ! ----------------------------------------------------------------------------
  4011. ! Provided for use by language definition files
  4012. ! ----------------------------------------------------------------------------
  4013. #Ifdef TARGET_ZCODE;
  4014. [ LTI_Insert i ch b y;
  4015. ! Protect us from strict mode, as this isn't an array in quite the
  4016. ! sense it expects
  4017. b = buffer;
  4018. ! Insert character ch into buffer at point i.
  4019. ! Being careful not to let the buffer possibly overflow:
  4020. y = b->1;
  4021. if (y > b->0) y = b->0;
  4022. ! Move the subsequent text along one character:
  4023. for (y=y+2 : y>i : y--) b->y = b->(y-1);
  4024. b->i = ch;
  4025. ! And the text is now one character longer:
  4026. if (b->1 < b->0) (b->1)++;
  4027. ];
  4028. #Ifnot; ! TARGET_GLULX
  4029. [ LTI_Insert i ch b y;
  4030. ! Protect us from strict mode, as this isn't an array in quite the
  4031. ! sense it expects
  4032. b = buffer;
  4033. ! Insert character ch into buffer at point i.
  4034. ! Being careful not to let the buffer possibly overflow:
  4035. y = b-->0;
  4036. if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN;
  4037. ! Move the subsequent text along one character:
  4038. for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1);
  4039. b->i = ch;
  4040. ! And the text is now one character longer:
  4041. if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++;
  4042. ];
  4043. #Endif; ! TARGET_
  4044. ! ============================================================================
  4045. [ PronounsSub x y c d;
  4046. L__M(##Pronouns, 1);
  4047. c = (LanguagePronouns-->0)/3;
  4048. if (player ~= selfobj) c++;
  4049. if (c == 0) return L__M(##Pronouns, 4);
  4050. for (x=1,d=0 : x<=LanguagePronouns-->0 : x=x+3) {
  4051. print "~", (address) LanguagePronouns-->x, "~ ";
  4052. y = LanguagePronouns-->(x+2);
  4053. if (y == NULL) L__M(##Pronouns, 3);
  4054. else {
  4055. L__M(##Pronouns, 2);
  4056. print (the) y;
  4057. }
  4058. d++;
  4059. if (d < c-1) print (string) COMMA__TX;
  4060. if (d == c-1) print (SerialComma) c, (string) AND__TX;
  4061. }
  4062. if (player ~= selfobj) {
  4063. print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2);
  4064. c = player; player = selfobj;
  4065. print (the) c; player = c;
  4066. }
  4067. L__M(##Pronouns, 5);
  4068. ];
  4069. [ SetPronoun dword value x;
  4070. for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
  4071. if (LanguagePronouns-->x == dword) {
  4072. LanguagePronouns-->(x+2) = value; return;
  4073. }
  4074. RunTimeError(12);
  4075. ];
  4076. [ PronounValue dword x;
  4077. for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
  4078. if (LanguagePronouns-->x == dword)
  4079. return LanguagePronouns-->(x+2);
  4080. return 0;
  4081. ];
  4082. [ ResetVagueWords obj; PronounNotice(obj); ];
  4083. #Ifdef EnglishNaturalLanguage;
  4084. [ PronounOldEnglish;
  4085. if (itobj ~= old_itobj) SetPronoun('it', itobj);
  4086. if (himobj ~= old_himobj) SetPronoun('him', himobj);
  4087. if (herobj ~= old_herobj) SetPronoun('her', herobj);
  4088. old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
  4089. ];
  4090. #Endif; !EnglishNaturalLanguage
  4091. [ PronounNotice obj x bm;
  4092. if (obj == player) return;
  4093. #Ifdef EnglishNaturalLanguage;
  4094. PronounOldEnglish();
  4095. #Endif; ! EnglishNaturalLanguage
  4096. bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
  4097. for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
  4098. if (bm & (LanguagePronouns-->(x+1)) ~= 0)
  4099. LanguagePronouns-->(x+2) = obj;
  4100. #Ifdef EnglishNaturalLanguage;
  4101. itobj = PronounValue('it'); old_itobj = itobj;
  4102. himobj = PronounValue('him'); old_himobj = himobj;
  4103. herobj = PronounValue('her'); old_herobj = herobj;
  4104. #Endif; ! EnglishNaturalLanguage
  4105. ];
  4106. ! ============================================================================
  4107. ! End of the parser proper: the remaining routines are its front end.
  4108. ! ----------------------------------------------------------------------------
  4109. Object InformLibrary "(Inform Library)"
  4110. with play [ i j k l;
  4111. #Ifdef TARGET_ZCODE;
  4112. ZZInitialise();
  4113. #Ifnot; ! TARGET_GLULX
  4114. GGInitialise();
  4115. #Endif; ! TARGET_
  4116. GamePrologue();
  4117. while (~~deadflag) { ! everything happens in this loop
  4118. #Ifdef EnglishNaturalLanguage;
  4119. PronounOldEnglish();
  4120. old_itobj = PronounValue('it');
  4121. old_himobj = PronounValue('him');
  4122. old_herobj = PronounValue('her');
  4123. #Endif; ! EnglishNaturalLanguage
  4124. .very__late__error;
  4125. if (score ~= last_score) {
  4126. if (notify_mode == 1) NotifyTheScore();
  4127. last_score = score;
  4128. }
  4129. .late__error;
  4130. inputobjs-->0 = 0; inputobjs-->1 = 0;
  4131. inputobjs-->2 = 0; inputobjs-->3 = 0; meta = false;
  4132. ! The Parser writes its results into inputobjs and meta,
  4133. ! a flag indicating a "meta-verb". This can only be set for
  4134. ! commands by the player, not for orders to others.
  4135. InformParser.parse_input(inputobjs);
  4136. action = inputobjs-->0;
  4137. ! --------------------------------------------------------------
  4138. ! Reverse "give fred biscuit" into "give biscuit to fred"
  4139. if (action == ##GiveR or ##ShowR) {
  4140. i = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = i;
  4141. if (action == ##GiveR) action = ##Give; else action = ##Show;
  4142. }
  4143. ! Convert "P, tell me about X" to "ask P about X"
  4144. if (action == ##Tell && inputobjs-->2 == player && actor ~= player) {
  4145. inputobjs-->2 = actor; actor = player; action = ##Ask;
  4146. }
  4147. ! Convert "ask P for X" to "P, give X to me"
  4148. if (action == ##AskFor && inputobjs-->2 ~= player && actor == player) {
  4149. actor = inputobjs-->2; inputobjs-->2 = inputobjs-->3;
  4150. inputobjs-->3 = player; action = ##Give;
  4151. }
  4152. ! For old, obsolete code: special_word contains the topic word
  4153. ! in conversation
  4154. if (action == ##Ask or ##Tell or ##Answer)
  4155. special_word = special_number1;
  4156. ! --------------------------------------------------------------
  4157. multiflag = false; onotheld_mode = notheld_mode; notheld_mode = false;
  4158. ! For implicit taking and multiple object detection
  4159. .begin__action;
  4160. inp1 = 0; inp2 = 0; i = inputobjs-->1;
  4161. if (i >= 1) inp1 = inputobjs-->2;
  4162. if (i >= 2) inp2 = inputobjs-->3;
  4163. ! inp1 and inp2 hold: object numbers, or 0 for "multiple object",
  4164. ! or 1 for "a number or dictionary address"
  4165. if (inp1 == 1) noun = special_number1; else noun = inp1;
  4166. if (inp2 == 1) {
  4167. if (inp1 == 1) second = special_number2;
  4168. else second = special_number1;
  4169. }
  4170. else second = inp2;
  4171. ! --------------------------------------------------------------
  4172. ! Generate the action...
  4173. if ((i == 0) ||
  4174. (i == 1 && inp1 ~= 0) ||
  4175. (i == 2 && inp1 ~= 0 && inp2 ~= 0)) {
  4176. if (self.actor_act(actor, action, noun, second) == ACTOR_ACT_ABORT_NOTUNDERSTOOD)
  4177. jump begin__action;
  4178. jump turn__end;
  4179. }
  4180. ! ...unless a multiple object must be substituted. First:
  4181. ! (a) check the multiple list isn't empty;
  4182. ! (b) warn the player if it has been cut short because too long;
  4183. ! (c) generate a sequence of actions from the list
  4184. ! (stopping in the event of death or movement away).
  4185. multiflag = true;
  4186. j = multiple_object-->0;
  4187. if (j == 0) {
  4188. L__M(##Miscellany, 2);
  4189. jump late__error;
  4190. }
  4191. if (toomany_flag) {
  4192. toomany_flag = false;
  4193. L__M(##Miscellany, 1);
  4194. }
  4195. i = location;
  4196. for (k=1 : k<=j : k++) {
  4197. if (deadflag) break;
  4198. if (location ~= i) {
  4199. L__M(##Miscellany, 51);
  4200. break;
  4201. }
  4202. l = multiple_object-->k;
  4203. PronounNotice(l);
  4204. print (name) l, (string) COLON__TX;
  4205. if (inp1 == 0) {
  4206. inp1 = l;
  4207. switch (self.actor_act(actor, action, l, second)) {
  4208. ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action;
  4209. ACTOR_ACT_ABORT_ORDER: jump turn__end;
  4210. }
  4211. inp1 = 0;
  4212. }
  4213. else {
  4214. inp2 = l;
  4215. if (self.actor_act(actor, action, noun, l) == ACTOR_ACT_ABORT_NOTUNDERSTOOD)
  4216. jump begin__action;
  4217. inp2 = 0;
  4218. }
  4219. }
  4220. ! --------------------------------------------------------------
  4221. .turn__end;
  4222. ! No time passes if either (i) the verb was meta, or
  4223. ! (ii) we've only had the implicit take before the "real"
  4224. ! action to follow.
  4225. if (notheld_mode == 1) { NoteObjectAcquisitions(); continue; }
  4226. if (meta) continue;
  4227. if (~~deadflag) self.end_turn_sequence();
  4228. else if (START_MOVE ~= 1) turns++;
  4229. } ! end of while()
  4230. if (deadflag ~= 2 && AfterLife() == false)
  4231. LibraryExtensions.RunAll(ext_afterlife);
  4232. if (deadflag == 0) jump very__late__error;
  4233. GameEpilogue();
  4234. ], ! end of 'play' property
  4235. end_turn_sequence [;
  4236. AdvanceWorldClock(); if (deadflag) return;
  4237. RunTimersAndDaemons(); if (deadflag) return;
  4238. RunEachTurnProperties(); if (deadflag) return;
  4239. if (TimePasses() == 0) LibraryExtensions.RunAll(ext_timepasses);
  4240. if (deadflag) return;
  4241. AdjustLight(); if (deadflag) return;
  4242. NoteObjectAcquisitions();
  4243. ],
  4244. actor_act [ p a n s j sp sa sn ss;
  4245. sp = actor; actor = p;
  4246. if (p ~= player) {
  4247. ! The player's "orders" property can refuse to allow
  4248. ! conversation here, by returning true. If not, the order is
  4249. ! sent to the other person's "orders" property. If that also
  4250. ! returns false, then: if it was a misunderstood command
  4251. ! anyway, it is converted to an Answer action (thus
  4252. ! "floyd, grrr" ends up as "say grrr to floyd"). If it was a
  4253. ! good command, it is finally offered to the Order: part of
  4254. ! the other person's "life" property, the old-fashioned
  4255. ! way of dealing with conversation.
  4256. sa = action; sn = noun; ss = second;
  4257. action = a; noun = n; second = s;
  4258. j = RunRoutines(player, orders);
  4259. if (j == 0) {
  4260. j = RunRoutines(actor, orders);
  4261. if (j == 0) {
  4262. if (action == ##NotUnderstood) {
  4263. inputobjs-->3 = actor; actor = player; action = ##Answer;
  4264. return 1; ! abort, not resetting action globals
  4265. }
  4266. if (RunLife(actor, ##Order) == 0) {
  4267. L__M(##Order, 1, actor);
  4268. return 2;
  4269. }
  4270. }
  4271. }
  4272. action = sa; noun = sn; second = ss;
  4273. }
  4274. else
  4275. self.begin_action(a, n, s, 0);
  4276. actor = sp;
  4277. ],
  4278. begin_action [ a n s source sa sn ss;
  4279. sa = action; sn = noun; ss = second;
  4280. action = a; noun = n; second = s;
  4281. #Ifdef DEBUG;
  4282. if (debug_flag & DEBUG_ACTIONS) TraceAction(source);
  4283. #Ifnot;
  4284. source = 0;
  4285. #Endif; ! DEBUG
  4286. #Iftrue (Grammar__Version == 1);
  4287. if ((meta || BeforeRoutines() == false) && action < 256) {
  4288. #Ifdef INFIX;
  4289. if (infix_verb) {
  4290. if (BeforeRoutines() == false)
  4291. ActionPrimitive();
  4292. } else ActionPrimitive();
  4293. #Ifnot;
  4294. ActionPrimitive();
  4295. #Endif;
  4296. }
  4297. #Ifnot;
  4298. if ((meta || BeforeRoutines() == false) && action < 4096) {
  4299. #Ifdef INFIX;
  4300. if (infix_verb) {
  4301. if (BeforeRoutines() == false)
  4302. ActionPrimitive();
  4303. } else ActionPrimitive();
  4304. #Ifnot;
  4305. ActionPrimitive();
  4306. #Endif;
  4307. }
  4308. #Endif; ! Grammar__Version
  4309. action = sa; noun = sn; second = ss;
  4310. ],
  4311. has proper;
  4312. ! ----------------------------------------------------------------------------
  4313. ! Routines called before and after main 'play' loop
  4314. [ GamePrologue i j;
  4315. before_first_turn = true;
  4316. for (i=1 : i<=100 : i++) j = random(i);
  4317. ChangeDefault(cant_go, CANTGO__TX);
  4318. real_location = thedark;
  4319. player = selfobj; actor = player;
  4320. selfobj.capacity = MAX_CARRIED; ! ### change?
  4321. #Ifdef LanguageInitialise;
  4322. LanguageInitialise();
  4323. #Endif; ! LanguageInitialise
  4324. #Ifdef EnglishNaturalLanguage;
  4325. old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
  4326. #Endif;! EnglishNaturalLanguage
  4327. new_line;
  4328. LibraryExtensions.RunAll(ext_initialise);
  4329. j = Initialise();
  4330. last_score = score;
  4331. initial_lookmode = lookmode;
  4332. objectloop (i in player) give i moved ~concealed;
  4333. move player to location;
  4334. while (parent(location)) location = parent(location);
  4335. real_location = location;
  4336. MoveFloatingObjects();
  4337. actor = player; ! resync, because player may have been changed in Initialise()
  4338. actors_location = location;
  4339. lightflag = OffersLight(parent(player));
  4340. if (lightflag == 0) location = thedark;
  4341. if (j ~= 2) Banner();
  4342. #ifndef NOINITIAL_LOOK;
  4343. <Look>;
  4344. #endif;
  4345. before_first_turn = false;
  4346. ];
  4347. [ GameEpilogue;
  4348. print "^^ ";
  4349. #Ifdef TARGET_ZCODE;
  4350. #IfV5; style bold; #Endif; ! V5
  4351. #Ifnot; ! TARGET_GLULX
  4352. glk($0086, 5); ! set alert style
  4353. #Endif; ! TARGET_
  4354. print "***";
  4355. switch (deadflag) {
  4356. 1: L__M(##Miscellany, 3);
  4357. 2: L__M(##Miscellany, 4);
  4358. default: print " ";
  4359. if (DeathMessage() == false)
  4360. LibraryExtensions.RunAll(ext_deathmessage);
  4361. print " ";
  4362. }
  4363. print "***";
  4364. #Ifdef TARGET_ZCODE;
  4365. #IfV5; style roman; #Endif; ! V5
  4366. #Ifnot; ! TARGET_GLULX
  4367. glk($0086, 0); ! set normal style
  4368. #Endif; ! TARGET_
  4369. print "^^";
  4370. #Ifndef NO_SCORE;
  4371. print "^";
  4372. #Endif; ! NO_SCORE
  4373. Epilogue();
  4374. ScoreSub();
  4375. DisplayStatus();
  4376. AfterGameOver();
  4377. ];
  4378. ! ----------------------------------------------------------------------------
  4379. ! Routines called at end of each turn
  4380. [ AdvanceWorldClock;
  4381. turns++;
  4382. if (the_time ~= NULL) {
  4383. if (time_rate >= 0) the_time=the_time+time_rate;
  4384. else {
  4385. time_step--;
  4386. if (time_step == 0) {
  4387. the_time++;
  4388. time_step = -time_rate;
  4389. }
  4390. }
  4391. the_time = the_time % 1440;
  4392. }
  4393. ];
  4394. [ RunTimersAndDaemons i j;
  4395. #Ifdef DEBUG;
  4396. if (debug_flag & DEBUG_TIMERS) {
  4397. for (i=0 : i<active_timers : i++) {
  4398. j = the_timers-->i;
  4399. if (j ~= 0) {
  4400. print (name) (j&~WORD_HIGHBIT), ": ";
  4401. if (j & WORD_HIGHBIT) print "daemon";
  4402. else
  4403. print "timer with ", j.time_left, " turns to go";
  4404. new_line;
  4405. }
  4406. }
  4407. }
  4408. #Endif; ! DEBUG
  4409. for (i=0 : i<active_timers : i++) {
  4410. if (deadflag) return;
  4411. j = the_timers-->i;
  4412. if (j ~= 0) {
  4413. if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT, daemon);
  4414. else {
  4415. if (j.time_left == 0) {
  4416. StopTimer(j);
  4417. RunRoutines(j, time_out);
  4418. }
  4419. else
  4420. j.time_left = j.time_left-1;
  4421. }
  4422. }
  4423. }
  4424. ];
  4425. [ RunEachTurnProperties;
  4426. scope_reason = EACH_TURN_REASON; verb_word = 0;
  4427. DoScopeAction(location);
  4428. SearchScope(ScopeCeiling(player), player, 0);
  4429. scope_reason = PARSING_REASON;
  4430. ];
  4431. ! ----------------------------------------------------------------------------
  4432. #Ifdef TARGET_ZCODE;
  4433. [ ActionPrimitive; (#actions_table-->action)(); ];
  4434. #Ifnot; ! TARGET_GLULX
  4435. [ ActionPrimitive; (#actions_table-->(action+1))(); ];
  4436. #Endif; ! TARGET_
  4437. [ AfterGameOver i amus_ret;
  4438. .RRQPL;
  4439. L__M(##Miscellany,5);
  4440. .RRQL;
  4441. L__M(##Prompt);
  4442. #Ifdef TARGET_ZCODE;
  4443. #IfV3; read buffer parse; #Endif; ! V3
  4444. temp_global=0;
  4445. #IfV5; read buffer parse DrawStatusLine; #Endif; ! V5
  4446. #Ifnot; ! TARGET_GLULX
  4447. KeyboardPrimitive(buffer, parse);
  4448. #Endif; ! TARGET_
  4449. i = parse-->1;
  4450. if (i == QUIT1__WD or QUIT2__WD) {
  4451. #Ifdef TARGET_ZCODE;
  4452. quit;
  4453. #Ifnot; ! TARGET_GLULX
  4454. quit;
  4455. #Endif; ! TARGET_
  4456. }
  4457. if (i == RESTART__WD) {
  4458. #Ifdef TARGET_ZCODE;
  4459. @restart;
  4460. #Ifnot; ! TARGET_GLULX
  4461. @restart;
  4462. #Endif; ! TARGET_
  4463. }
  4464. if (i == RESTORE__WD) {
  4465. RestoreSub();
  4466. jump RRQPL;
  4467. }
  4468. if (i == FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0) {
  4469. new_line; FullScoreSub();
  4470. jump RRQPL;
  4471. }
  4472. if (deadflag == 2 && i == AMUSING__WD) {
  4473. amus_ret = false;
  4474. if (AMUSING_PROVIDED == 0) {
  4475. new_line;
  4476. amus_ret = Amusing();
  4477. }
  4478. if (amus_ret == false) LibraryExtensions.RunAll(ext_amusing);
  4479. jump RRQPL;
  4480. }
  4481. #IfV5;
  4482. if (i == UNDO1__WD or UNDO2__WD or UNDO3__WD) {
  4483. i = PerformUndo();
  4484. if (i == 0) jump RRQPL;
  4485. }
  4486. #Endif; ! V5
  4487. L__M(##Miscellany, 8);
  4488. jump RRQL;
  4489. ];
  4490. [ NoteObjectAcquisitions i;
  4491. objectloop (i in player)
  4492. if (i hasnt moved) {
  4493. give i moved;
  4494. if (i has scored) {
  4495. score = score + OBJECT_SCORE;
  4496. things_score = things_score + OBJECT_SCORE;
  4497. }
  4498. }
  4499. ];
  4500. ! ----------------------------------------------------------------------------
  4501. ! R_Process is invoked by the <...> and <<...>> statements, whose syntax is:
  4502. ! <action [noun] [second]> ! traditional
  4503. ! <action [noun] [second], actor> ! introduced at compiler 6.33
  4504. [ R_Process a n s p
  4505. s1 s2 s3;
  4506. s1 = inp1; s2 = inp2; s3 = actor;
  4507. inp1 = n; inp2 = s; if (p) actor = p; else actor = player;
  4508. InformLibrary.begin_action(a, n, s, 1);
  4509. inp1 = s1; inp2 = s2; actor = s3;
  4510. ];
  4511. ! ----------------------------------------------------------------------------
  4512. [ TestScope obj act a al sr x y;
  4513. x = parser_one; y = parser_two;
  4514. parser_one = obj; parser_two = 0; a = actor; al = actors_location;
  4515. sr = scope_reason; scope_reason = TESTSCOPE_REASON;
  4516. if (act == 0) actor = player; else actor = act;
  4517. actors_location = ScopeCeiling(actor);
  4518. SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a;
  4519. actors_location = al; parser_one = x; x = parser_two; parser_two = y;
  4520. return x;
  4521. ];
  4522. [ LoopOverScope routine act x y a al;
  4523. x = parser_one; y = scope_reason; a = actor; al = actors_location;
  4524. parser_one = routine;
  4525. if (act == 0) actor = player; else actor = act;
  4526. actors_location = ScopeCeiling(actor);
  4527. scope_reason = LOOPOVERSCOPE_REASON;
  4528. SearchScope(actors_location, actor, 0);
  4529. parser_one = x; scope_reason = y; actor = a; actors_location = al;
  4530. ];
  4531. [ BeforeRoutines rv;
  4532. if (GamePreRoutine()) rtrue;
  4533. if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepreroutine, 0);
  4534. if (rv) rtrue;
  4535. if (RunRoutines(player, orders)) rtrue;
  4536. scope_reason = REACT_BEFORE_REASON; parser_one = 0;
  4537. SearchScope(ScopeCeiling(player), player, 0);
  4538. scope_reason = PARSING_REASON;
  4539. if (parser_one) rtrue;
  4540. if (location && RunRoutines(location, before)) rtrue;
  4541. if (inp1 > 1 && RunRoutines(inp1, before)) rtrue;
  4542. rfalse;
  4543. ];
  4544. [ AfterRoutines rv;
  4545. scope_reason = REACT_AFTER_REASON; parser_one = 0;
  4546. SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON;
  4547. if (parser_one) rtrue;
  4548. if (location && RunRoutines(location, after)) rtrue;
  4549. if (inp1 > 1 && RunRoutines(inp1, after)) rtrue;
  4550. rv = GamePostRoutine();
  4551. if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepostroutine, false);
  4552. return rv;
  4553. ];
  4554. [ RunLife a j;
  4555. #Ifdef DEBUG;
  4556. if (debug_flag & DEBUG_ACTIONS) TraceAction(2, j);
  4557. #Endif; ! DEBUG
  4558. reason_code = j; return RunRoutines(a,life);
  4559. ];
  4560. [ ZRegion addr;
  4561. switch (metaclass(addr)) { ! Left over from Inform 5
  4562. nothing: return 0;
  4563. Object, Class: return 1;
  4564. Routine: return 2;
  4565. String: return 3;
  4566. }
  4567. ];
  4568. [ PrintOrRun obj prop flag;
  4569. if (obj.#prop > WORDSIZE) return RunRoutines(obj, prop);
  4570. if (obj.prop == NULL) rfalse;
  4571. switch (metaclass(obj.prop)) {
  4572. Class, Object, nothing:
  4573. return RunTimeError(2,obj,prop);
  4574. String:
  4575. print (string) obj.prop;
  4576. if (flag == 0) new_line;
  4577. rtrue;
  4578. Routine:
  4579. return RunRoutines(obj, prop);
  4580. }
  4581. ];
  4582. [ PrintOrRunVar var flag;
  4583. switch (metaclass(var)) {
  4584. Object:
  4585. print (name) var;
  4586. String:
  4587. print (string) var;
  4588. if (flag == 0) new_line;
  4589. Routine:
  4590. return var();
  4591. default:
  4592. print (char) '(', var, (char) ')';
  4593. }
  4594. rtrue;
  4595. ];
  4596. [ ValueOrRun obj prop;
  4597. !### this is entirely unlikely to work. Does anyone care? (AP)
  4598. ! Well, it's certainly used three times in verblibm.h (RF)
  4599. ! Update: now not used (RF)
  4600. if (obj.prop < 256) return obj.prop;
  4601. return RunRoutines(obj, prop);
  4602. ];
  4603. [ RunRoutines obj prop;
  4604. if (obj == thedark && prop ~= initial or short_name or description) obj = real_location;
  4605. if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse;
  4606. return obj.prop();
  4607. ];
  4608. #Ifdef TARGET_ZCODE;
  4609. [ ChangeDefault prop val a b;
  4610. ! Use assembly-language here because -S compilation won't allow this:
  4611. @loadw 0 5 -> a;
  4612. b = prop-1;
  4613. @storew a b val;
  4614. ];
  4615. #Ifnot; ! TARGET_GLULX
  4616. [ ChangeDefault prop val;
  4617. ! Use assembly-language here because -S compilation won't allow this:
  4618. ! #cpv__start-->prop = val;
  4619. @astore #cpv__start prop val;
  4620. ];
  4621. #Endif; ! TARGET_
  4622. ! ----------------------------------------------------------------------------
  4623. [ StartTimer obj timer i;
  4624. for (i=0 : i<active_timers : i++)
  4625. if (the_timers-->i == obj) rfalse;
  4626. for (i=0 : i<active_timers : i++)
  4627. if (the_timers-->i == 0) jump FoundTSlot;
  4628. i = active_timers++;
  4629. if (i >= MAX_TIMERS) { RunTimeError(4); return; }
  4630. .FoundTSlot;
  4631. if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; }
  4632. the_timers-->i = obj; obj.time_left = timer;
  4633. ];
  4634. [ StopTimer obj i;
  4635. for (i=0 : i<active_timers : i++)
  4636. if (the_timers-->i == obj) jump FoundTSlot2;
  4637. rfalse;
  4638. .FoundTSlot2;
  4639. if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; }
  4640. the_timers-->i = 0; obj.time_left = 0;
  4641. ];
  4642. [ StartDaemon obj i;
  4643. for (i=0 : i<active_timers : i++)
  4644. if (the_timers-->i == WORD_HIGHBIT + obj) rfalse;
  4645. for (i=0 : i<active_timers : i++)
  4646. if (the_timers-->i == 0) jump FoundTSlot3;
  4647. i = active_timers++;
  4648. if (i >= MAX_TIMERS) RunTimeError(4);
  4649. .FoundTSlot3;
  4650. the_timers-->i = WORD_HIGHBIT + obj;
  4651. ];
  4652. [ StopDaemon obj i;
  4653. for (i=0 : i<active_timers : i++)
  4654. if (the_timers-->i == WORD_HIGHBIT + obj) jump FoundTSlot4;
  4655. rfalse;
  4656. .FoundTSlot4;
  4657. the_timers-->i = 0;
  4658. ];
  4659. ! ----------------------------------------------------------------------------
  4660. [ DisplayStatus;
  4661. if (sys_statusline_flag == 0) { sline1 = score; sline2 = turns; }
  4662. else { sline1 = the_time/60; sline2 = the_time%60;}
  4663. ];
  4664. [ SetTime t s;
  4665. the_time = t; time_rate = s; time_step = 0;
  4666. if (s < 0) time_step = 0-s;
  4667. ];
  4668. [ NotifyTheScore;
  4669. #Ifdef TARGET_GLULX;
  4670. glk($0086, 6); ! set note style
  4671. #Endif; ! TARGET_GLULX
  4672. print "^["; L__M(##Miscellany, 50, score-last_score); print ".]^";
  4673. #Ifdef TARGET_GLULX;
  4674. glk($0086, 0); ! set normal style
  4675. #Endif; ! TARGET_GLULX
  4676. ];
  4677. ! ----------------------------------------------------------------------------
  4678. [ AdjustLight flag i;
  4679. i = lightflag;
  4680. lightflag = OffersLight(parent(player));
  4681. if (i == 0 && lightflag == 1) {
  4682. location = real_location;
  4683. if (flag == 0) <Look>;
  4684. }
  4685. if (i == 1 && lightflag == 0) {
  4686. real_location = location; location = thedark;
  4687. if (flag == 0) {
  4688. NoteArrival();
  4689. return L__M(##Miscellany, 9);
  4690. }
  4691. }
  4692. if (i == 0 && lightflag == 0) location = thedark;
  4693. ];
  4694. [ OffersLight i j;
  4695. if (i == 0) rfalse;
  4696. if (i has light) rtrue;
  4697. objectloop (j in i)
  4698. if (HasLightSource(j) == 1) rtrue;
  4699. if (i has container) {
  4700. if (i has open || i has transparent)
  4701. return OffersLight(parent(i));
  4702. }
  4703. else {
  4704. if (i has enterable || i has transparent || i has supporter)
  4705. return OffersLight(parent(i));
  4706. }
  4707. rfalse;
  4708. ];
  4709. [ HidesLightSource obj;
  4710. if (obj == player) rfalse;
  4711. if (obj has transparent or supporter) rfalse;
  4712. if (obj has container) return (obj hasnt open);
  4713. return (obj hasnt enterable);
  4714. ];
  4715. [ HasLightSource i j ad;
  4716. if (i == 0) rfalse;
  4717. if (i has light) rtrue;
  4718. if (i has enterable || IsSeeThrough(i) == 1)
  4719. if (~~(HidesLightSource(i)))
  4720. objectloop (j in i)
  4721. if (HasLightSource(j) == 1) rtrue;
  4722. ad = i.&add_to_scope;
  4723. if (parent(i) ~= 0 && ad ~= 0) {
  4724. if (metaclass(ad-->0) == Routine) {
  4725. ats_hls = 0; ats_flag = 1;
  4726. RunRoutines(i, add_to_scope);
  4727. ats_flag = 0; if (ats_hls == 1) rtrue;
  4728. }
  4729. else {
  4730. for (j=0 : (WORDSIZE*j)<i.#add_to_scope : j++)
  4731. if (HasLightSource(ad-->j) == 1) rtrue;
  4732. }
  4733. }
  4734. rfalse;
  4735. ];
  4736. [ ChangePlayer obj flag i;
  4737. ! if (obj.&number == 0) return RunTimeError(7, obj);
  4738. if (obj == nothing) obj = selfobj;
  4739. if (actor == player) actor=obj;
  4740. give player ~transparent ~concealed;
  4741. i = obj; while (parent(i) ~= 0) {
  4742. if (i has animate) give i transparent;
  4743. i = parent(i);
  4744. }
  4745. if (player == selfobj && player provides nameless && player.nameless == true) {
  4746. if (player provides narrative_voice) {
  4747. if (player.narrative_voice == 1) {
  4748. player.short_name = MYFORMER__TX;
  4749. (player.&name)-->0 = 'my';
  4750. (player.&name)-->1 = 'former';
  4751. (player.&name)-->2 = 'self';
  4752. } else if (player.narrative_voice == 2) {
  4753. player.short_name = FORMER__TX;
  4754. (player.&name)-->0 = 'my';
  4755. (player.&name)-->1 = 'former';
  4756. (player.&name)-->2 = 'self';
  4757. }
  4758. }
  4759. }
  4760. player = obj;
  4761. give player transparent concealed animate;
  4762. i = player; while (parent(i) ~= 0) i = parent(i);
  4763. location = i; real_location = location;
  4764. if (parent(player) == 0) return RunTimeError(10);
  4765. MoveFloatingObjects();
  4766. lightflag = OffersLight(parent(player));
  4767. if (lightflag == 0) location = thedark;
  4768. print_player_flag = flag;
  4769. ];
  4770. ! ----------------------------------------------------------------------------
  4771. #Ifdef DEBUG;
  4772. #Ifdef TARGET_ZCODE;
  4773. [ DebugParameter w;
  4774. print w;
  4775. if (w >= 1 && w <= top_object) print " (", (name) w, ")";
  4776. if (UnsignedCompare(w, dict_start) >= 0 &&
  4777. UnsignedCompare(w, dict_end) < 0 &&
  4778. (w - dict_start) % dict_entry_size == 0)
  4779. print " ('", (address) w, "')";
  4780. ];
  4781. [ DebugAction a anames;
  4782. #Iftrue (Grammar__Version == 1);
  4783. if (a >= 256) { print "<fake action ", a-256, ">"; return; }
  4784. #Ifnot;
  4785. if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
  4786. #Endif; ! Grammar__Version
  4787. anames = #identifiers_table;
  4788. anames = anames + 2*(anames-->0) + 2*48;
  4789. print (string) anames-->a;
  4790. ];
  4791. [ DebugAttribute a anames;
  4792. if (a < 0 || a >= 48) print "<invalid attribute ", a, ">";
  4793. else {
  4794. anames = #identifiers_table; anames = anames + 2*(anames-->0);
  4795. print (string) anames-->a;
  4796. }
  4797. ];
  4798. #Ifnot; ! TARGET_GLULX
  4799. [ DebugParameter w endmem;
  4800. print w;
  4801. @getmemsize endmem;
  4802. if (w >= 1 && w < endmem) {
  4803. if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")";
  4804. if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')";
  4805. }
  4806. ];
  4807. [ DebugAction a str;
  4808. if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
  4809. if (a < 0 || a >= #identifiers_table-->7) print "<invalid action ", a, ">";
  4810. else {
  4811. str = #identifiers_table-->6;
  4812. str = str-->a;
  4813. if (str) print (string) str; else print "<unnamed action ", a, ">";
  4814. }
  4815. ];
  4816. [ DebugAttribute a str;
  4817. if (a < 0 || a >= NUM_ATTR_BYTES*8) print "<invalid attribute ", a, ">";
  4818. else {
  4819. str = #identifiers_table-->4;
  4820. str = str-->a;
  4821. if (str) print (string) str; else print "<unnamed attribute ", a, ">";
  4822. }
  4823. ];
  4824. #Endif; ! TARGET_
  4825. [ TraceAction source ar;
  4826. if (source < 2) print "[ Action ", (DebugAction) action;
  4827. else {
  4828. if (ar == ##Order)
  4829. print "[ Order to ", (name) actor, ": ", (DebugAction) action;
  4830. else
  4831. print "[ Life rule ", (DebugAction) ar;
  4832. }
  4833. if (noun ~= 0) print " with noun ", (DebugParameter) noun;
  4834. if (second ~= 0) print " and second ", (DebugParameter) second;
  4835. if (source == 0) print " ";
  4836. if (source == 1) print " (from < > statement) ";
  4837. print "]^";
  4838. ];
  4839. [ DebugToken token;
  4840. AnalyseToken(token);
  4841. switch (found_ttype) {
  4842. ILLEGAL_TT:
  4843. print "<illegal token number ", token, ">";
  4844. ELEMENTARY_TT:
  4845. switch (found_tdata) {
  4846. NOUN_TOKEN: print "noun";
  4847. HELD_TOKEN: print "held";
  4848. MULTI_TOKEN: print "multi";
  4849. MULTIHELD_TOKEN: print "multiheld";
  4850. MULTIEXCEPT_TOKEN: print "multiexcept";
  4851. MULTIINSIDE_TOKEN: print "multiinside";
  4852. CREATURE_TOKEN: print "creature";
  4853. SPECIAL_TOKEN: print "special";
  4854. NUMBER_TOKEN: print "number";
  4855. TOPIC_TOKEN: print "topic";
  4856. ENDIT_TOKEN: print "END";
  4857. }
  4858. PREPOSITION_TT:
  4859. print "'", (address) found_tdata, "'";
  4860. ROUTINE_FILTER_TT:
  4861. #Ifdef INFIX;
  4862. print "noun=", (InfixPrintPA) found_tdata;
  4863. #Ifnot;
  4864. print "noun=Routine(", found_tdata, ")";
  4865. #Endif; ! INFIX
  4866. ATTR_FILTER_TT:
  4867. print (DebugAttribute) found_tdata;
  4868. SCOPE_TT:
  4869. #Ifdef INFIX;
  4870. print "scope=", (InfixPrintPA) found_tdata;
  4871. #Ifnot;
  4872. print "scope=Routine(", found_tdata, ")";
  4873. #Endif; ! INFIX
  4874. GPR_TT:
  4875. #Ifdef INFIX;
  4876. print (InfixPrintPA) found_tdata;
  4877. #Ifnot;
  4878. print "Routine(", found_tdata, ")";
  4879. #Endif; ! INFIX
  4880. }
  4881. ];
  4882. [ DebugGrammarLine pcount;
  4883. print " * ";
  4884. for (: line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
  4885. if ((line_token-->pcount)->0 & $10) print "/ ";
  4886. print (DebugToken) line_token-->pcount, " ";
  4887. }
  4888. print "-> ", (DebugAction) action_to_be;
  4889. if (action_reversed) print " reverse";
  4890. ];
  4891. #Ifdef TARGET_ZCODE;
  4892. [ ShowVerbSub grammar lines j;
  4893. if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0)
  4894. "Try typing ~showverb~ and then the name of a verb.";
  4895. print "Verb";
  4896. if ((noun->#dict_par1) & DICT_META) print " meta";
  4897. for (j=dict_start : j<dict_end : j=j+dict_entry_size)
  4898. if (j->#dict_par2 == noun->#dict_par2)
  4899. print " '", (address) j, "'";
  4900. new_line;
  4901. grammar = (HDR_STATICMEMORY-->0)-->($ff-(noun->#dict_par2));
  4902. lines = grammar->0;
  4903. grammar++;
  4904. if (lines == 0) "has no grammar lines.";
  4905. for (: lines>0 : lines--) {
  4906. grammar = UnpackGrammarLine(grammar);
  4907. print " "; DebugGrammarLine(); new_line;
  4908. }
  4909. ];
  4910. #Ifnot; ! TARGET_GLULX
  4911. [ ShowVerbSub grammar lines j wd dictlen entrylen;
  4912. if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0)
  4913. "Try typing ~showverb~ and then the name of a verb.";
  4914. print "Verb";
  4915. if ((noun->#dict_par1) & DICT_META) print " meta";
  4916. dictlen = #dictionary_table-->0;
  4917. entrylen = DICT_WORD_SIZE + 7;
  4918. for (j=0 : j<dictlen : j++) {
  4919. wd = #dictionary_table + WORDSIZE + entrylen*j;
  4920. if (wd->#dict_par2 == noun->#dict_par2)
  4921. print " '", (address) wd, "'";
  4922. }
  4923. new_line;
  4924. grammar = (#grammar_table)-->($ff-(noun->#dict_par2)+1);
  4925. lines = grammar->0;
  4926. grammar++;
  4927. if (lines == 0) "has no grammar lines.";
  4928. for (: lines>0 : lines--) {
  4929. grammar = UnpackGrammarLine(grammar);
  4930. print " "; DebugGrammarLine(); new_line;
  4931. }
  4932. ];
  4933. #Endif; ! TARGET_
  4934. [ ShowObjSub c f l a n x numattr;
  4935. if (noun == 0) noun = location;
  4936. objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
  4937. if (f == 1) print (name) l, " ~"; else print "Object ~";
  4938. print (name) noun, "~ (", noun, ")";
  4939. if (parent(noun)) print " in ~", (name) parent(noun), "~ (", parent(noun), ")";
  4940. new_line;
  4941. if (f > 1) {
  4942. print " class ";
  4943. objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
  4944. new_line;
  4945. }
  4946. #Ifdef TARGET_ZCODE;
  4947. numattr = 48;
  4948. #Ifnot; ! TARGET_GLULX
  4949. numattr = NUM_ATTR_BYTES * 8;
  4950. #Endif; ! TARGET_
  4951. for (a=0,f=0 : a<numattr : a++) if (noun has a) f=1;
  4952. if (f) {
  4953. print " has ";
  4954. for (a=0 : a<numattr : a++) if (noun has a) print (DebugAttribute) a, " ";
  4955. new_line;
  4956. }
  4957. if (noun ofclass Class) return;
  4958. f=0;
  4959. #Ifdef TARGET_ZCODE;
  4960. l = #identifiers_table-->0;
  4961. #Ifnot; ! TARGET_GLULX
  4962. l = INDIV_PROP_START + #identifiers_table-->3;
  4963. #Endif; ! TARGET_
  4964. for (a=1 : a<=l : a++) {
  4965. if ((a ~= 2 or 3) && noun.&a) {
  4966. if (f == 0) { print " with "; f=1; }
  4967. print (property) a;
  4968. n = noun.#a;
  4969. for (c=0 : WORDSIZE*c<n : c++) {
  4970. print " ";
  4971. x = (noun.&a)-->c;
  4972. if (a == name) print "'", (address) x, "'";
  4973. else {
  4974. if (a == number or capacity or time_left) print x;
  4975. else {
  4976. switch (x) {
  4977. NULL: print "NULL";
  4978. 0: print "0";
  4979. 1: print "1";
  4980. default:
  4981. switch (metaclass(x)) {
  4982. Class, Object:
  4983. print (name) x;
  4984. String:
  4985. print "~", (string) x, "~";
  4986. Routine:
  4987. print "[...]";
  4988. }
  4989. print " (", x, ")";
  4990. }
  4991. }
  4992. }
  4993. }
  4994. print ",^ ";
  4995. }
  4996. }
  4997. ! if (f==1) new_line;
  4998. ];
  4999. [ ShowDictSub_helper x; print (address) x; ];
  5000. [ ShowDictSub
  5001. dp el ne f x y z;
  5002. #Ifdef TARGET_ZCODE;
  5003. dp = HDR_DICTIONARY-->0; ! start of dictionary
  5004. dp = dp + dp->0 + 1; ! skip over word-separators table
  5005. el = dp->0; dp = dp + 1; ! entry length
  5006. ne = dp-->0; dp = dp + WORDSIZE; ! number of entries
  5007. #Ifnot; ! TARGET_GLULX;
  5008. dp = #dictionary_table; ! start of dictionary
  5009. el = DICT_WORD_SIZE + 7; ! entry length
  5010. ne = dp-->0; dp = dp + WORDSIZE; ! number of entries
  5011. #Endif; ! TARGET_
  5012. ! dp now at first entry
  5013. wn = 2; x = NextWordStopped();
  5014. switch (x) {
  5015. 0:
  5016. "That word isn't in the dictionary.";
  5017. -1:
  5018. ; ! show all entries
  5019. THEN1__WD:
  5020. dp = './/'; ne = 1; ! show '.'
  5021. COMMA_WORD:
  5022. dp = ',//'; ne = 1; ! show ','
  5023. default:
  5024. dp = x; ne = 1; f = true; ! show specified entry, plus associated objects
  5025. }
  5026. for ( : ne-- : dp=dp+el) {
  5027. print (address) dp, " --> ";
  5028. x = dp->#dict_par1; ! flag bits
  5029. y = PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, ShowDictSub_helper, dp) + WORDSIZE;
  5030. for (z=WORDSIZE : z<y : z++) {
  5031. !if (x & DICT_NOUN) StorageForShortName->z = UpperCase(StorageForShortName->z);
  5032. if (y > WORDSIZE+1 && StorageForShortName->z == ' ' or '.' or ',') x = x | $8000;
  5033. print (char) StorageForShortName->z;
  5034. }
  5035. print " --> ";
  5036. if (x == 0)
  5037. print " no flags";
  5038. else {
  5039. !if (x & $0040) print " BIT_6";
  5040. !if (x & $0020) print " BIT_5";
  5041. !if (x & $0010) print " BIT_4";
  5042. if (x & $8000) print " UNTYPEABLE";
  5043. if (x & DICT_NOUN) print " noun";
  5044. if (x & DICT_PLUR) print "+plural";
  5045. if (x & DICT_VERB) print " verb";
  5046. if (x & DICT_META) print "+meta";
  5047. if (x & DICT_PREP) print " preposition";
  5048. if (f && (x & DICT_NOUN)) {
  5049. print " --> refers to these objects:";
  5050. objectloop (x)
  5051. if (WordInProperty(dp, x, name)) print "^ ", (name) x, " (", x, ")";
  5052. }
  5053. }
  5054. new_line;
  5055. }
  5056. ];
  5057. #Endif; ! DEBUG
  5058. ! ----------------------------------------------------------------------------
  5059. ! Miscellaneous display routines used by DrawStatusLine and available for
  5060. ! user. Most of these vary according to which machine is being compiled to
  5061. ! ----------------------------------------------------------------------------
  5062. #Ifdef TARGET_ZCODE;
  5063. [ ClearScreen window;
  5064. switch (window) {
  5065. WIN_ALL: @erase_window -1;
  5066. WIN_STATUS: @erase_window 1;
  5067. WIN_MAIN: @erase_window 0;
  5068. }
  5069. ];
  5070. #Iftrue (#version_number == 6);
  5071. [ MoveCursorV6 line column charw; ! 1-based postion on text grid
  5072. @get_wind_prop 1 13 -> charw; ! font size
  5073. charw = charw & $FF;
  5074. line = 1 + charw*(line-1);
  5075. column = 1 + charw*(column-1);
  5076. @set_cursor line column;
  5077. ];
  5078. #Endif;
  5079. #Ifndef MoveCursor;
  5080. [ MoveCursor line column; ! 1-based postion on text grid
  5081. if (~~statuswin_current) {
  5082. @set_window 1;
  5083. #Ifdef COLOUR;
  5084. if (clr_on && clr_bgstatus > 1)
  5085. @set_colour clr_fgstatus clr_bgstatus;
  5086. else
  5087. #Endif; ! COLOUR
  5088. style reverse;
  5089. }
  5090. if (line == 0) { line = 1; column = 1; }
  5091. #Iftrue (#version_number == 6);
  5092. MoveCursorV6(line, column);
  5093. #Ifnot;
  5094. @set_cursor line column;
  5095. #Endif;
  5096. statuswin_current = true;
  5097. ];
  5098. #Endif;
  5099. [ MainWindow;
  5100. if (statuswin_current) {
  5101. #Ifdef COLOUR;
  5102. if (clr_on && clr_bgstatus > 1) @set_colour clr_fg clr_bg;
  5103. else
  5104. #Endif; ! COLOUR
  5105. style roman;
  5106. @set_window 0;
  5107. }
  5108. statuswin_current = false;
  5109. ];
  5110. #Iftrue (#version_number == 6);
  5111. [ ScreenWidth width charw;
  5112. @get_wind_prop 1 3 -> width;
  5113. @get_wind_prop 1 13 -> charw;
  5114. charw = charw & $FF;
  5115. if (charw == 0) return width;
  5116. return (width+charw-1) / charw;
  5117. ];
  5118. #Ifnot;
  5119. [ ScreenWidth;
  5120. return (HDR_SCREENWCHARS->0);
  5121. ];
  5122. #Endif;
  5123. [ ScreenHeight;
  5124. return (HDR_SCREENHLINES->0);
  5125. ];
  5126. #Iftrue (#version_number == 6);
  5127. [ StatusLineHeight height wx wy x y charh;
  5128. ! Split the window. Standard 1.0 interpreters should keep the window 0
  5129. ! cursor in the same absolute position, but older interpreters,
  5130. ! including Infocom's don't - they keep the window 0 cursor in the
  5131. ! same position relative to its origin. We therefore compensate
  5132. ! manually.
  5133. @get_wind_prop 0 0 -> wy; @get_wind_prop 0 1 -> wx;
  5134. @get_wind_prop 0 13 -> charh; @log_shift charh $FFF8 -> charh;
  5135. @get_wind_prop 0 4 -> y; @get_wind_prop 0 5 -> x;
  5136. height = height * charh;
  5137. @split_window height;
  5138. y = y - height + wy - 1;
  5139. if (y < 1) y = 1;
  5140. x = x + wx - 1;
  5141. @set_cursor y x 0;
  5142. gg_statuswin_cursize = height;
  5143. ];
  5144. #Ifnot;
  5145. [ StatusLineHeight height;
  5146. if (gg_statuswin_cursize ~= height)
  5147. @split_window height;
  5148. gg_statuswin_cursize = height;
  5149. ];
  5150. #Endif;
  5151. #Ifdef COLOUR;
  5152. [ SetColour f b window;
  5153. if (window == 0) { ! if setting both together, set reverse
  5154. clr_fgstatus = b;
  5155. clr_bgstatus = f;
  5156. }
  5157. if (window == 1) {
  5158. clr_fgstatus = f;
  5159. clr_bgstatus = b;
  5160. }
  5161. if (window == 0 or 2) {
  5162. clr_fg = f;
  5163. clr_bg = b;
  5164. }
  5165. if (clr_on) {
  5166. if (statuswin_current)
  5167. @set_colour clr_fgstatus clr_bgstatus;
  5168. else
  5169. @set_colour clr_fg clr_bg;
  5170. }
  5171. ];
  5172. #Endif; ! COLOUR
  5173. #Ifnot; ! TARGET_GLULX
  5174. [ ClearScreen window;
  5175. if (window == WIN_ALL or WIN_MAIN) {
  5176. glk($002A, gg_mainwin);
  5177. if (gg_quotewin) {
  5178. glk($0024, gg_quotewin, 0); ! close_window
  5179. gg_quotewin = 0;
  5180. }
  5181. }
  5182. if (gg_statuswin && window == WIN_ALL or WIN_STATUS) glk($002A, gg_statuswin);
  5183. ];
  5184. [ MoveCursor line column; ! 0-based postion on text grid
  5185. if (gg_statuswin) {
  5186. glk($002F, gg_statuswin); ! set_window
  5187. }
  5188. if (line == 0) { line = 1; column = 1; }
  5189. glk($002B, gg_statuswin, column-1, line-1); ! window_move_cursor
  5190. statuswin_current=1;
  5191. ];
  5192. [ MainWindow;
  5193. glk($002F, gg_mainwin); ! set_window
  5194. statuswin_current=0;
  5195. ];
  5196. [ MakeColourWord c;
  5197. if (c > 9) return c;
  5198. c = c-2;
  5199. return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0);
  5200. ];
  5201. [ ScreenWidth id;
  5202. id=gg_mainwin;
  5203. if (gg_statuswin && statuswin_current) id = gg_statuswin;
  5204. glk($0025, id, gg_arguments, 0); ! window_get_size
  5205. return gg_arguments-->0;
  5206. ];
  5207. [ ScreenHeight;
  5208. glk($0025, gg_mainwin, 0, gg_arguments); ! window_get_size
  5209. return gg_arguments-->0;
  5210. ];
  5211. #Ifdef COLOUR;
  5212. [ SetColour f b window doclear i fwd bwd swin;
  5213. if (window) swin = 5-window; ! 4 for TextGrid, 3 for TextBuffer
  5214. if (clr_on) {
  5215. fwd = MakeColourWord(f);
  5216. bwd = MakeColourWord(b);
  5217. for (i=0 : i<=10: i++) {
  5218. if (f == CLR_DEFAULT || b == CLR_DEFAULT) { ! remove style hints
  5219. glk($00B1, swin, i, 7);
  5220. glk($00B1, swin, i, 8);
  5221. }
  5222. else {
  5223. glk($00B0, swin, i, 7, fwd);
  5224. glk($00B0, swin, i, 8, bwd);
  5225. }
  5226. }
  5227. ! Now re-open the windows to apply the hints
  5228. if (gg_statuswin) glk($0024, gg_statuswin, 0); ! close_window
  5229. if (doclear || ( window ~= 1 && (clr_fg ~= f || clr_bg ~= b) ) ) {
  5230. glk($0024, gg_mainwin, 0);
  5231. gg_mainwin = glk($0023, 0, 0, 0, 3, GG_MAINWIN_ROCK); ! window_open
  5232. if (gg_scriptstr ~= 0)
  5233. glk($002D, gg_mainwin, gg_scriptstr); ! window_set_echo_stream
  5234. }
  5235. gg_statuswin = glk($0023, gg_mainwin, $12, gg_statuswin_cursize,
  5236. 4, GG_STATUSWIN_ROCK); ! window_open
  5237. if (statuswin_current && gg_statuswin)
  5238. MoveCursor(); else MainWindow();
  5239. }
  5240. if (window ~= 2) {
  5241. clr_fgstatus = f;
  5242. clr_bgstatus = b;
  5243. }
  5244. if (window ~= 1) {
  5245. clr_fg = f;
  5246. clr_bg = b;
  5247. }
  5248. ];
  5249. #Endif; ! COLOUR
  5250. #Endif; ! TARGET_
  5251. #Ifndef COLOUR;
  5252. [ SetColour f b window doclear;
  5253. f = b = window = doclear = 0;
  5254. ];
  5255. #Endif;
  5256. [ SetClr f b w;
  5257. SetColour (f, b, w);
  5258. ];
  5259. [ RestoreColours; ! L61007, L61113
  5260. gg_statuswin_cursize = -1; ! Force window split in StatusLineHeight()
  5261. #Ifdef COLOUR;
  5262. if (clr_on) { ! check colour has been used
  5263. SetColour(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored
  5264. SetColour(clr_fgstatus, clr_bgstatus, 1, true);
  5265. ClearScreen();
  5266. }
  5267. #Endif;
  5268. #Ifdef TARGET_ZCODE;
  5269. #Iftrue (#version_number == 6); ! request screen update
  5270. (0-->8) = (0-->8) | $$00000100;
  5271. #Endif;
  5272. #Endif; ! TARGET_
  5273. ];
  5274. ! ----------------------------------------------------------------------------
  5275. ! Except in Version 3, the DrawStatusLine routine does just that: this is
  5276. ! provided explicitly so that it can be Replace'd to change the style, and
  5277. ! as written it emulates the ordinary Standard game status line, which is
  5278. ! drawn in hardware
  5279. ! ----------------------------------------------------------------------------
  5280. #Ifdef TARGET_ZCODE;
  5281. #IfV5;
  5282. #Iftrue (#version_number == 6);
  5283. [ DrawStatusLine width x charw scw mvw;
  5284. HDR_GAMEFLAGS-->0 = (HDR_GAMEFLAGS-->0) & ~$0004;
  5285. StatusLineHeight(gg_statuswin_size);
  5286. ! Now clear the window. This isn't totally trivial. Our approach is to
  5287. ! select the fixed space font, measure its width, and print an appropriate
  5288. ! number of spaces. We round up if the screen isn't a whole number of
  5289. ! characters wide, and rely on window 1 being set to clip by default.
  5290. MoveCursor(1, 1);
  5291. @set_font 4 -> x;
  5292. width = ScreenWidth();
  5293. spaces width;
  5294. ! Back to standard font for the display. We use output_stream 3 to
  5295. ! measure the space required, the aim being to get 50 characters
  5296. ! worth of space for the location name.
  5297. MoveCursor(1, 2);
  5298. @set_font 1 -> x;
  5299. if (location == thedark)
  5300. print (name) location;
  5301. else {
  5302. FindVisibilityLevels();
  5303. if (visibility_ceiling == location) print (name) location;
  5304. else print (The) visibility_ceiling;
  5305. }
  5306. @get_wind_prop 1 3 -> width;
  5307. @get_wind_prop 1 13 -> charw;
  5308. charw = charw & $FF;
  5309. @output_stream 3 StorageForShortName;
  5310. print (string) SCORE__TX, "00000";
  5311. @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
  5312. @output_stream 3 StorageForShortName;
  5313. print (string) MOVES__TX, "00000";
  5314. @output_stream -3; mvw = HDR_PIXELSTO3-->0 + charw;
  5315. if (width - scw - mvw >= 50*charw) {
  5316. x = 1+width-scw-mvw;
  5317. @set_cursor 1 x; print (string) SCORE__TX, sline1;
  5318. x = x+scw;
  5319. @set_cursor 1 x; print (string) MOVES__TX, sline2;
  5320. }
  5321. else {
  5322. @output_stream 3 StorageForShortName;
  5323. print "00000/00000";
  5324. @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
  5325. if (width - scw >= 50*charw) {
  5326. x = 1+width-scw;
  5327. @set_cursor 1 x; print sline1, "/", sline2;
  5328. }
  5329. }
  5330. ! Reselect roman, as Infocom's interpreters interpreters go funny
  5331. ! if reverse is selected twice.
  5332. MainWindow();
  5333. ];
  5334. #Endif; ! #version_number == 6
  5335. #Endif; ! V5
  5336. #Endif; ! TARGET_ZCODE
  5337. #Ifndef DrawStatusLine;
  5338. [ DrawStatusLine width posa posb;
  5339. #Ifdef TARGET_GLULX;
  5340. ! If we have no status window, we must not try to redraw it.
  5341. if (gg_statuswin == 0)
  5342. return;
  5343. #Endif;
  5344. ! If there is no player location, we shouldn't try to draw status window
  5345. if (location == nothing || parent(player) == nothing)
  5346. return;
  5347. StatusLineHeight(gg_statuswin_size);
  5348. MoveCursor(1, 1);
  5349. width = ScreenWidth();
  5350. posa = width-26; posb = width-13;
  5351. spaces width;
  5352. MoveCursor(1, 2);
  5353. if (location == thedark) {
  5354. print (name) location;
  5355. }
  5356. else {
  5357. FindVisibilityLevels();
  5358. if (visibility_ceiling == location)
  5359. print (name) location;
  5360. else
  5361. print (The) visibility_ceiling;
  5362. }
  5363. if (sys_statusline_flag && width > 53) {
  5364. MoveCursor(1, posa);
  5365. print (string) TIME__TX;
  5366. LanguageTimeOfDay(sline1, sline2);
  5367. }
  5368. else {
  5369. if (width > 66) {
  5370. #Ifndef NO_SCORE;
  5371. MoveCursor(1, posa);
  5372. print (string) SCORE__TX, sline1;
  5373. #Endif;
  5374. MoveCursor(1, posb);
  5375. print (string) MOVES__TX, sline2;
  5376. }
  5377. #Ifndef NO_SCORE;
  5378. if (width > 53 && width <= 66) {
  5379. MoveCursor(1, posb);
  5380. print sline1, "/", sline2;
  5381. }
  5382. #Endif;
  5383. }
  5384. MainWindow(); ! set_window
  5385. ];
  5386. #Endif;
  5387. #Ifdef TARGET_GLULX;
  5388. [ StatusLineHeight hgt parwin;
  5389. if (gg_statuswin == 0) return;
  5390. if (hgt == gg_statuswin_cursize) return;
  5391. parwin = glk($0029, gg_statuswin); ! window_get_parent
  5392. glk($0026, parwin, $12, hgt, 0); ! window_set_arrangement
  5393. gg_statuswin_cursize = hgt;
  5394. ];
  5395. [ Box__Routine maxwid arr ix lines lastnl parwin;
  5396. maxwid = 0; ! squash compiler warning
  5397. lines = arr-->0;
  5398. if (gg_quotewin == 0) {
  5399. gg_arguments-->0 = lines;
  5400. ix = InitGlkWindow(GG_QUOTEWIN_ROCK);
  5401. if (ix == false) ix = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_QUOTEWIN_ROCK);
  5402. if (ix == false)
  5403. gg_quotewin = glk($0023, gg_mainwin, $12, lines, 3,
  5404. GG_QUOTEWIN_ROCK); ! window_open
  5405. }
  5406. else {
  5407. parwin = glk($0029, gg_quotewin); ! window_get_parent
  5408. glk($0026, parwin, $12, lines, 0); ! window_set_arrangement
  5409. }
  5410. lastnl = true;
  5411. if (gg_quotewin) {
  5412. glk($002A, gg_quotewin); ! window_clear
  5413. glk($002F, gg_quotewin); ! set_window
  5414. lastnl = false;
  5415. }
  5416. ! If gg_quotewin is zero here, the quote just appears in the story window.
  5417. glk($0086, 7); ! set blockquote style
  5418. for (ix=0 : ix<lines : ix++) {
  5419. print (string) arr-->(ix+1);
  5420. if (ix < lines-1 || lastnl) new_line;
  5421. }
  5422. glk($0086, 0); ! set normal style
  5423. if (gg_quotewin) {
  5424. glk($002F, gg_mainwin); ! set_window
  5425. }
  5426. ];
  5427. #Endif; ! TARGET_GLULX
  5428. #Ifdef TARGET_ZCODE;
  5429. [ ZZInitialise;
  5430. standard_interpreter = HDR_TERPSTANDARD-->0;
  5431. transcript_mode = ((HDR_GAMEFLAGS-->0) & $0001);
  5432. sys_statusline_flag = ( (HDR_TERPFLAGS->0) & $0002 ) / 2;
  5433. top_object = #largest_object-255;
  5434. dict_start = HDR_DICTIONARY-->0;
  5435. dict_entry_size = dict_start->(dict_start->0 + 1);
  5436. dict_start = dict_start + dict_start->0 + 4;
  5437. dict_end = dict_start + (dict_start - 2)-->0 * dict_entry_size;
  5438. #Ifdef DEBUG;
  5439. if (dict_start > 0 && dict_end < 0 &&
  5440. ((-dict_start) - dict_end) % dict_entry_size == 0)
  5441. print "** Warning: grammar properties might not work correctly **^";
  5442. #Endif; ! DEBUG
  5443. buffer->0 = INPUT_BUFFER_LEN - WORDSIZE;
  5444. buffer2->0 = INPUT_BUFFER_LEN - WORDSIZE;
  5445. buffer3->0 = INPUT_BUFFER_LEN - WORDSIZE;
  5446. parse->0 = MAX_BUFFER_WORDS;
  5447. parse2->0 = MAX_BUFFER_WORDS;
  5448. ];
  5449. #Ifnot; ! TARGET_GLULX;
  5450. [ GGInitialise res;
  5451. @gestalt 4 2 res; ! Test if this interpreter has Glk.
  5452. if (res == 0) {
  5453. ! Without Glk, we're entirely screwed.
  5454. quit;
  5455. }
  5456. ! Set the VM's I/O system to be Glk.
  5457. @setiosys 2 0;
  5458. ! First, we must go through all the Glk objects that exist, and see
  5459. ! if we created any of them. One might think this strange, since the
  5460. ! program has just started running, but remember that the player might
  5461. ! have just typed "restart".
  5462. GGRecoverObjects();
  5463. res = InitGlkWindow(0);
  5464. if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 0);
  5465. if (res) return;
  5466. ! Now, gg_mainwin and gg_storywin might already be set. If not, set them.
  5467. if (gg_mainwin == 0) {
  5468. ! Open the story window.
  5469. res = InitGlkWindow(GG_MAINWIN_ROCK);
  5470. if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_MAINWIN_ROCK);
  5471. if (res == false)
  5472. gg_mainwin = glk($0023, 0, 0, 0, 3, GG_MAINWIN_ROCK); ! window_open
  5473. if (gg_mainwin == 0) {
  5474. ! If we can't even open one window, there's no point in going on.
  5475. quit;
  5476. }
  5477. }
  5478. else {
  5479. ! There was already a story window. We should erase it.
  5480. glk($002A, gg_mainwin); ! window_clear
  5481. }
  5482. if (gg_statuswin == 0) {
  5483. res = InitGlkWindow(GG_STATUSWIN_ROCK);
  5484. if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_STATUSWIN_ROCK);
  5485. if (res == false) {
  5486. gg_statuswin_cursize = gg_statuswin_size;
  5487. gg_statuswin = glk($0023, gg_mainwin, $12, gg_statuswin_cursize,
  5488. 4, GG_STATUSWIN_ROCK); ! window_open
  5489. }
  5490. }
  5491. ! It's possible that the status window couldn't be opened, in which case
  5492. ! gg_statuswin is now zero. We must allow for that later on.
  5493. glk($002F, gg_mainwin); ! set_window
  5494. if (InitGlkWindow(1) == false) LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 1);
  5495. ];
  5496. [ GGRecoverObjects id;
  5497. ! If GGRecoverObjects() has been called, all these stored IDs are
  5498. ! invalid, so we start by clearing them all out.
  5499. ! (In fact, after a restoreundo, some of them may still be good.
  5500. ! For simplicity, though, we assume the general case.)
  5501. gg_mainwin = 0;
  5502. gg_statuswin = 0;
  5503. gg_quotewin = 0;
  5504. gg_scriptfref = 0;
  5505. gg_scriptstr = 0;
  5506. gg_savestr = 0;
  5507. gg_statuswin_cursize = 0;
  5508. #Ifdef DEBUG;
  5509. gg_commandstr = 0;
  5510. gg_command_reading = false;
  5511. #Endif; ! DEBUG
  5512. ! Also tell the game to clear its object references.
  5513. if (IdentifyGlkObject(0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 0);
  5514. id = glk($0040, 0, gg_arguments); ! stream_iterate
  5515. while (id) {
  5516. switch (gg_arguments-->0) {
  5517. GG_SAVESTR_ROCK: gg_savestr = id;
  5518. GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
  5519. #Ifdef DEBUG;
  5520. GG_COMMANDWSTR_ROCK: gg_commandstr = id;
  5521. gg_command_reading = false;
  5522. GG_COMMANDRSTR_ROCK: gg_commandstr = id;
  5523. gg_command_reading = true;
  5524. #Endif; ! DEBUG
  5525. default: if (IdentifyGlkObject(1, 1, id, gg_arguments-->0) == false)
  5526. LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 1, id, gg_arguments-->0);
  5527. }
  5528. id = glk($0040, id, gg_arguments); ! stream_iterate
  5529. }
  5530. id = glk($0020, 0, gg_arguments); ! window_iterate
  5531. while (id) {
  5532. switch (gg_arguments-->0) {
  5533. GG_MAINWIN_ROCK: gg_mainwin = id;
  5534. GG_STATUSWIN_ROCK: gg_statuswin = id;
  5535. GG_QUOTEWIN_ROCK: gg_quotewin = id;
  5536. default: if (IdentifyGlkObject(1, 0, id, gg_arguments-->0) == false)
  5537. LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 0, id, gg_arguments-->0);
  5538. }
  5539. id = glk($0020, id, gg_arguments); ! window_iterate
  5540. }
  5541. id = glk($0064, 0, gg_arguments); ! fileref_iterate
  5542. while (id) {
  5543. switch (gg_arguments-->0) {
  5544. GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
  5545. default: if (IdentifyGlkObject(1, 2, id, gg_arguments-->0) == false)
  5546. LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 2, id, gg_arguments-->0);
  5547. }
  5548. id = glk($0064, id, gg_arguments); ! fileref_iterate
  5549. }
  5550. ! Tell the game to tie up any loose ends.
  5551. if (IdentifyGlkObject(2) == false)
  5552. LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 2);
  5553. ];
  5554. ! This somewhat obfuscated function will print anything.
  5555. ! It handles strings, functions (with optional arguments), objects,
  5556. ! object properties (with optional arguments), and dictionary words.
  5557. ! It does *not* handle plain integers, but you can use
  5558. ! DecimalNumber or EnglishNumber to handle that case.
  5559. !
  5560. ! Calling: Is equivalent to:
  5561. ! ------- ----------------
  5562. ! PrintAnything() <nothing printed>
  5563. ! PrintAnything(0) <nothing printed>
  5564. ! PrintAnything("string"); print (string) "string";
  5565. ! PrintAnything('word') print (address) 'word';
  5566. ! PrintAnything(obj) print (name) obj;
  5567. ! PrintAnything(obj, prop) obj.prop();
  5568. ! PrintAnything(obj, prop, args...) obj.prop(args...);
  5569. ! PrintAnything(func) func();
  5570. ! PrintAnything(func, args...) func(args...);
  5571. [ PrintAnything _vararg_count obj mclass;
  5572. print_anything_result = 0;
  5573. if (_vararg_count == 0) return;
  5574. @copy sp obj;
  5575. _vararg_count--;
  5576. if (obj == 0) return;
  5577. if (obj->0 == $60) {
  5578. ! Dictionary word. Metaclass() can't catch this case, so we do
  5579. ! it manually.
  5580. print (address) obj;
  5581. return;
  5582. }
  5583. mclass = metaclass(obj);
  5584. switch (mclass) {
  5585. nothing:
  5586. return;
  5587. String:
  5588. print (string) obj;
  5589. return;
  5590. Routine:
  5591. ! Call the function with all the arguments which are already
  5592. ! on the stack.
  5593. @call obj _vararg_count print_anything_result;
  5594. return;
  5595. Object:
  5596. if (_vararg_count == 0) {
  5597. print (name) obj;
  5598. }
  5599. else {
  5600. ! Push the object back onto the stack, and call the
  5601. ! veneer routine that handles obj.prop() calls.
  5602. @copy obj sp;
  5603. _vararg_count++;
  5604. @call CA__Pr _vararg_count print_anything_result;
  5605. }
  5606. return;
  5607. }
  5608. ];
  5609. ! This does the same as PrintAnything, but the output is sent to a
  5610. ! byte array in memory. The first two arguments must be the array
  5611. ! address and length; the following arguments are interpreted as
  5612. ! for PrintAnything. The return value is the number of characters
  5613. ! output.
  5614. ! If the output is longer than the array length given, the extra
  5615. ! characters are discarded, so the array does not overflow.
  5616. ! (However, the return value is the total length of the output,
  5617. ! including discarded characters.)
  5618. [ PrintAnyToArray _vararg_count arr arrlen str oldstr len;
  5619. @copy sp arr;
  5620. @copy sp arrlen;
  5621. _vararg_count = _vararg_count - 2;
  5622. oldstr = glk($0048); ! stream_get_current
  5623. str = glk($0043, arr, arrlen, 1, 0); ! stream_open_memory
  5624. if (str == 0) return 0;
  5625. glk($0047, str); ! stream_set_current
  5626. @call PrintAnything _vararg_count 0;
  5627. glk($0047, oldstr); ! stream_set_current
  5628. @copy $ffffffff sp;
  5629. @copy str sp;
  5630. @glk $0044 2 0; ! stream_close
  5631. @copy sp len;
  5632. @copy sp 0;
  5633. return len;
  5634. ];
  5635. ! And this calls PrintAnyToArray on a particular array, jiggering
  5636. ! the result to be a Glulx C-style ($E0) string.
  5637. Constant GG_ANYTOSTRING_LEN 66;
  5638. Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1;
  5639. [ ChangeAnyToCString _vararg_count ix len;
  5640. ix = GG_ANYTOSTRING_LEN-2;
  5641. @copy ix sp;
  5642. ix = AnyToStrArr+1;
  5643. @copy ix sp;
  5644. ix = _vararg_count+2;
  5645. @call PrintAnyToArray ix len;
  5646. AnyToStrArr->0 = $E0;
  5647. if (len >= GG_ANYTOSTRING_LEN)
  5648. len = GG_ANYTOSTRING_LEN-1;
  5649. AnyToStrArr->(len+1) = 0;
  5650. return AnyToStrArr;
  5651. ];
  5652. #Endif; ! TARGET_
  5653. ! This is a trivial function which just prints a number, in decimal
  5654. ! digits. It may be useful as a stub to pass to PrintAnything.
  5655. [ DecimalNumber num; print num; ];
  5656. #Ifndef SHORTNAMEBUF_LEN; ! Can't use 'Default', unfortunately,
  5657. Constant SHORTNAMEBUF_LEN 160; ! but this is functionally equivalent
  5658. #Endif;
  5659. #IfV5;
  5660. #Ifdef VN_1630;
  5661. Array StorageForShortName buffer SHORTNAMEBUF_LEN;
  5662. #Ifnot;
  5663. Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN;
  5664. #Endif; ! VN_1630
  5665. #Endif; ! V5
  5666. #Ifdef TARGET_ZCODE;
  5667. ! Platform-independent way of printing strings, routines and properties
  5668. ! to a buffer (defined as length word followed by byte characters).
  5669. [ PrintToBuffer buf len a b c d e;
  5670. print_anything_result = 0;
  5671. @output_stream 3 buf;
  5672. switch (metaclass(a)) {
  5673. String:
  5674. print (string) a;
  5675. Routine:
  5676. print_anything_result = a(b, c, d, e);
  5677. Object,Class:
  5678. if (b)
  5679. print_anything_result = PrintOrRun(a, b, true);
  5680. else
  5681. print (name) a;
  5682. }
  5683. @output_stream -3;
  5684. if (buf-->0 > len) RunTimeError(14, len, "in PrintToBuffer()");
  5685. return buf-->0;
  5686. ];
  5687. #Ifnot; ! TARGET_GLULX
  5688. [ PrintToBuffer buf len a b c d e;
  5689. if (b) {
  5690. if (metaclass(a) == Object && a.#b == WORDSIZE
  5691. && metaclass(a.b) == String)
  5692. buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a.b);
  5693. else
  5694. buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a, b, c, d, e);
  5695. }
  5696. else
  5697. buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a);
  5698. if (buf-->0 > len) buf-->0 = len;
  5699. return buf-->0;
  5700. ];
  5701. #Endif; ! TARGET_
  5702. ! Print contents of buffer (defined as length word followed by byte characters).
  5703. ! no_break == 1: omit trailing newline.
  5704. ! set_case == 1: capitalise first letter;
  5705. ! == 2: capitalise first letter, remainder lower case;
  5706. ! == 3: all lower case;
  5707. ! == 4: all upper case.
  5708. ! centred == 1: add leading spaces.
  5709. [ PrintFromBuffer buf no_break set_case centred
  5710. i j k;
  5711. j = (buf-->0) - 1;
  5712. if (buf->(j+WORDSIZE) ~= 10 or 13) j++; ! trim any trailing newline
  5713. if (centred) {
  5714. k = (ScreenWidth() - j) / 2;
  5715. if (k>0) spaces k;
  5716. }
  5717. for (i=0 : i<j : i++) {
  5718. k = buf->(WORDSIZE+i);
  5719. switch (set_case) {
  5720. 0: break;
  5721. 1: if (i) set_case = 0;
  5722. else k = UpperCase(k);
  5723. 2: if (i) k = LowerCase(k);
  5724. else k = UpperCase(k);
  5725. 3: k = LowerCase(k);
  5726. 4: k = UpperCase(k);
  5727. }
  5728. print (char) k;
  5729. }
  5730. if (no_break == false) new_line;
  5731. return j;
  5732. ];
  5733. ! None of the following functions should be called for zcode if the
  5734. ! output exceeds the size of the buffer.
  5735. [ StringSize a b c d e;
  5736. PrintToBuffer(StorageForShortName, 160, a, b, c, d, e);
  5737. return StorageForShortName-->0;
  5738. ];
  5739. [ PrintCapitalised a b no_break no_caps centred;
  5740. if (metaclass(a) == Routine or String || b == 0 || metaclass(a.b) == Routine or String)
  5741. PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, a, b);
  5742. else
  5743. if (a.b == NULL) rfalse;
  5744. else return RunTimeError(2, a, b);
  5745. if (no_caps == 0 or 1) no_caps = ~~no_caps;
  5746. PrintFromBuffer(StorageForShortName, no_break, no_caps, centred);
  5747. return print_anything_result;
  5748. ];
  5749. [ Centre a b;
  5750. PrintCapitalised(a, b, false, true, true);
  5751. ];
  5752. [ CapitRule str no_caps;
  5753. if (no_caps) print (string) str;
  5754. else PrintCapitalised(str,0,true);
  5755. ];
  5756. [ PrefaceByArticle o acode pluralise capitalise i artform findout artval;
  5757. if (o provides articles) {
  5758. artval=(o.&articles)-->(acode+short_name_case*LanguageCases);
  5759. if (capitalise)
  5760. print (CapitRule) artval;
  5761. else
  5762. print (string) artval;
  5763. if (pluralise) return;
  5764. print (PSN__) o; return;
  5765. }
  5766. i = GetGNAOfObject(o);
  5767. if (pluralise) {
  5768. if (i < 3 || (i >= 6 && i < 9)) i = i + 3;
  5769. }
  5770. i = LanguageGNAsToArticles-->i;
  5771. artform = LanguageArticles
  5772. + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);
  5773. #Iftrue (LanguageContractionForms == 2);
  5774. if (artform-->acode ~= artform-->(acode+3)) findout = true;
  5775. #Endif; ! LanguageContractionForms
  5776. #Iftrue (LanguageContractionForms == 3);
  5777. if (artform-->acode ~= artform-->(acode+3)) findout = true;
  5778. if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
  5779. #Endif; ! LanguageContractionForms
  5780. #Iftrue (LanguageContractionForms == 4);
  5781. if (artform-->acode ~= artform-->(acode+3)) findout = true;
  5782. if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
  5783. if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
  5784. #Endif; ! LanguageContractionForms
  5785. #Iftrue (LanguageContractionForms > 4);
  5786. findout = true;
  5787. #Endif; ! LanguageContractionForms
  5788. #Ifdef TARGET_ZCODE;
  5789. if (standard_interpreter && findout) {
  5790. StorageForShortName-->0 = SHORTNAMEBUF_LEN;
  5791. @output_stream 3 StorageForShortName;
  5792. if (pluralise) print (number) pluralise; else print (PSN__) o;
  5793. @output_stream -3;
  5794. acode = acode + 3*LanguageContraction(StorageForShortName + 2);
  5795. }
  5796. #Ifnot; ! TARGET_GLULX
  5797. if (findout) {
  5798. if (pluralise)
  5799. PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, EnglishNumber, pluralise);
  5800. else
  5801. PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
  5802. acode = acode + 3*LanguageContraction(StorageForShortName);
  5803. }
  5804. #Endif; ! TARGET_
  5805. CapitRule (artform-->acode, ~~capitalise); ! print article
  5806. if (pluralise) return;
  5807. print (PSN__) o;
  5808. ];
  5809. [ PSN__ o;
  5810. if (o == 0) { print (string) NOTHING__TX; rtrue; }
  5811. switch (metaclass(o)) {
  5812. Routine: print "<routine ", o, ">"; rtrue;
  5813. String: print "<string ~", (string) o, "~>"; rtrue;
  5814. nothing: print "<illegal object number ", o, ">"; rtrue;
  5815. }
  5816. #Ifdef LanguagePrintShortName;
  5817. if (LanguagePrintShortName(o)) rtrue;
  5818. #Endif; ! LanguagePrintShortName
  5819. if (indef_mode && o.&short_name_indef ~= 0 && PrintOrRun(o, short_name_indef, 1) ~= 0) rtrue;
  5820. if (o.&short_name ~= 0 && PrintOrRun(o, short_name, 1) ~= 0) rtrue;
  5821. print (object) o;
  5822. ];
  5823. [ Indefart o saveIndef;
  5824. saveIndef = indef_mode; indef_mode = true; caps_mode = false;
  5825. if (o has proper) {
  5826. indef_mode = NULL;
  5827. print (PSN__) o;
  5828. indef_mode = saveIndef;
  5829. return;
  5830. }
  5831. if (o provides article) {
  5832. PrintOrRun(o, article, 1);
  5833. print " ", (PSN__) o;
  5834. indef_mode = saveIndef;
  5835. return;
  5836. }
  5837. PrefaceByArticle(o, 2);
  5838. indef_mode = saveIndef;
  5839. ];
  5840. [ CInDefArt o saveIndef saveCaps;
  5841. saveIndef = indef_mode; indef_mode = true;
  5842. saveCaps = caps_mode; caps_mode = true;
  5843. if (o has proper) {
  5844. indef_mode = NULL;
  5845. PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
  5846. PrintFromBuffer(StorageForShortName, true, caps_mode);
  5847. caps_mode = saveCaps;
  5848. return;
  5849. }
  5850. if (o provides article) {
  5851. PrintCapitalised(o, article, true);
  5852. print " ", (PSN__) o;
  5853. indef_mode = saveIndef;
  5854. caps_mode = saveCaps;
  5855. return;
  5856. }
  5857. PrefaceByArticle(o, 2, 0, 1);
  5858. caps_mode = saveCaps;
  5859. indef_mode = saveIndef;
  5860. ];
  5861. [ Defart o saveIndef;
  5862. saveIndef = indef_mode;
  5863. indef_mode = false;
  5864. caps_mode = false;
  5865. if ((~~o ofclass Object) || o has proper) {
  5866. indef_mode = NULL;
  5867. if (o == player) {
  5868. if (player provides narrative_voice) {
  5869. switch (player.narrative_voice) {
  5870. 1: print (string) MYSELF__TX;
  5871. 2: print (string) YOURSELF__TX;
  5872. 3: print (PSN__) o;
  5873. default: RunTimeError(16, player.narrative_voice);
  5874. }
  5875. }
  5876. else ThatOrThose(player);
  5877. } else {
  5878. print (PSN__) o;
  5879. }
  5880. indef_mode = saveIndef;
  5881. return;
  5882. }
  5883. PrefaceByArticle(o, 1);
  5884. indef_mode = saveIndef;
  5885. ];
  5886. [ CDefart o saveIndef saveCaps;
  5887. saveIndef = indef_mode; indef_mode = false;
  5888. saveCaps = caps_mode; caps_mode = true;
  5889. if (~~o ofclass Object) {
  5890. indef_mode = NULL; print (PSN__) o;
  5891. }
  5892. else
  5893. if (o has proper) {
  5894. indef_mode = NULL;
  5895. PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
  5896. PrintFromBuffer(StorageForShortName, true, caps_mode);
  5897. }
  5898. else
  5899. PrefaceByArticle(o, 0);
  5900. indef_mode = saveIndef; caps_mode = saveCaps;
  5901. ];
  5902. [ PrintShortName o saveIndef;
  5903. saveIndef = indef_mode; indef_mode = NULL;
  5904. PSN__(o); indef_mode = saveIndef;
  5905. ];
  5906. [ EnglishNumber n; LanguageNumber(n); ];
  5907. [ SerialComma n;
  5908. #Ifdef SERIAL_COMMAS;
  5909. if (n>2) print ",";
  5910. #Endif;
  5911. n=0; ! quell unused n variable warning
  5912. ];
  5913. [ NumberWord o i n;
  5914. n = LanguageNumbers-->0;
  5915. for (i=1 : i<=n : i=i+2)
  5916. if (o == LanguageNumbers-->i) return LanguageNumbers-->(i+1);
  5917. return 0;
  5918. ];
  5919. [ RandomEntry tab;
  5920. if (tab-->0 == 0) return RunTimeError(8);
  5921. return tab-->(random(tab-->0));
  5922. ];
  5923. ! ----------------------------------------------------------------------------
  5924. ! Useful routine: unsigned comparison (for addresses in Z-machine)
  5925. ! Returns 1 if x>y, 0 if x=y, -1 if x<y
  5926. ! ----------------------------------------------------------------------------
  5927. [ UnsignedCompare x y u v;
  5928. if (x == y) return 0;
  5929. if (x < 0 && y >= 0) return 1;
  5930. if (x >= 0 && y < 0) return -1;
  5931. u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT;
  5932. if (u > v) return 1;
  5933. return -1;
  5934. ];
  5935. ! ==============================================================================
  5936. #Ifdef NITFOL_HOOKS; ! Code contributed by Evin Robertson
  5937. #Ifdef TARGET_GLULX; ! Might be nice for Z-machine games too,
  5938. ! but I'm not going to try to make this work
  5939. ! given #Ifdef funniness.
  5940. Array magic_array --> ! This is so nitfol can do typo correction /
  5941. ! automapping / debugging on Glulx games
  5942. $6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc' 10 refers to length
  5943. Magic_Global_Dispatch__
  5944. DI__check_word ! DI__check_word(buf, length)
  5945. PrintShortName
  5946. WV__Pr RV__Pr CA__Pr ! obj.prop = x; x = obj.prop; obj.prop(x)
  5947. RA__Pr RL__Pr RA__Sc ! obj.&prop; obj.#prop; class::prop
  5948. OP__Pr OC__Cl ! obj provides prop; obj ofclass class
  5949. OB__Move OB__Remove
  5950. OB__Parent__ OB__Child__ OB__Sibling__ ! No explicit veneer for these
  5951. ;
  5952. [ OB__Parent__ obj; return parent(obj); ];
  5953. [ OB__Child__ obj; return child(obj); ];
  5954. [ OB__Sibling__ obj; return sibling(obj); ];
  5955. [ Magic_Global_Dispatch__ glbl;
  5956. switch (glbl) {
  5957. 0:
  5958. if (location == TheDark) return real_location; return location;
  5959. 1:
  5960. return Player;
  5961. -1:
  5962. return CompassDirection::number; ! Silliness to make exist RA__Sc
  5963. ! Should never be called.
  5964. }
  5965. return magic_array; ! Silences a warning.
  5966. ];
  5967. [ DI__check_word buf wlen ix val res dictlen entrylen;
  5968. ! Just like in Tokenise__. In fact, Tokenise__ could call this if
  5969. ! it wanted, instead of doing this itself.
  5970. if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
  5971. for (ix=0 : ix<wlen : ix++) {
  5972. gg_tokenbuf->ix = glk($00A0, buf->ix);
  5973. }
  5974. for (: ix<DICT_WORD_SIZE : ix++) {
  5975. gg_tokenbuf->ix = 0;
  5976. }
  5977. val = #dictionary_table + WORDSIZE;
  5978. entrylen = DICT_WORD_SIZE + 7;
  5979. @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
  5980. return res;
  5981. ];
  5982. #Endif; ! TARGET_
  5983. #Endif; ! NITFOL_HOOKS
  5984. ! ==============================================================================
  5985. Object LibraryExtensions "(Library Extensions)"
  5986. with RunAll [ prop a1 a2 a3
  5987. obj rval max;
  5988. objectloop (obj in self)
  5989. if (obj provides prop && obj.prop ofclass Routine) {
  5990. rval = obj.prop(a1, a2, a3);
  5991. if (rval > max) max = rval;
  5992. if (self.BetweenCalls) self.BetweenCalls();
  5993. }
  5994. return max;
  5995. ],
  5996. RunUntil [ prop exitval a1 a2 a3
  5997. obj rval;
  5998. objectloop (obj in self)
  5999. if (obj provides prop && obj.prop ofclass Routine) {
  6000. rval = obj.prop(a1, a2, a3);
  6001. if (rval == exitval) return rval;
  6002. if (self.BetweenCalls) self.BetweenCalls();
  6003. }
  6004. return ~~exitval;
  6005. ],
  6006. RunWhile [ prop exitval a1 a2 a3
  6007. obj rval;
  6008. objectloop (obj in self)
  6009. if (obj provides prop && obj.prop ofclass Routine) {
  6010. rval = obj.prop(a1, a2, a3);
  6011. if (rval ~= exitval) return rval;
  6012. if (self.BetweenCalls) self.BetweenCalls();
  6013. }
  6014. return exitval;
  6015. ],
  6016. ext_number_1 0, ! general temporary workspace
  6017. ! can be set to a function (e.g. RestoreWN) meant for execution
  6018. ! after non-terminating calls to extension objects
  6019. ! (via RunUntil/While/All)
  6020. BetweenCalls 0,
  6021. RestoreWN [; wn = self.ext_number_1; ],
  6022. ! Special interception points
  6023. ext_messages 0, ! Called if LibraryMessages.before()
  6024. ! returns false
  6025. ! Extensions run while they return false
  6026. ! Cross-platform entry points
  6027. ! Called/Runs
  6028. ext_afterlife 0, ! [C2/R1]
  6029. ext_afterprompt 0, ! [C2/R1]
  6030. ext_amusing 0, ! [C2/R1]
  6031. ext_beforeparsing 0, ! [C2/R2]
  6032. ext_chooseobjects 0, ! [C2/R2]
  6033. ext_darktodark 0, ! [C2/R1]
  6034. ext_deathmessage 0, ! [C2/R1]
  6035. ext_gamepostroutine 0, ! [C2/R2]
  6036. ext_gamepreroutine 0, ! [C2/R2]
  6037. ext_initialise 0, ! [C1/R1]
  6038. ext_inscope 0, ! [C2/R2]
  6039. ext_lookroutine 0, ! [C2/R1]
  6040. ext_newroom 0, ! [C2/R1]
  6041. ext_objectdoesnotfit 0, ! [C2/R2]
  6042. ext_parsenoun 0, ! [C3/R3]
  6043. ext_parsenumber 0, ! [C2/R2]
  6044. ext_parsererror 0, ! [C2/R2]
  6045. ext_printrank 0, ! [C2/R1]
  6046. ext_printtaskname 0, ! [C2/R1]
  6047. ext_printverb 0, ! [C2/R2]
  6048. ext_timepasses 0, ! [C2/R1]
  6049. ext_unknownverb 0, ! [C2/R2]
  6050. ! [C1] = Called in all cases
  6051. ! [C2] = Called if EP is undefined, or returns false
  6052. ! [C3] = called if EP is undefined, or returns -1
  6053. ! [R1] = All extensions run
  6054. ! [R2] = Extensions run while they return false
  6055. ! [R3] = Extensions run while they return -1
  6056. #Ifdef TARGET_GLULX;
  6057. ! Glulx entry points
  6058. ! Called: Runs:
  6059. ext_handleglkevent 0, ! if EP undefined while extensions return false
  6060. ext_identifyglkobject 0, ! if EP undefined while extensions return false
  6061. ext_initglkwindow 0, ! if EP undefined while extensions return false
  6062. #Endif; ! TARGET_GLULX;
  6063. has proper;
  6064. ! ==============================================================================