კვესტის არქიტექტურა

სკრიპტების გარეშე, მაგრამ ლოგიკით: როგორ მუშაობს ნოდები და ცვლადები Romergo-ში

როდესაც თამაშების რედაქტორში ლოგიკაზე ლაპარაკობენ, ჩვეულებრივ ძალიან მალე მიდიან სკრიპტებზე. სადღაც ავტორი წერს JavaScript, სადღაც Python, ხოლო სადღაც სწავლობს ძრავის საკუთარ ენას. ეს იძლევა თითქმის ულიმიტო თავისუფლებას, მაგრამ ამას თან ახლავს სინტაქსური შეცდომები, გაჩერებული ციკლები, შეუთავსებელი პლაგინები და კოდი, რომლის გახსნაც ექვსი დღის შემდეგ მისი ავტორისთვის კი საშინელებაა.

Romergo-ში სხვა გზა ავირჩიე. კვესტში თვითნებური სკრიპტები არ არსებობს. ფუნქციის ჩასმა, ქსელური მოთხოვნის გაგზავნა ან Player-ზე პირდაპირი წვდომა შეუძლებელია. ამის ნაცვლად, ისტორია იქმნება შეზღუდული, მაგრამ საკმარისად მოქნილი ნაკრებით: ლოგიკური ნოდებით, ცვლადებით, პირობებითა და ეფექტებით.

ეს არ ნიშნავს, რომ Romergo-ში მხოლოდ მარტივი დარღვევები შეიძლება. შესაძლებელია მოთამაშის გადაწყვეტილებების გახსოვნა, ქულების თვლა, ვარიანტების გახსნა და დამალვა, შემდეგი თავების სხვადასხვა მარშრუტების არჩევა, შემთხვევითი მოვლენების შექმნა და ინტერაქტიული ინტერფეისების შექმნა. უბრალოდ, ბრძანების „ეს კოდი შესრულდეს“ ნაცვლად, ავტორი აღწერს „ასეთ მდგომარეობაში უნდა მოხდეს ეს მოვლენა“.

გრაფი უკვე პროგრამაა

ყველაზე ბაზისური ლოგიკა Romergo პირდაპირ თავკაცის რუკაზეა. სცენები დაკავშირებულია გადასვლებით, ხოლო სპეციალური ნოდები წყვეტენ, სად გაგრძელდება გატარება შემდეგ.

თუ ძალიან შევამარტივებთ, პატარა კვესტი ასე გამოიყურება:

სტარტი
  ↓
ურედიმებლისთან საუბარი
  ↓
გასასვლელი გაქვს?
  ├─ დიახ → დახურული არქივი
  └─ არა → ალტერნატიული გზა

ეს უკვე შესრულებადი ლოგიკა არის. Player შედის საწყის კვანძში, აჩვენებს სცენას, კითხულობს ისტორიის მდგომარეობას და ირჩევს შესაბამის გადასვლას. ავტორი ხედავს ერთსა და იმავე მარშრუტს როგორც გასაგებ რუკას, და არა როგორც if, goto და სამსახურის აიდენთიფიკატორთა ნაკრებს.

![Flow Romergo: Final Accusation გადის Switch და სამი If / Else-ით სხვადასხვა დასასრულიებისკენ, ქვემოთ ღიაა Logic პანელი](../assets/logic-without-scripts-flow-nodes.webp)

*რეალური Flow-ის ფრაგმენტი Builder-ში: სცენა Final Accusation გადასცემს გადაწყვეტილებას Switch-ს, შემდეგ რამდენიმე If / Else შეამოწმებს მდგომარეობას და აყალიბებს ისტორიას სხვადასხვა დასასრულებისაკენ. Logic-ის ღია პანელი ძირში აჩვენებს ყველა ხელმისაწვდომ ნაკრებს: Dead End, Checkpoint, If / Else, Switch, Random და Teleport.*

ლოგიკის პანელში ახლა ექვსი ძირითადი ნოდია:

სტარტი და ფინიში ჩაეკეტება თავით, ჩვეულებრივი სცენები აჩვენებს შინაარსს, ხოლო ლოგიკური ნოდები აქცევს მათ მარშრუტად. უკვე ამ დონეზე შეიძლება შეაგროვოთ ხაზოვანი ისტორია, განშტოება, რამდენიმე დასასრული, შემთხვევითი მოვლენა და უსაფრთხო დაბრუნების წერტილი — ერთ-ერთი სკრიპტის ხაზის გარეშე.

მონაკვეთი — ეს არის ისტორიის მეხსიერება

ერთ გრაფიკი საკმარისი არაა, თუ ისტორია უნდა გაიხსენოს, რა მოხდა ადრე. ამისათვის კვესტში არსებობს სამი მარტივი ტიპის მონაკვეთი:

ყოველ ცვლადს აქვს გასაღები, ავტორს გასაგები წარწერა და საწყისი მნიშვნელობა. რიცხვითი ცვლადი შეიძლება დამატებით დაახასიათოთ მინიმუმით და მაქსიმუმით, ხოლო მომსახურებითი ცვლადი — დამალოთ მოთამაშის სტატუსის პანელიდან ან აჩვენოთ მხოლოდ პირობის შესრულებისას.

აქ მთავარი ის არის, რომ ცვლადები მთელი კვესტის გავლას ეკუთვნის და არა ერთ სცენას. თუ მოთამაშემ პირველ თავში საშვი მიიღო, მეოთხე თავში იმავე დროშის შემოწმება შეიძლება. Finish-ის გავლით შემდეგ თავში გადასვლისას მდგომარეობა არ ნულდება. ის ინახება არჩეულ ვარიანტებთან, გამოყენებულ ეფექტებთან, Checkpoint-თან და შემთხვევითობის მიმდინარე ნაბიჯთან ერთად.

ის გამოდის როგორც მარტივი ავტორული ციკლი:

მოთამაშის არჩევანი გეგმავს მნიშვნელობას
              ↓
ცვლადი ინახავს მდგომარეობას
              ↓
პირობა წაიკითხავს მას მოგვიანებით
              ↓
გრაფი, სცენა ან ინტერფეისი რეაგირებს

მაგალითად, პირველ სცენაში არჩევანს „მიჩვენე ნაპოვნი ფოტო“ შეუძლია portrait_revealed = true დააფიქსიროს. მოგვიანებით, **თუ / სხვა შემთხვევაში** ნოდი ამ დროშას შეამოწმებს და საუბრის ახალ სცენას გახსნის. კიდევ მოგვიანებით, ტერმინალის ინტერფეისი დამატებით ჩანაწერს მხოლოდ იმ მოთამაშეებს აჩვენებს, რომლებმაც ფოტო გამოავლინეს.

ერთი ცვლადი აკავშირებს მის კვეისთვის სამ სხვადასხვა ნაწილს. ამისთვის არ არის საჭირო მნიშვნელობის ხელით გადაცემა სცენებსა ან თავებს შორის: ისინი იყენებენ ერთ საერთო პროცესის მდგომარეობას.

![ცვლად route გვერდი აჩვენებს გასაღებს, სახელწოდებას, ტიპს და მდგომარეობის ხილვადობას](../assets/logic-without-scripts-variables.webp)

*ცვლად route–ს ერთ ბარათში შეყოვნებულია სტაბილური გასაღები, გასაგები სახელწოდება, მნიშვნელობის ტიპი და მდგომარეობის ხილვადობა. არჩევანებთან და შტოებთან დაკავშირება ნაჩვენებია იგივე გვერდის ქვედა ნაწილში.*

არჩენები წერენ, შტოები კითხულობენ

საშუალო სცენარში მდგომარეობის შეცვლის ყველაზე მარტივი გზა არის ეფექტის დამატება არჩევანის ვარიანტს. ვიზუალურ რედაქტორში ეს ასე გამოიყურება, როგორც მინიჭება:

«დაუჯერე მარის» → ally = "mary"
«წადი მარტო»     → ally = "none"

ამის შემდეგ Switch-ის შეუძლია მოთამაშე მისი ally მნიშვნელობის მიხედვით საჭირო სცენაში გზავნოს. ცვლადების გვერდი ცალსახად აჩვენებს, რომელი არჩევანები ყალიბდება ცვლადში და რომელი განტოლებები კითხულობენ მას. ეს პატარა დეტალია, მაგრამ დიდ საკვესტო პროექტში იგი ამცირებს ათეულობით სცენის კარეტის ძიებას.

![Switch პარამეტრები Laboratory და Habitat შტოებით, რომლებიც ათვალსიწნებენ არჩეული მარშრუტის ცვალებელს](../assets/logic-without-scripts-switch-rules.webp)

*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-ით ჩაწერეთ ახალი მნიშვნელობა. ინტერფეისის სცენების გაფართოებულ ლოგიკაში პირობები და ეფექტები შემოწმებადი სტრუქტურების სახით შეიძლება განისაზღვროს. მაგალითად, ტერმინალის მდგომარეობებს შორის გადასვლამ შეიძლება ერთდროულად დაიმახსოვროს გადაწყვეტილება, დაამატოს მტკიცებულება და ჩართოს განგაში:

![სცენის რედაქტორი Romergo: სამი არჩევანის ვარიანტი წერს lab, habitat და server ცვლადში route](../assets/logic-without-scripts-choice-effects.webp)

*ეფექტი პირდაპირ პასუხის ვარიანტზეა მიმაგრებული: სამი არჩევანი შეინახავს 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 წინასწარ ნახვაში.

![რეალური დიზაინის რედაქტორი Romergo: CSS Brutalism თემებისა და მობილური დიალოგის წინასწარი ნახვისთვის](../assets/logic-without-scripts-custom-html-css.webp)

*აქ არ არის კონცეპტუალური არტი: მარცხნივ ნაჩვენებია რეალური CSS ჩაშენებული თემა Brutalism, მარჯვნივ — იგივე კოდის შედეგი ტელეფონის წინასწარ դիտების რეჟიმში. Desktop / Mobile გადამრთველი საშუალებას იძლევა დაუყოვნებლად გადამოწმოთ ადაპტიური წესები, ხოლო მომხმარებლის თემა იწყებს მუშაობას არჩეული შაბლონის ასლიდან.*

მაგრამ საზღვარი უცვლელია. მომხმარებლის HTML არ შეიცავს <script>, მოვლენების დამუშავებლებს, ფორმებს, iframe-სა და გარე რესურსებს. CSS ვერ შეგიძლიათ ჩატვირთოს გარე url() ან @import. სცენის აუცილებელი ადგილები უნდა დარჩეს ადგილას; თუ შაბლონმა ისინი დაკარგა, Player უბრუნდება უსაფრთხო სტანდარტულ ფორმატირებას.

ეს ნიშნავს, რომ HTML და CSS პასუხისმგებელი არიან იმაზე, **როგორ გამოიყურება ისტორია**, ხოლო ნოდები, ვარიაბლები, პირობები და ეფექტები — იმაზე, **როგორ მუშაობს იგი**.

მესმის ასეთი გაყოფა. ავტორი შეუძლია შექმნას მისთვის ვიზუალურად უნიკალური კვანძი, მაგრამ მისი გავლა მაინც რჩება საერთო შემოწმებადი runtime-ის ნაწილად. გარე დაფარს შეიძლება უკეთესად გადაღებოთ და გადარქმიოთ, ისე რომ თითოეული თემა ცალკე პროგრამად არ გადაიქცეს.

კოდი კოდის გარეშე — ეს არ ნიშნავს ლოგიკის არარსებობას

Romergo-ის მიზანი არ არის პროგრამირების ენას სურათებით შეეცვალოს. გრაფი ცუდად ერგება რთულ მათემატიკას, ხოლო ატომური ეფექტების კომპლექტი უნივერსალურ ავტომატიზაციის ძრავად ვერ იქცევა.

თუმცა ინტერაქტიული ისტორიებისთვის ხშირად სხვა რამეებია საჭირო: ფაქტის შეხსენება, კაუნტერის შეცვლა, ვარიანტის გახსნა, მარშრუტის არჩევა, ინტერფეისის მდგომარეობის ჩვენება, დაბრუნების წერტილის შენახვა და ყველა ამ გადაწყვეტილების გადაცემა შემდეგ თავში.

ამ მიზნით, Nodes და ვარიბლები აღმოჩნდებიან არა სკრიპტების გამარტივებული ვერსიები, არამედ თვით ისტორიის უფრო პირდაპირი ენა. ავტორი აღწერს არა კომპიუტერისთვის ბრძანებებს, არამედ სამყაროს მიზეზობრივი კავშირს: მოთამაშემ აირჩია, მდგომარეობა შეიცვალა, ისტორია რეაგირებს.

და თუ გინდა ცოტა რეალური კოდი, HTML და CSS მაინც გელოდებიან სადილად.

დაბრუნება dev-ბლოგში