249 lines
15 KiB
Python
249 lines
15 KiB
Python
"""
|
||
Örnek dünya: 15 lokasyon + 21 yol + lokasyon kaynakları + 12 D&D olayı.
|
||
Idempotent (code üzerinden upsert). Harita koordinatları 0-1000 (statik görsel için).
|
||
Yeni ham maddeler (13 odun, cevherler, otlar, balıklar, av) lokasyonlara tier'a göre dağıtılır.
|
||
Çalıştırma: cd /opt/stepstead && ./venv/bin/python3 db/seed_world_graph.py
|
||
"""
|
||
import asyncio
|
||
import json
|
||
import sys
|
||
|
||
import asyncpg
|
||
|
||
sys.path.insert(0, "/opt/stepstead")
|
||
from config import settings
|
||
|
||
DSN = settings.DATABASE_URL.replace("postgresql+asyncpg://", "postgresql://")
|
||
|
||
# (code, ad, tür, açıklama, x, y)
|
||
LOCATIONS = [
|
||
("koy", "Stepstead Köyü", "village", "Yolculuğun başladığı huzurlu köy. Etrafta taş, sopa ve sarmaşık bulunur.", 163, 186),
|
||
("pazar", "Pazarköy", "city", "Tüccarların uğrak yeri, canlı bir kasaba.", 232, 392),
|
||
("liman", "Liman Kenti", "city", "Büyük gemilerin yanaştığı ticaret limanı. Derin deniz balıkçılığı buradan yapılır.", 140, 630),
|
||
("fisilti_ormani", "Fısıltı Ormanı", "forest", "Köye en yakın orman. Meşe ve çam ağaçları.", 205, 110),
|
||
("gumus_camlik", "Gümüş Çamlık", "forest", "Sisli tepelerde huş ve kayın ağaçları.", 110, 300),
|
||
("akca_koru", "Akça Koru", "forest", "Akçaağaç ve söğütleriyle ünlü sakin koru.", 282, 255),
|
||
("kadim_orman", "Kadim Orman", "forest", "Yüzyıllık dişbudak, sedir ve ceviz ağaçları.", 320, 468),
|
||
("karanlik_orman", "Karaağaç Karanlığı", "forest", "En değerli ağaçların büyüdüğü tekinsiz orman: porsuk, maun, abanoz...", 262, 600),
|
||
("tas_ocagi", "Taş Ocağı", "mine", "Bakır ve kalay damarları, bol kömür.", 610, 290),
|
||
("derin_maden", "Derin Maden", "mine", "Demir ve gümüş çıkarılan eski maden.", 750, 320),
|
||
("ejder_dagi", "Ejder Dağı", "mine", "Altın damarlarının saklandığı efsanevi zirve.", 790, 115),
|
||
("aynali_gol", "Aynalı Göl", "lake", "Sazan ve alabalığın bol olduğu berrak göl.", 723, 655),
|
||
("firtina_kiyisi", "Fırtına Kıyısı", "lake", "Dalgalı denizde uskumru, palamut ve lüfer.", 685, 905),
|
||
("cayirlar", "Uçsuz Çayırlar", "meadow", "Şifalı otların yetiştiği rüzgârlı çayırlar.", 590, 760),
|
||
("av_topraklari", "Av Toprakları", "hunting", "Tavşan ve geyik avlanır. Daha derinde kurt vadisi.", 855, 712),
|
||
]
|
||
|
||
# (a, b, adım, olay_şansı)
|
||
ROADS = [
|
||
("koy", "fisilti_ormani", 320, 0.30),
|
||
("koy", "tas_ocagi", 350, 0.30),
|
||
("koy", "aynali_gol", 380, 0.25),
|
||
("koy", "cayirlar", 400, 0.30),
|
||
("koy", "pazar", 450, 0.20),
|
||
("fisilti_ormani", "gumus_camlik", 400, 0.35),
|
||
("gumus_camlik", "akca_koru", 450, 0.35),
|
||
("akca_koru", "kadim_orman", 500, 0.40),
|
||
("kadim_orman", "karanlik_orman", 600, 0.50),
|
||
("gumus_camlik", "av_topraklari", 500, 0.40),
|
||
("av_topraklari", "karanlik_orman", 650, 0.50),
|
||
("tas_ocagi", "derin_maden", 450, 0.35),
|
||
("derin_maden", "ejder_dagi", 600, 0.45),
|
||
("aynali_gol", "pazar", 300, 0.20),
|
||
("pazar", "liman", 500, 0.25),
|
||
("liman", "firtina_kiyisi", 250, 0.25),
|
||
("aynali_gol", "firtina_kiyisi", 550, 0.35),
|
||
("cayirlar", "av_topraklari", 450, 0.35),
|
||
("cayirlar", "gumus_camlik", 350, 0.30),
|
||
("derin_maden", "liman", 550, 0.30),
|
||
("cayirlar", "tas_ocagi", 300, 0.25),
|
||
]
|
||
|
||
# lokasyon → [(item_code, skill, adım, xp, cooldown_s)]
|
||
RESOURCES = {
|
||
"koy": [
|
||
("stick", "foraging", 4, 3, 120), ("stone_small", "foraging", 5, 3, 120),
|
||
("vine", "foraging", 6, 4, 120), ("herb_mint", "foraging", 8, 6, 180),
|
||
],
|
||
"fisilti_ormani": [
|
||
("log_oak", "woodcutting", 12, 8, 240), ("log_pine", "woodcutting", 14, 10, 240),
|
||
("stick", "foraging", 4, 3, 120), ("vine", "foraging", 6, 4, 120),
|
||
("herb_sage", "foraging", 9, 7, 180),
|
||
],
|
||
"gumus_camlik": [
|
||
("log_birch", "woodcutting", 16, 12, 240), ("log_beech", "woodcutting", 18, 14, 240),
|
||
("herb_thyme", "foraging", 10, 8, 180), ("stick", "foraging", 4, 3, 120),
|
||
],
|
||
"akca_koru": [
|
||
("log_maple", "woodcutting", 20, 16, 300), ("log_willow", "woodcutting", 22, 18, 300),
|
||
("herb_chamomile", "foraging", 11, 9, 180), ("vine", "foraging", 6, 4, 120),
|
||
],
|
||
"kadim_orman": [
|
||
("log_ash", "woodcutting", 24, 20, 300), ("log_cedar", "woodcutting", 26, 22, 300),
|
||
("log_walnut", "woodcutting", 28, 24, 300), ("herb_nettle", "foraging", 12, 10, 180),
|
||
],
|
||
"karanlik_orman": [
|
||
("log_cherry", "woodcutting", 30, 26, 360), ("log_yew", "woodcutting", 34, 30, 360),
|
||
("log_mahogany", "woodcutting", 38, 34, 360), ("log_ebony", "woodcutting", 42, 38, 360),
|
||
("herb_ginseng", "foraging", 16, 14, 240), ("herb_saffron", "foraging", 18, 16, 240),
|
||
],
|
||
"tas_ocagi": [
|
||
("ore_copper", "mining", 15, 10, 240), ("ore_tin", "mining", 15, 10, 240),
|
||
("coal", "mining", 12, 8, 240), ("stone_small", "foraging", 5, 3, 120),
|
||
],
|
||
"derin_maden": [
|
||
("ore_iron", "mining", 20, 16, 300), ("coal", "mining", 12, 8, 240),
|
||
("ore_silver", "mining", 26, 22, 300),
|
||
],
|
||
"ejder_dagi": [
|
||
("ore_gold", "mining", 34, 30, 360), ("ore_silver", "mining", 26, 22, 300),
|
||
("ore_iron", "mining", 20, 16, 300),
|
||
],
|
||
"aynali_gol": [
|
||
("fish_carp", "fishing", 14, 9, 240), ("fish_perch", "fishing", 15, 10, 240),
|
||
("fish_trout", "fishing", 18, 14, 240), ("vine", "foraging", 6, 4, 120),
|
||
],
|
||
"firtina_kiyisi": [
|
||
("fish_anchovy", "fishing", 14, 9, 240), ("fish_mackerel", "fishing", 16, 12, 240),
|
||
("fish_bonito", "fishing", 18, 14, 240), ("fish_bluefish", "fishing", 20, 16, 240),
|
||
("fish_bream", "fishing", 20, 16, 240),
|
||
],
|
||
"liman": [
|
||
("fish_pike", "fishing", 24, 20, 300), ("fish_catfish", "fishing", 26, 22, 300),
|
||
("fish_salmon", "fishing", 28, 24, 300), ("fish_turbot", "fishing", 30, 26, 300),
|
||
("fish_tuna", "fishing", 34, 30, 360), ("fish_sturgeon", "fishing", 38, 34, 360),
|
||
],
|
||
"cayirlar": [
|
||
("herb_rosemary", "foraging", 10, 8, 180), ("herb_basil", "foraging", 10, 8, 180),
|
||
("herb_lemonbalm", "foraging", 12, 10, 180), ("herb_fennel", "foraging", 12, 10, 180),
|
||
("herb_lavender", "foraging", 14, 12, 180), ("stick", "foraging", 4, 3, 120),
|
||
],
|
||
"av_topraklari": [
|
||
("meat_rabbit", "hunting", 16, 12, 300), ("leather_rabbit", "hunting", 16, 12, 300),
|
||
("meat_deer", "hunting", 20, 16, 300), ("leather_deer", "hunting", 20, 16, 300),
|
||
("meat_boar", "hunting", 24, 20, 360), ("leather_boar", "hunting", 24, 20, 360),
|
||
("meat_wolf", "hunting", 28, 24, 360), ("leather_wolf", "hunting", 28, 24, 360),
|
||
("meat_bear", "hunting", 34, 30, 420), ("leather_bear", "hunting", 34, 30, 420),
|
||
],
|
||
"pazar": [
|
||
("herb_mint", "foraging", 8, 6, 180), ("stick", "foraging", 4, 3, 120),
|
||
],
|
||
}
|
||
|
||
# D&D olay kartları. choices: skill check'li {text, skill, dc, success{}, fail{}} ya da düz {text, outcome{}}
|
||
EVENTS = [
|
||
("gezgin_tacir", "Gezgin Tacir", "Yolda rengarenk arabasıyla bir tacirle karşılaşıyorsun. Sana el sallıyor.", [
|
||
{"text": "Selam ver ve sohbet et", "outcome": {"text": "Tacir sohbetten memnun, sana bir nane çayı ikram ediyor.", "items": [{"code": "tea_mint", "qty": 1}]}},
|
||
{"text": "Başını öne eğip yoluna devam et", "outcome": {"text": "Tacir omuz silkip uzaklaşıyor."}},
|
||
]),
|
||
("haydut_pususu", "Haydut Pususu", "Çalıların arasından iki haydut fırlıyor! 'Adımlarını ya da canını!'", [
|
||
{"text": "Savaş!", "skill": "combat", "dc": 12,
|
||
"success": {"text": "Haydutları püskürttün! Kaçarken düşürdükleri keseyi aldın.", "items": [{"code": "stone_small", "qty": 3}], "xp": {"skill": "combat", "amount": 25}},
|
||
"fail": {"text": "Dayak yedin ve kaçtın. Yorgunluktan adımların eridi.", "steps": -150}},
|
||
{"text": "Kaçmayı dene", "skill": "hunting", "dc": 10,
|
||
"success": {"text": "Çevik adımlarla izini kaybettirdin.", "xp": {"skill": "hunting", "amount": 15}},
|
||
"fail": {"text": "Yakalandın, keseni silkelediler.", "steps": -100}},
|
||
{"text": "Adımlarından 100 ver", "outcome": {"text": "Haydutlar parayı alıp gözden kayboldu.", "steps": -100}},
|
||
]),
|
||
("yarali_gezgin", "Yaralı Gezgin", "Yol kenarında ayağı burkulmuş bir gezgin oturuyor, yardım istiyor.", [
|
||
{"text": "Yarasını sar (Simya)", "skill": "alchemy", "dc": 8,
|
||
"success": {"text": "Gezgin minnetle teşekkür edip çantasından ot uzatıyor.", "items": [{"code": "herb_mint", "qty": 2}], "xp": {"skill": "alchemy", "amount": 20}},
|
||
"fail": {"text": "Elinden geleni yaptın ama pek işe yaramadı. Zaman kaybettin.", "steps": -30}},
|
||
{"text": "Görmezden gel", "outcome": {"text": "Vicdanın sızlasa da yoluna devam ettin."}},
|
||
]),
|
||
("kayip_sandik", "Kayıp Sandık", "Patikanın kenarında yarı gömülü eski bir sandık görüyorsun.", [
|
||
{"text": "Kazıp aç", "outcome": {"text": "İçinden işe yarar malzemeler çıktı!", "items": [{"code": "stone_small", "qty": 2}, {"code": "vine", "qty": 2}]}},
|
||
{"text": "Tuzak olabilir, dokunma", "outcome": {"text": "Temkinli davranıp uzaklaştın."}},
|
||
]),
|
||
("dev_ayi", "Dev Ayı", "Önünde iki ayağı üzerinde doğrulmuş kocaman bir ayı! Homurtusu yeri titretiyor.", [
|
||
{"text": "Savaş!", "skill": "combat", "dc": 15,
|
||
"success": {"text": "Zorlu bir mücadele sonunda ayıyı alt ettin!", "items": [{"code": "meat_bear", "qty": 1}, {"code": "leather_bear", "qty": 1}], "xp": {"skill": "combat", "amount": 40}},
|
||
"fail": {"text": "Ayı seni fena hırpaladı. Canını zor kurtardın.", "steps": -200}},
|
||
{"text": "Yavaşça geri çekil", "skill": "hunting", "dc": 8,
|
||
"success": {"text": "Ses çıkarmadan uzaklaştın.", "xp": {"skill": "hunting", "amount": 10}},
|
||
"fail": {"text": "Bir dala bastın! Ayı kovaladı, koşmaktan tükendin.", "steps": -120}},
|
||
]),
|
||
("mantar_halkasi", "Mantar Halkası", "Bir açıklıkta mükemmel daire çizen mantarlar... Ortasında bir şey parlıyor.", [
|
||
{"text": "Dikkatlice topla (Toplama)", "skill": "foraging", "dc": 10,
|
||
"success": {"text": "Halkanın ortasından nadir otlar topladın!", "items": [{"code": "herb_chamomile", "qty": 2}], "xp": {"skill": "foraging", "amount": 20}},
|
||
"fail": {"text": "Mantarlar zehirliymiş, başın döndü.", "steps": -60}},
|
||
{"text": "Uğursuzluktur, dokunma", "outcome": {"text": "Halkaya saygıyla selam verip geçtin."}},
|
||
]),
|
||
("eski_harabe", "Eski Harabe", "Sarmaşıklarla kaplı yıkık bir taş yapı. İçinden serin bir esinti geliyor.", [
|
||
{"text": "İçini araştır (İnşaat)", "skill": "construction", "dc": 12,
|
||
"success": {"text": "Sağlam taşlar ve eski aletler buldun!", "items": [{"code": "stone_small", "qty": 4}], "xp": {"skill": "construction", "amount": 25}},
|
||
"fail": {"text": "Bir taş kaydı, az daha altında kalıyordun. Hızla çıktın.", "steps": -80}},
|
||
{"text": "Dışarıdan bak, devam et", "outcome": {"text": "Harabeyi haritana not edip ilerledin."}},
|
||
]),
|
||
("nehir_gecidi", "Nehir Geçidi", "Yol taşkın bir dereyle kesilmiş. Sığ bir geçit var ama akıntı hızlı.", [
|
||
{"text": "Geçerken balık da yakala (Balıkçılık)", "skill": "fishing", "dc": 8,
|
||
"success": {"text": "Hem karşıya geçtin hem eline bir sazan takıldı!", "items": [{"code": "fish_carp", "qty": 1}], "xp": {"skill": "fishing", "amount": 15}},
|
||
"fail": {"text": "Ayağın kaydı, sırılsıklam oldun.", "steps": -40}},
|
||
{"text": "Uzun ama güvenli yoldan dolaş", "outcome": {"text": "Ekstra yürüdün ama kuru kaldın.", "steps": -50}},
|
||
]),
|
||
("gizemli_yasli", "Gizemli Yaşlı", "Bastonlu bir ihtiyar taşın üstünde oturmuş, sana bakıp gülümsüyor: 'Otların dilinden anlar mısın evlat?'", [
|
||
{"text": "Yanına otur ve dinle", "outcome": {"text": "Sana otların sırlarını anlattı. Bilgeleştin.", "xp": {"skill": "alchemy", "amount": 20}}},
|
||
{"text": "Kibarca reddet", "outcome": {"text": "İhtiyar gülümseyip gözlerini kapadı."}},
|
||
]),
|
||
("kirik_araba", "Kırık Araba", "Bir çiftçinin arabasının tekeri kırılmış, yükü yola saçılmış.", [
|
||
{"text": "Tamir et (İnşaat)", "skill": "construction", "dc": 10,
|
||
"success": {"text": "Tekeri taktın! Çiftçi teşekkür olarak sarmaşık ipi verdi.", "items": [{"code": "vine", "qty": 3}], "xp": {"skill": "construction", "amount": 20}},
|
||
"fail": {"text": "Uğraştın ama beceremedin. Çiftçi yine de teşekkür etti.", "steps": -30}},
|
||
{"text": "Yardım edemem, acelem var", "outcome": {"text": "Çiftçi arkandan söylendi."}},
|
||
]),
|
||
("saganak", "Sağanak", "Gökyüzü kararıyor, bardaktan boşanırcasına yağmur başlıyor!", [
|
||
{"text": "Hızlıca sığınak yap (İnşaat)", "skill": "construction", "dc": 8,
|
||
"success": {"text": "Dallardan derme çatma ama işlevsel bir sığınak! Yağmuru kuru geçirdin.", "xp": {"skill": "construction", "amount": 15}},
|
||
"fail": {"text": "Sığınak çöktü, iliklerine kadar ıslandın.", "steps": -60}},
|
||
{"text": "Yağmurda yürümeye devam et", "outcome": {"text": "Islandın, üşüdün, yoruldun ama yol aldın.", "steps": -30}},
|
||
]),
|
||
("sansli_bulus", "Şanslı Buluş", "Patikanın hemen kenarında birinin unuttuğu küçük bir malzeme yığını duruyor.", [
|
||
{"text": "Topla", "outcome": {"text": "Sopa ve taşları sırt çantana koydun.", "items": [{"code": "stick", "qty": 2}, {"code": "stone_small", "qty": 2}]}},
|
||
{"text": "Sahibi gelir, bırak", "outcome": {"text": "Dürüstlüğünle gurur duyarak ilerledin."}},
|
||
]),
|
||
]
|
||
|
||
|
||
async def main():
|
||
conn = await asyncpg.connect(DSN)
|
||
|
||
for code, name, typ, desc, x, y in LOCATIONS:
|
||
await conn.execute("""
|
||
INSERT INTO locations (code, name, type, description, map_x, map_y)
|
||
VALUES ($1,$2,$3,$4,$5,$6)
|
||
ON CONFLICT (code) DO UPDATE SET name=$2, type=$3, description=$4, map_x=$5, map_y=$6
|
||
""", code, name, typ, desc, x, y)
|
||
ids = {r["code"]: r["id"] for r in await conn.fetch("SELECT id, code FROM locations")}
|
||
|
||
for a, b, cost, chance in ROADS:
|
||
await conn.execute("""
|
||
INSERT INTO roads (loc_a, loc_b, step_cost, event_chance)
|
||
VALUES ($1,$2,$3,$4)
|
||
ON CONFLICT (loc_a, loc_b) DO UPDATE SET step_cost=$3, event_chance=$4
|
||
""", ids[a], ids[b], cost, chance)
|
||
|
||
n_res = 0
|
||
for loc, res in RESOURCES.items():
|
||
for item, skill, steps, xp, cd in res:
|
||
await conn.execute("""
|
||
INSERT INTO location_resources (location_id, item_code, skill, step_cost, xp, cooldown_s)
|
||
VALUES ($1,$2,$3,$4,$5,$6)
|
||
ON CONFLICT (location_id, item_code) DO UPDATE SET skill=$3, step_cost=$4, xp=$5, cooldown_s=$6
|
||
""", ids[loc], item, skill, steps, xp, cd)
|
||
n_res += 1
|
||
|
||
for code, name, txt, choices in EVENTS:
|
||
await conn.execute("""
|
||
INSERT INTO events (code, name, text, choices) VALUES ($1,$2,$3,$4::jsonb)
|
||
ON CONFLICT (code) DO UPDATE SET name=$2, text=$3, choices=$4::jsonb
|
||
""", code, name, txt, json.dumps(choices))
|
||
|
||
# Konumu olmayan karakterleri köye koy
|
||
moved = await conn.execute(
|
||
"UPDATE characters SET location_id=$1 WHERE location_id IS NULL", ids["koy"])
|
||
|
||
print(f"Lokasyon: {len(LOCATIONS)}, yol: {len(ROADS)}, kaynak: {n_res}, olay: {len(EVENTS)}. Karakter taşındı: {moved}")
|
||
await conn.close()
|
||
|
||
|
||
asyncio.run(main())
|