Imperator: Rome’da Modlama ve Kod Yenilikleri

Imperator: Rome geliştirici günlüklerinde bu hafta oyunun modlanabilirliğine dair bilgi veriliyor.

Herkese merhabalar, bir başka Imperator: Rome günlüğüne daha hoş geldiniz.

Bugün biraz daha farklı bir günlükle karşınızdayız. İşin tamamen teknik yönüne odaklanacağımız, modlama odaklı bir günlük olacak.

Her Paradox oyununda olduğu üzere geniş bir trigger, effect ve scope listesine sahibiz. Bu günlükte hepsinden teker teker bahsetmektense Imperator’a has üst seviye mekaniklerden, örneğin yeni script value sisteminden ve modlanabilir gui sisteminden bahsedeceğiz. 

Bu gibi sistemleri en iyi anlatacak kişiler bu sistemleri oluşturan kişiler, dolayısıyla sözü onlara bırakacağım:

Scope

Herkese merhabalar. Ben Matthew Clohessy, Paradox’ta programcı olarak çalışıyorum. Yaklaşık 6 ay önce içerik tasarımcısıydım, beni daha önceleri CK2 mod bölümünde görmüş olabilirsiniz. Geçtiğimiz 1 yıl içerisinde çalışmanın yanı sıra yeni Jomini scriptinde çeşitli geliştirmelerde bulundum.

Jomini Clausewitz motorunu kullanan farklı oyun projelerimiz arasında bir kütüphane görevi görüyor, grand strategy oyunların ortak olarak sahip olduğu oyun durumu, multiplayer, bölgeler ve tabi ki kodlama sistemimiz gibi unsurları barındırıyor.

Bu kısımda sizlere Jomini’deki yeni script sisteminden bahsedeceğim. Jomini’de kullandığımız scope türleri: no scope, value, color ve flag. Her event veya etkileşimin bir top scope’u bulunuyor, burada root, kayıtlı scopelar ve local variable’lar saklanıyor.

Eventlerde hedef terimler aracılığıyla araya noktalar koyarak unsurları çağırabiliyoruz, örneğin root.mother.father. Aralarda noktalar olduğu için tek bir satırda şu şekilde kullanılabilir:

set_character_religion = root.father.mother.religion

Bu kodlar tek bir çıkış türüne sahip çok sayıda girişe sahip olabilir, scope unsuru daha sonraları tanımlanan rastgele bir isme sahip olabilir.

father = { save_scope_as = cool_person }
scope:cool_person = { kill_painfully = yes }

Artık bu şekilde kodlarda father = { character = root.mother.father } gibi kısımlarla iki karakterin aynı karakter olup olmadığını kontrol etmenize gerek yok. Artık direkt olarak father = root.mother.father kodunu kullanabiliyoruz. Bu kısımda >, <, >=, <=, = ve != şeklinde değer karşılaştırmaları da yapılabiliyor. [title type="h2"]Script Listeleri[/title] Script listeleri benzer objeler arasında bir scope'dan diğer scope'a geçişi sağlıyor. Yeni sistemle birlikte bir liste alıp o listeye any_, every_, random_ ve ordered_ şeklinde versiyonlar oluşturuyoruz.

  • Any: Bu trigger listenizdeki herhangi bir unsurun şartları yerine getirip getirmediğini kontrol eder.
  • Every: Bu trigger listedeki her unsurun bir şartı yerine getirip getirmediğini kontrol eder.
  • Random: Bu trigger listedeki herhangi bir rastgele unsurun şartları yerine getirip getirmediğini kontrol eder.
  • Ordered: Bu trigger belli bir sırayla unsurların şartları yerine getirip getirmediğini kontrol eder.

Variable

Variable’lar 3 yerde bulunabilir, scope unsuru (karakter, ülke vs.), top scope içinde veya gamestate dahilinde.

Bu variablelar da scope mantığıyla çalışır, event hedef bağlantısı storage tipine göre farklılık gösterir:

var:name
local_var:name
global_var:name

Bu şekilde efektleri veya triggerları etkileyebilir, sayı değerlerini tespit edip bunlarda değişiklik yapabilirsiniz.

Listeler

Oyunda event scopelarından ve variable’lardan oluşan custom bir liste oluşturmanız mümkün. 

every_character = {
   limit = {
       has_variable = olympic_attendee
   }
   add_to_list = olympic_competitor_list
}
random_in_list = {
   list = olympic_competitor_list
   die_very_painfully = yes
}

Listelerden unsur çıkartabilir veya onların listede olup olmadığını denetleyebilirsiniz.

Belgelendirme

Daha önceleri otomatik belgelendirmeye yönelik bazı denemelerimiz olmuştu, bazı kısımların eskide kalmasından bazı değerlerin direkt yanlış olmasına kadar çeşitli sorunlara rastlanabiliyordu. Artık script belgelendirme konsol komutunun Jomini’ye aktarılmasıyla birlikte games log klasöründe farklı dosyalar içerisinde belgelendirme yapılıyor.

  • Tüm efektler, basit tanımlara sahip bir biçimde kullanılabilecekleri scopelar, bu scope’un bağlantılı olduğu bir listenin olup olmaması.
  • Tüm triggerlar, basit tanımlara sahip bir biçimde kullanılabilecekleri scopelar, bu scope’un bağlantılı olduğu bir listenin olup olmaması.
  • Tüm scope türleri, karakter, ülke, değer vs.
  • Tüm event bağlantıları, kullanılabilecekleri scopelar, çıkış yaptıkları scope ve tanım.
  • Tüm kaydedilmiş scopelar.
  • Tüm modifierlar ve uygulanabilecekleri scopelar örneğin levy_reinforcement_rate.
  • Tüm etkileşimler, koddan mı scriptten mi geldikleri ve beklenen sonuçlar.

GUI & Yerelleştirme Sistemi

Spesifik bir scripting ile çalışan yeni bir GUI sistemine sahibiz, aynı sistem yerelleştirme için de kullanılıyor, bu sisteme Data Sistemi adı veriyoruz. Çalıştırdığınız her unsur ya kod itibariyle registre edilmiş olmalı ya da GUI olarak kodlanmalı.

Bu datayı kullanabileceğiniz 4 farklı kategori mevcut:

  • Türler, koddaki class/struct değerlerine göre şekillenen objeler.
  • Değişimler, bir unsuru bir başka türdeki unsura taşımak.
  • Fonksiyonlar, bir sonucu olan fonksiyonu çağırmak.
  • Geridönüşler, bir sonucu olmayan fonksiyonu çağırmak.

Aklınızda bulunması gereken bir diğer husus ise data sisteminin büyük oranda C++’de const prensiplerine uygun hareket etmesi. Çok fazla teknik bilgiye girmeden, 2. ve 4. maddedeki unsurların sadece const bir biçimde kodlanabileceğini belirtmek mümkün. Bu durum const olmayan bir unsurun çağırılamayacağı anlamına geliyor. 

Kodlanmış GUI

Kodlanmış GUI ile arayüze multiplayerda senkronizasyonu bozmayacak bir biçimde kod ekleyebiliyorsunuz. Bunun için common/scripted_guis klasöründe script oluşturup data verilerinde referans vermeniz gerekiyor.

Örneğin bir başka karakterin altınını çalma butonu:

# common/scripted_guis
cheat_gold_button = {
   scope = character
   saved_scopes = {
       second
   }
   is_shown = { # Can be omitted as always true
       always = yes
   }
   is_valid = {
       gold < 5000
   }
   effect = {
       add_gold = 500
       scope:second = {
           add_gold = -500
       }
   }
}

# in a gui entry
button = {
   name = "my_cheat_button"
   datacontext = "[GetScriptedGui('cheat_gold_button')"
   texture = "gfx/interface/icons/shared_icons/bankruptcy.dds"
   visible = "[ScriptedGui.IsShown( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"
   enabled = "[ScriptedGui.IsValid( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"
   onclick = "[ScriptedGui.Execute( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"
   tooltip = "[ScriptedGui.BuildTooltip( GuiScope.SetRoot( SomeCharacter.MakeScope ).AddScope( SomeOtherCharacter.MakeScope ).End )]"
}

Bu tarz bir buton eklemeniz durumunda yapay zeka bu butonları kullanmıyor, ancak gizli tetikleyici eventler ekleyerek bu tür etkileşimleri değerlendirmelerini sağlayabilirsiniz.

Script Değerleri

Merhabalar ben Magne “Meneth” Skjæran, PDS’te programcı olarak çalışıyorum. Daha önceleri Crusader Kings II’de çalışmıştım, bir süredir üzerinde çalışıyorum, yaklaşık 1 aylığına Imperator üzerinde çalışma fırsatı edindim ve orada çalışmamın bir parçası olarak Jomini’de script matematik sistemi oluşturdum. Jomini kabaca Clausewitz ile oyun arasındaki bir katmanı oluşturuyor ve spesifik bir oyuna has olmayan unsurları üstleniyor.

Bu daha önce bir oyunda yaptığımız bir şey değildi, daha önceleri çok sayıda variable kullanarak benzer bir sonuca ulaşabiliyorduk ancak o da oldukça kısıtlayıcıydı. Bugün sizlere bu sistem hakkında bilgi vereceğim.

Script matematik sistemi değer sistemi üzerine inşa edilen yeni bir sistem. Daha önceki oyunlarımızda bunun farklı versiyonları yer alıyordu, örneğin şu şekilde bir kodun değeri oluyor:

some_value_name = 1000

Bunu da sonra şu şekilde kullanıyorsunuz:

add_gold = some_value_name

Eski oyunlarımızda bunun yapılabilirliği bazı alanlarda zorluk gösterebiliyordu. Jomini ile birlikte neredeyse değer girebildiğiniz her yerde bunu yapabilmeniz mümkün. Jomini oyunlarında bu değerler illa ki sayı bazlı da olmak zorunda değil, örneğin şöyle bir şey yapabilirsiniz:

add_gold = scope:some_country.gold # Adds as much gold as "some_country" has

Matematik İşlemleri

Script matematik sistemiyle birlikte kod üzerinden matematik işlemleri yapabiliyorsunuz:

some_value_name = {
   value = scope:some_country.gold
   add = 50
   multiply = 100
}

Bu da “bir ülkenin” altınının 50 fazlasının 100’le çarpışıyla sonuçlanıyor.

Bu şekilde aşağıdaki işlemleri yapabilmeniz mümkün:

  • value = Bir değer oluşturur.
  • add = Mevcut değere ekleme yapar.
  • subtract = Mevcut değerden çıkarır.
  • multiply = Mevcut değerle çarpar.
  • divide = Mevcut değeri böler.
  • modulo = Bölme sonrası kalan değeri alır.
  • min = Düşük olması durumunda mevcut değeri bu değere yükseltir.
  • max = Fazla olması durumunda mevcut değeri bu değere indirir.
  • floor = yes – Mevcut değeri yuvarlar.
  • ceiling = yes – Mevcut değeri yukarıya tamamlar.
  • round = yes – Mevcut değeri en yakın tamsayıya yuvarlar.

Görebileceğiniz üzere bu işlemlerle kompleks matematik değerleri oluşturmanız mümkün.

Inlining

Bu sistemi bir adım ileriye taşımak adına değerin isimle elde edilmesinin (add_gold = some_value_name) yanı sıra inlining ile de elde edilebilmesini sağladık. Dolayısıyla şu şekilde kodlama yapılması mümkün:

add_gold = {
   value = scope:some_country.gold
   add = 50
   multiply = 100
}

Bu durum kodun tek bir yerde kullanıldığında oldukça kolaylık sağlıyor, matematik işlemi içerisinde de bunu kullanabiliyorsunuz, örneğin altın * (prestij + 50) hesabı yapmak istiyorsanız:

add_gold = {
   value = gold
   multiply = {
       value = prestige
       add = 50
   }
}

Bu şekilde açabileceğiniz parantezlerin herhangi bir limiti yok.

Koşullu Mantık

Matematik işlemlerinin yanı sıra koşullarla da kodlama yapabilirsiniz. Örneğin bir ülkenin spesifik bir innovation’a sahip olması durumunda daha fazla ödül vermek istiyorsanız:

add_gold = {
   value = 100
   if = {
       limit = { has_innovation = some_innovation }
       multiply = 3
   }
   else_if = {
       limit = { has_innovation = some_other_innovation }
       multiply = 2
   }
}

Bu durumda eğer ülke innovation’a sahipse 300 altın, bir başka innovation’a sahipse 200, hiçbirine sahip değilse 100 altın elde ediyor.

Değer Menzili

Kodlama esnasında bir değer menzili oluşturup o menzil içerisinde rastgele bir değerin seçilmesini sağlayabilirsiniz. Bunu yapabileceğiniz iki yöntem var, birincisi:

add_gold = { 10 100 }

Bu kod 10 ila 100 arasında bir altın elde edileceğini ifade ediyor.

Bunu aynı zamanda şu şekilde de yapabilirsiniz:

add_gold = { some_value some_other_value }

Bu synthax’ta inlining açmanız mümkün değil, bunun için kullanabileceğiniz 2 kod var integer_range ve fixed_range.

integer_range menzil içerisinde tam sayılardan birini seçerken (1, 2, 3), fixed_range’de küsüratlı rakam görebilirsiniz (0.1, 0.2, 0.3)

Bunun bir örneği:

add_gold = {
   integer_range = {
       min = { value = gold multiply = 2 }
       max = { value = gold multiply = 10 }
   }
}

Bu koda göre ülke mevcut altın değerinin 2 katıyla 10 katı arasında bir altın elde edecek.

Listeler

Aynı zamanda işlem listelerini de destekliyoruz, bu şekilde listenin üyesi olan tüm unsurlarla tek seferde işlem yapabiliyorsunuz. Örneğin aşağıda tüm vassallarınıza altın ekleme kodu mevcut:

add_gold = {
   every_subject= {
       add = gold
   }
}

Burada dilerseniz scope’u değiştirip, örneğin vassalı olduğunuz ülkenin tüm vassallarına altın vermeyi kodlayabilirsiniz:

add_gold = {
   overlord = {
       every_subject = {
           add = gold
       }
   }
}

Gördüğünüz üzere bu sistem birçok kodlamayı çok daha basit hale getiriyor, bu durumlar daha önceki oyunlarımızda ya çok zor kodlanıyordu ya da kodlanamıyordu.

Bu sistem Imperator’da yoğun bir biçimde kullanıldı, modcuların da bu sistemle neler yapabileceğini merakla takip ediyoruz.

Bu şekilde bir geliştirici günlüğünün daha sonuna geldik. Ne yazık ki kodlardan bahsederken sizlerle paylaşabileceğimiz güzel ekran görüntüleri olamıyor, bu sebeple oyun sona erdiğinde karşılaşabileceğiniz ekranlardan birisini sizlerle paylaşıyoruz. Bu ekranın görüntüsü ve karşınıza çıkan metin oyununuzun gidişatına göre değişiklik gösteriyor, bu ekran devclash kaydında Baktriya ile oynadığım ülkenin oyun sonu ekranı. Ne yazık ki oyun pek bir şey başaramadığımızı belirtmiş. 

Yazar: Ali Alper Duman

Yayın Direktörü @ Strategyturk

Yorumla

STRATEGYTURK

Strategyturk'te strateji oyunlarından haberleri, yama notlarını, geliştirici günlüklerini ve daha birçok içeriği Türk strateji oyuncularına Türkçe bir biçimde sunuyoruz. Aynı zamanda yeni çıkan strateji oyunlarının ve eklentilerinin incelemelerini yapıyor, bu oyunlara dair sürekli olarak içerik oluşturuyoruz.

Sosyal medya sayfalarımızı takip ederek strateji oyunlarındaki gelişmelerden haberdar olabilirsiniz.

STRATEGYTURK TWITTER

Humankind'da Yeni Bir Serüven #humankind https://t.co/v0AyOE7s85
Hearts of Iron IV'te Tatilden Dönüş #hoi4 #heartsofiron4 https://t.co/Y42PJs53dw

Strategyturk Flickr