क्वेस्ट आर्किटेक्चर
कोई स्क्रिप्ट नहीं है। लॉजिक है: यह कि Romergo में नोड्स और वेरिएबल्स कैसे व्यवस्थित हैं।
जब गेम एडिटर में लॉजिक की बात की जाती है, तो आमतौर पर जल्दी से स्क्रिप्ट्स की ओर आ जाते हैं। कहीं लेखक JavaScript लिखता है, कहीं Python, और कहीं खुद इंजन की भाषा सीखता है। यह लगभग असीमित स्वतंत्रता देता है, लेकिन इसके साथ ही यह वाक्य रचना की त्रुटियाँ, फंसी हुई लूप्स, असंगत प्लगइन्स और ऐसा कोड लाता है जिसे छह महीने के बाद इसके लेखक के लिए भी खोलना डरावना होता है।
Romergo में मैंने एक अलग रास्ता अपनाया। क्वेस्ट के अंदर कोई मनमाना स्क्रिप्ट नहीं है। कोई फंक्शन डालना, नेटवर्क अनुरोध करना या प्लेयर तक सीधे पहुंच प्राप्त करना संभव नहीं है। इसके बजाय, कहानी एक सीमित, लेकिन पर्याप्त अभिव्यक्तिपूर्ण सेट से बनाई जाती है: लॉजिकल नोड्स, वेरिएबल्स, कंडीशन्स और इफेक्ट्स।
इसका मतलब यह नहीं है कि Romergo में केवल सरल शाखाएँ बनाना संभव है। खिलाड़ी के निर्णयों को याद रखना, पॉइंट्स गिनना, विकल्प खोलना और छिपाना, अगली चैप्टर्स में अलग रास्ते चुनना, यादृच्छिक घटनाएँ बनाना और इंटरएक्टिव इंटरफेस तैयार करना संभव है। बस "इस कोड को चलाओ" कमांड के बजाय, लेखक यह वर्णन करता है कि "इस स्थिति में यह होना चाहिए"।
ग्राफ पहले से ही एक प्रोग्राम है
सबसे बुनियादी लॉजिक Romergo सीधे अध्याय के नक्शे पर होती है। दृश्य ट्रांज़िशन से जुड़े होते हैं, और विशेष नोड यह तय करते हैं कि आगे गेम कहाँ जाएगा।
अगर बहुत सरल किया जाए, तो छोटा सा क्वेस्ट इस तरह दिखता है:
स्टार्ट
↓
सुरक्षाकर्मी से बातचीत
↓
पास है?
├─ हाँ → बंद आर्काइव
└─ नहीं → बैकअप रास्ता
यह पहले से ही निष्पादित होने वाली लॉजिक है। Player प्रारंभिक नोड में प्रवेश करता है, दृश्य दिखाता है, इतिहास की स्थिति पढ़ता है और उपयुक्त संक्रमण का चयन करता है। लेखक वही मार्ग एक समझने योग्य मानचित्र की तरह देखता है, न कि if, goto और सेवा पहचानकर्ताओं के सेट के रूप में।

*Builder में वास्तविक Flow का एक अंश: Final Accusation सीन निर्णय को Switch में भेजता है, इसके बाद कुछ If / Else स्थिति की जाँच करते हैं और कहानी को विभिन्न अंतों की ओर मोड़ते हैं। नीचे खुला Logic पैनल उपलब्ध पूरा सेट दिखाता है: Dead End, Checkpoint, If / Else, Switch, Random और Teleport।*
लॉजिक पैनल में अब छह मुख्य नोड हैं:
- **खेल का अंत** वर्तमान मार्ग को रोक देता है। यह हार, खराब अंत, या केवल जानबूझकर बंद किया गया परिणाम हो सकता है।
- **चेकपॉइंट** इस बिंदु पर स्थिति को सहेजता है और एक नई वापसी इतिहास शुरू करता है। खिलाड़ी चेकपॉइंट पर वापस जा सकता है, लेकिन उसके पीछे नहीं लौट सकता।
- **अगर / अन्यथा** एक नियम की जाँच करता है। सही आउटपुट मिलान की शर्त के अनुसार जाता है, गलत आउटपुट वैकल्पिक मार्ग रहेगा।
- **स्विच** तब उपयोगी होता है जब विकल्प दो से अधिक हों। इसकी शाखाओं की जाँच ऊपर से नीचे की जाती है: पहला मेल खाने वाला शर्त सक्रिय हो जाता है, और बिना शर्त वाली शाखा डिफ़ॉल्ट विकल्प बन जाती है।
- **रैन्डम** कनेक्टेड आउटपुट में से एक को चुनता है। रैन्डम अपनी स्थिति में बीज और स्टेप को रखता है, इसलिए बचत को बिना खिलाड़ी को किसी अन्य रास्ते पर अराजक रूप से भेजे हुए पुनर्स्थापित किया जा सकता है।
- **टेलीपोर्ट** स्वयं कुछ भी तय नहीं करता। यह लंबी दृश्य संबंध को तोड़ने और बड़े मानचित्र के दूर के हिस्सों को सुरक्षित तरीके से जोड़ने में मदद करता है।
स्टार्ट और फिनिश अध्याय को फ्रेम करते हैं, सामान्य दृश्य सामग्री दिखाते हैं, और तार्किक नोड्स उन्हें मार्ग में बदल देते हैं। इसी स्तर पर आप एक रैखिक कहानी, डिवर्जेंस, कई अंत, यादृच्छिक घटना और सुरक्षित वापसी बिंदु बना सकते हैं — बिना किसी स्क्रिप्ट की एक भी पंक्ति के।
परिवर्ती — यह इतिहास की याददाश्त है
एक ग्राफ़ पर्याप्त नहीं है, अगर इतिहास को याद रखना है कि पहले क्या हुआ था। इसके लिए क्वेस्ट में तीन सरल प्रकार के परिवर्तनशील हैं:
booleanतथ्य को संग्रहित करता है: क्या चाबी मिली, नायक ने झूठ बोला, चेतावनी चालू है;numberसंख्या को संग्रहित करता है: विश्वास, स्वास्थ्य, सबूतों की संख्या या प्रतिष्ठा अंक;stringचयनित मान को संग्रहित करता है: मार्ग का नाम, समूह या समाधान का कोड।
हर चर के पास एक कुंजी, लेखक के लिए समझने योग्य शीर्षक और प्रारंभिक मान होता है। संख्यात्मक चर के लिए आप न्यूनतम और अधिकतम को भी वर्णित कर सकते हैं, और सेवा चर को खिलाड़ी की स्थिति पैनल से छिपाया जा सकता है या केवल उस स्थिति में दिखाया जा सकता है जब कोई शर्त पूरी हो।
यहाँ मुख्य बात यह है कि चर पूरे क्वेस्ट के लिए होते हैं, किसी एक दृश्य के लिए नहीं। यदि पहले अध्याय में खिलाड़ी ने प्रवेश पत्र प्राप्त किया, तो चौथे अध्याय में आप उसी झंडे की जांच कर सकते हैं। फिनिश के माध्यम से अगले अध्याय में जाते समय स्थिति शून्य नहीं होती। यह चयनित विकल्पों, लागू प्रभावों, चेकपॉइंट और वर्तमान यादृच्छिक चरण के साथ सुरक्षित रहती है।
यह एक सरल लेखक चक्र बनाता है:
खिलाड़ी का चयन मान को रिकॉर्ड करता है
↓
चर स्थिति को संग्रहीत करता है
↓
शर्त बाद में इसे पढ़ती है
↓
ग्राफ़, दृश्य या इंटरफ़ेस प्रतिक्रिया देते हैं
उदाहरण के लिए, पहले दृश्य में "मिली फोटो दिखाएँ" का विकल्प portrait_revealed = true सेट कर सकता है। बाद में **यदि / अन्यथा** नोड इस फ़्लैग की जांच करेगा और नई वार्तालाप दृश्य खोलेगा। और बाद में टर्मिनल इंटरफ़ेस केवल उन खिलाड़ियों को अतिरिक्त प्रविष्टि दिखाएगा जिन्होंने फोटो को खोल दिया था।
एक ही वेरिएबल तीन अलग-अलग क्वेस्ट हिस्सों को जोड़ता है। इसके लिए मैनुअली सीन या चैप्टर के बीच मान पास करने की जरूरत नहीं है: वे एक ही सामान्य प्रगति स्थिति को पढ़ते हैं।

*वेरिएबल route में एक कार्ड में स्थिर कुंजी, समझने योग्य नाम, मान का प्रकार और स्थिति की दृश्यता निर्धारित की गई है। विकल्पों और शाखाओं के साथ जुड़ेने की जानकारी उसी पृष्ठ पर नीचे है।*
विकल्प लिखते हैं, शाखाएँ पढ़ती हैं
सामान्य दृश्य में, स्थिति बदलने का सबसे स्पष्ट तरीका यह है कि विकल्प पर प्रभाव जोड़ें। दृश्य संपादक में यह इस तरह दिखता है जैसे असाइनमेंट हो:
«मैरी पर भरोसा करें» → ally = "mary"
«अकेले जाना» → ally = "none"
इसके बाद Switch खिलाड़ी को ally के मान के अनुसार सही दृश्य में निर्देशित कर सकता है। वेरिएबल पेज अलग दिखाता है कि कौन से विकल्प वेरिएबल को रिकॉर्ड करते हैं और कौन सी शाखाएँ इसे पढ़ती हैं। यह एक छोटी सी विवरण है, लेकिन बड़े क्वेस्ट में यह कई दृश्यों में झंझट भरे खोज की जगह ले सकती है।

*Switch शाखाएँ ऊपर से नीचे जाँच की जाती हैं। इस उदाहरण में Laboratory मार्ग route = lab पर सक्रिय होता है, जबकि Habitat route = habitat पर।*
शर्तें सामान्य तुलना का समर्थन करती हैं:
- बराबर और बराबर नहीं;
- बड़ा, बड़ा या बराबर;
- छोटा, छोटा या बराबर;
- मान मौजूद है या अभी तक नहीं बनाया गया।
सरल मामले में लेखक सीधे If / Else या Switch की सेटिंग्स में चर, ऑपरेटर और मान चुनता है। अधिक जटिल तर्क के लिए, सामान्य runtime **सभी शर्तें**, **कोई भी शर्त** और प्रतिकूलता समूहों को समझता है।
इस तरह के नियम को संरचनात्मक रूप से इस तरह वर्णित किया जा सकता है:
{
"op": "all",
"conditions": [
{ "op": "gte", "key": "evidence", "value": 3 },
{ "op": "eq", "key": "alarm_active", "value": false }
]
}
यह छोटे स्क्रिप्ट जैसा है, लेकिन मूल रूप से ऐसा नहीं है। यहां कोई फंक्शन कॉल नहीं की जा सकती या किसी चीज़ को यादृच्छिक रूप से बदल नहीं सकते। Romergo पहले से सभी अनुमत ऑपरेशनों को जानता है, संरचना की जाँच करता है और इसे Builder पूर्वावलोकन और प्रकाशित Player में समान रूप से निष्पादित करता है।
प्रभाव: प्रोग्रामिंग भाषा के बिना छोटे आदेश
शर्त स्थिति को पढ़ती है, और प्रभाव इसे बदलता है। Runtime Romergo कई परमाणु संचालन का समर्थन करता है:
setएक विशिष्ट मान लिखता है;incऔरdecसंख्या को बढ़ाते या घटाते हैं;toggleतर्कीय फ्लैग को टॉगल करता है;clampसंख्या को निर्दिष्ट सीमा के भीतर बनाए रखता है।
साधारण चयन निरीक्षक में मुख्य परिदृश्य जानबूझकर सरल होता है: किसी चर को चुनें और set के माध्यम से नई मान लिखें। उन्नत इंटरफ़ेस लॉजिक में, शर्तें और प्रभाव ऐसे संरचित रूपों के रूप में दिए जा सकते हैं जिन्हें जाँचा जा सकता है। उदाहरण के लिए, टर्मिनल की स्थितियों के बीच संक्रमण एक ही समय में निर्णय को याद रख सकता है, सबूत जोड़ सकता है और अलार्म चालू कर सकता है:

*प्रभाव सीधे उत्तर विकल्प से जुड़ा हुआ है: तीन विकल्प route में lab, habitat और server मान दर्ज करते हैं। चर का क्षेत्र और नया मान संकीर्ण निरीक्षक में भी अलग रहते हैं।*
[
{ "op": "set", "key": "terminal_decision", "value": "cut-power" },
{ "op": "inc", "key": "evidence", "amount": 1 },
{ "op": "toggle", "key": "alarm_active" }
]
इस दृष्टिकोण की एक महत्वपूर्ण सीमा है: यह किसी भी फॉर्मूला कैल्कुलेटर नहीं है। आप evidence * trust / 2 नहीं लिख सकते, चक्र चला सकते हैं या अपनी फ़ंक्शन बना सकते हैं। जटिल व्यवहार कई स्पष्ट चरणों से इकट्ठा किया जाता है, और लॉजिक का विस्तार नए जाँचे जाने योग्य ऑपरेशन runtime के माध्यम से होता है, अनजान कोड चलाने के माध्यम से नहीं।
लेखक के लिए यह थोड़ा कम असीमित है, लेकिन कहानी पूर्वानुमेय रहती है। वही प्रभाव प्रकाशित करने से पहले जाँचे जा सकते हैं, सहेजे जा सकते हैं, खिलाड़ियों के बीच समकालिक किए जा सकते हैं और प्रगति लोड करने के बाद पुनः चलाए जा सकते हैं।
क्यों अंदर लॉजिकल नोड में अभी तक एक और Flow नहीं है
संपूर्ण पूर्वानुमेयता के बावजूद वर्तमान दृष्टिकोण की एक स्पष्ट कमी है: विकास के अनुभव के बिना व्यक्ति के लिए "चर — ऑपरेटर — मान" का फॉर्म पहले से ही प्रोग्रामिंग जैसा लग सकता है। और जब इसके पास शर्तों के समूह और कई प्रभाव आते हैं, तो डिक्लेरेटिव संरचना Player के लिए सुरक्षित रहती है, लेकिन लेखक के लिए जरूरी नहीं कि सरल बन जाए।
किसी दिन विश्लेषणात्मक नोड और खुद की लॉजिक के अंदर जाना प्राकृतिक लगता है। शर्तों की सूची की बजाय आप ब्लॉकों **और**, **या**, **न**, चर की तुलना और मान बदलने को जोड़ सकते हैं। कागज पर यह टेक्स्ट या JSON की तुलना में अधिक दोस्ताना लगता है।
लेकिन फिर मैं वास्तविक परिदृश्य की कल्पना करता हूँ: लेखक अध्याय के मानचित्र पर लॉजिक नोड खोलता है, और उसके अंदर एक और Flow मिलता है जिसमें अपने नोड और कनेक्शन होते हैं। यह ग्राफ के अंदर ग्राफ पैदा करता है। यह समझना जरूरी है कि आप अभी कहाँ हैं, कहानी के स्तर पर कैसे लौटें और दो Flow में से किसमें त्रुटि ढूँढें। शुरुआती के लिए ऐसी नेस्टिंग मौजूदा फॉर्म से भी डरावनी हो सकती है।
इसलिए फिलहाल समझौता किया गया है। कहानी का मार्ग दृश्य रूप में रहता है, जबकि नोड्स के भीतर के शर्तें और प्रभाव संक्षिप्त स्पष्ट फील्ड्स में दिखाई देते हैं। यह काम करता है, लेकिन मैं इंटरफेस को अंतिम रूप से हल किया हुआ नहीं मानता। शायद बाद में तैयार टेम्पलेट्स, चरण-दर-चरण सेटिंग और दृश्य ब्लॉक्स का अधिक स्पष्ट मिश्रण आएगा — ऐसा जो वास्तव में जटिलता को कम करे, न कि इसे बस किसी और संपादक में स्थानांतरित करे।
तर्क केवल मैप पर ही नहीं रहता
इंटरफ़ेस दृश्य वही स्थिति उपयोग करते हैं। विजेट, संदेश, उत्तर विकल्प, कार्य, सूचना या मीडिया स्ट्रीम केवल showWhen से मेल होने पर ही दिखाई दे सकता है। मीट्रिक का मान एक चर से जोड़ा जा सकता है, और विजेट की स्थिति के बीच संक्रमण—विलंब के बाद, शर्त के अनुसार या खिलाड़ी की क्रिया के बाद—प्रारंभ किया जा सकता है।
उदाहरण के लिए, ऑन-बोर्ड कंप्यूटर स्क्रीन इस तरह व्यवहार कर सकता है:
1. सबसे पहले केवल ब्लॉक किए गए टर्मिनल दिखाई देते हैं।
2. खिलाड़ी द्वारा चयन करने के बाद प्रभाव terminal_unlocked = true सेट करता है।
3. शर्त जहाज की योजना और नए बटन को खोलती है।
4. बटन दबाने से विजेट की स्थिति बदलती है और evidence बढ़ जाता है।
5. जब evidence >= 3, एक छिपा हुआ नोटिफिकेशन प्रकट होता है और क्वेस्ट की नई शाखा उपलब्ध हो जाती है।
अध्याय का नक्शा, सामान्य विकल्प और इंटरैक्टिव इंटरफ़ेस तीन अलग-अलग सिस्टम नहीं बनाते। वे एक ही स्थिति ऑब्जेक्ट को पढ़ते और बदलते हैं। इसलिए संवाद में पाए गए सबूत से इंटरफ़ेस का एक तत्व खुल सकता है, और इंटरफ़ेस के अंदर की क्रिया — आगामी दृश्य को बदल सकती है।
यहीं पर घोषणात्मक तर्क वास्तव में प्रोग्रामिंग के रूप में महसूस होने लगता है। केवल फ़ाइलों, फ़ंक्शनों और इवेंट बस की बजाय, लेखक कहानी की समझने योग्य विशिष्टताओं के साथ काम करता है: तथ्यों, विकल्पों, शर्तों और संक्रमणों के साथ।
मैं जानबूझकर कोई मनमाना JavaScript क्यों नहीं जोड़ता
स्क्रिप्ट डालने की संभावना किसी भी नई यांत्रिकी तक पहुँचने का सबसे छोटा रास्ता लगती है। लेकिन जैसे ही प्रकाशित क्वेस्ट को मनमाना कोड चलाने का अधिकार मिलता है, पूरी उत्पाद मॉडल बदल जाती है।
निर्णय लेना जरूरी है कि इस कोड के लिए कौन-कौन से ब्राउज़र API उपलब्ध हैं, नेटवर्क और स्टोरेज को कैसे सीमित करें, अनंत लूप के साथ क्या करना है, क्वेस्ट को ऑफलाइन कैसे ले जाना है, इसे सह-खेल में कैसे सिंक्रनाइज़ करना है और यह सुनिश्चित कैसे करना है कि पुराना प्रोजेक्ट Player अपडेट के बाद भी काम करता रहे।
घोषणात्मक नियम किसी भी मनमाने फ़ंक्शन की तुलना में बहुत अधिक नीरस है — और यही कारण है कि यह अधिक भरोसेमंद है। इसे लॉन्च से पहले सत्यापित किया जा सकता है। यह समझा जा सकता है कि यह कौन-कौन से वेरिएबल पढ़ता और लिखता है। बीच में प्रगति को सुरक्षित रूप से संरक्षित किया जा सकता है। क्वेस्ट को ब्राउज़र, स्वायत्त निर्माण और एम्बेडेड Player में समान रूप से किया जा सकता है।
यहाँ सीमित शब्दावली तर्क को बाधित नहीं करती, बल्कि इसकी सीमाएँ निर्धारित करती है। यदि कोई वास्तव में उपयोगी ऑपरेशन प्रकट होता है, तो इसे सामान्य runtime में जोड़ना और सभी लेखकों को समान परीक्षण किए गए व्यवहार देना बेहतर है, बजाय इसके कि प्रत्येक परियोजना अपना खुद का इंजन आविष्कार करे।
डेज़र्ट के लिए — अपनी HTML और CSS
इस बीच, Romergo वास्तव में असली कोड के लिए जगह छोड़ देता है वहाँ जहाँ यह सबसे सुरक्षित है: सामान्य दृश्य की सजावट में।
इस परियोजना में आप HTML और CSS पर अपनी खुद की बाहरी दिखावट थीम बना सकते हैं और इसे विभिन्न अध्यायों में उपयोग कर सकते हैं। HTML तैयार किए गए क्षेत्रों की स्थिति निर्धारित करता है — रिप्लाई विंडो, पोर्ट्रेट, पात्र का नाम, पाठ, प्रश्न और चयन बटन। CSS इन क्षेत्रों को काफी हद तक बदलने की अनुमति देता है: फ्रेम, पृष्ठभूमि, कार्ड का आकार, मार्जिन, टाइपोग्राफी और स्क्रीन आकार पर प्रतिक्रिया।
तैयार थीमें **Standard**, **Minimalism**, **Brutalism**, **Romance** और **Neon** बिना मैनुअल लेआउट के पुष्टि की गई सीमा दिखाती हैं। जब कस्टम थीम Romergo बनाई जाती है, तो यह चुने गए टेम्पलेट को कॉपी करता है: इसके बाद इसका HTML और CSS संपादित किया जा सकता है और तुरंत वास्तविक प्रीव्यू Player में देखा जा सकता है।

*यहाँ कोई कॉन्सेप्ट आर्ट नहीं है: बाईं ओर दिखाया गया है असली CSS एम्बेडेड थीम Brutalism का, दाईं ओर — उसी कोड का परिणाम फोन प्रीव्यू में। डेस्कटॉप / मोबाइल स्विच तुरंत एडाप्टिव नियमों की जांच करने की अनुमति देता है, और कस्टम थीम चयनित टेम्पलेट की कॉपी से काम करना शुरू करती है।*
लेकिन सीमा पहले जैसी रहती है। कस्टम HTML में <script>, इवेंट हैंडलर, फॉर्म, iframe और बाहरी संसाधन नहीं हैं। CSS बाहरी url() या @import लोड नहीं कर सकता। सीन के अनिवार्य क्षेत्र वहीं रहने चाहिए; अगर टेम्पलेट उन्हें खो देता है, तो Player सुरक्षित मानक लेआउट पर वापस लौटता है।
यानी HTML और CSS इस बात के लिए जिम्मेदार हैं कि **कहानी कैसे दिखती है**, जबकि नोड्स, वेरिएबल्स, कंडीशन्स और इफेक्ट्स इस बात के लिए कि **यह कैसे काम करती है**।
मुझे यही तरह का विभाजन पसंद है। लेखक किसी क्वेस्ट को विजुअली बाकी से अलग बना सकता है, लेकिन उसका पूरा करना तब भी सामान्य जांच योग्य runtime का हिस्सा रहता है। बाहरी परत को पूरी तरह बदल और पुनर्निर्माण किया जा सकता है, बिना हर विषय को अलग प्रोग्राम में बदलें।
कोड बिना कोड का मतलब लॉजिक की अनुपस्थिति नहीं है
Romergo का उद्देश्य प्रोग्रामिंग भाषा को चित्रों से बदलने का नहीं है। ग्राफ जटिल गणित के लिए उपयुक्त नहीं है, और परमाणु प्रभावों का सेट सार्वभौमिक स्वचालन इंजन नहीं बन पाएगा।
हालाँकि, इंटरैक्टिव कहानी के लिए अक्सर अन्य चीजों की आवश्यकता होती है: तथ्य को याद रखना, काउंटर बदलना, विकल्प खोलना, मार्ग चुनना, इंटरफ़ेस की स्थिति दिखाना, वापसी बिंदु सुरक्षित करना और इन सभी निर्णयों को अगले अध्याय तक पहुँचाना।
इसके लिए नोड्स और वेरिएबल्स स्क्रिप्ट का सरलीकृत संस्करण नहीं होते, बल्कि कहानी की खुद की भाषा का अधिक प्रत्यक्ष रूप होते हैं। लेखक कंप्यूटर को आदेश नहीं देता, बल्कि दुनिया के कारण-परिणाम संबंध को बताता है: खिलाड़ी ने चयन किया, स्थिति बदल गई, कहानी ने प्रतिक्रिया दी।
और अगर थोड़ी असली कोडिंग की इच्छा है, तो HTML और CSS फिर भी मिठाई पर इंतजार कर रहे हैं।