""" Orman düşmanları — 12 çeşit hostile NPC (2026-07-08). NPC sayfasının yazdığı `npcs` tablosuna eklenir (stance='hostile'). Yer = forest region'larına (map_tags kind='region' category='forest', O-1..O-10) round-robin dağıtılır. İsim'e göre idempotent (varsa günceller, yoksa ekler). Çalıştırma: cd /opt/stepstead && ./venv/bin/python3 db/seed_enemies_forest.py """ import asyncio import sys sys.path.insert(0, "/opt/stepstead") import asyncpg # noqa: E402 from config import settings # noqa: E402 def desc(hp, dmg, atk, dc, weak, drops, danger, lore): """Özellikleri okunur bir description bloğuna paketle.""" return ( f"{lore}\n\n" f"⚔ Saldırı türü: {atk}\n" f"❤ Can (HP): {hp}\n" f"🗡 Hasar: {dmg}\n" f"🎯 Savaş zorluğu (combat DC): {dc}\n" f"💀 Tehlike: {danger}\n" f"🩹 Zayıflık: {weak}\n" f"🎁 Düşürdükleri: {drops}" ) # (name, role, stance, description) ENEMIES = [ ("Mana Solucanı", "Büyülü Yaratık", "hostile", desc(hp=18, dmg="4-8 (mana yakımı)", atk="Büyü — mana emer", dc=13, weak="Fiziksel darbe, demir silah", drops="Mana özü ×1-2, parlak salgı", danger="Orta", lore="Yeraltı mana damarlarında beslenen, ışıldayan segmentli bir solucan. " "Yaklaşana enerjisini emer; öldürüldüğünde içindeki saf mana damlar.")), ("Aç Kurt", "Canavar", "hostile", desc(hp=22, dmg="6-10 (ısırık)", atk="Yakın dövüş — sürü halinde", dc=13, weak="Ateş, yüksek zemin", drops="Kurt eti ×1-2, kurt derisi ×1", danger="Orta", lore="Kışın açlıktan gözü dönmüş orman kurdu. Tek başına ürkek ama " "sürüye katılınca korkusuzca saldırır.")), ("Yaşlı Ağaç Adam", "Bitki Devi", "hostile", desc(hp=55, dmg="12-18 (dal savurma)", atk="Yakın dövüş — ağır ve yavaş", dc=16, weak="Ateş, balta (woodcutting)", drops="Kadim odun ×2-3, canlı kök, meşe palamudu", danger="Yüksek", lore="Yüzyıllık bir meşenin uyanmış ruhu. Toprağı sarsan adımlarla yürür; " "yavaştır ama tek darbesi kemik kırar. Ateşten ölesiye korkar.")), ("Dev Orman Faresi", "Haşere", "hostile", desc(hp=8, dmg="2-4 (kemirme)", atk="Yakın dövüş — kalabalık", dc=9, weak="Hemen her şey", drops="Fare kuyruğu, paslı çöp, bazen 1-3 gold", danger="Düşük", lore="İnsan boyunun yarısı kadar, hastalık taşıyan bir kemirgen. Tek başına " "zararsız ama erzak çuvalını bir gecede boşaltır.")), ("Zehirli Orman Örümceği", "Haşere", "hostile", desc(hp=16, dmg="5-9 (zehirli ısırık)", atk="Yakın dövüş — zehir + ağ", dc=14, weak="Ateş, alan silahları", drops="Örümcek zehri ×1, ipek ×1-2", danger="Orta", lore="Karanlık kovuklara dev ağlar geren, avını zehirle felç eden bir örümcek. " "Ağı hareketi yavaşlatır, ısırığı damarlarda yanar.")), ("Sarmaşık Cini", "Bitki Ruhu", "hostile", desc(hp=24, dmg="6-11 (sarma + diken)", atk="Yakın dövüş — kavrayıp sıkar", dc=14, weak="Ateş, kesici balta", drops="Canlı sarmaşık ×2, diken tozu, çiçek özü", danger="Orta", lore="Küçük ama sinsi bir bitki ruhu. Yaprakların arasına gizlenir, " "geçeni sarmaşıklarıyla sarıp dikenlerine çeker.")), ("Mantar Spor Yaratığı", "Sporlu Canavar", "hostile", desc(hp=20, dmg="4-7 (spor bulutu)", atk="Alan — zehirli spor patlaması", dc=13, weak="Ateş, uzak menzil", drops="Işıldayan mantar ×2, spor tozu, şifa mantarı", danger="Orta", lore="Yürüyen dev bir mantar kümesi. Vurulunca zehirli spor bulutu patlatır; " "yakın dövüşte nefes almak tehlikelidir.")), ("Gölge Kurdu (Alfa)", "Canavar (Elit)", "hostile", desc(hp=40, dmg="10-16 (gölge pençesi)", atk="Yakın dövüş — hızlı, görünmez sıçrar", dc=17, weak="Işık, gümüş silah", drops="Gölge postu ×1, alfa dişi, gümüş ore ×1", danger="Yüksek", lore="Karanlık ormanın en yaşlı kurt sürüsünün lideri. Gölgelerde erir, " "arkandan belirir. Işık tuttuğunda bir an donar — tek fırsatın odur.")), ("Orman Haydutu", "Goblin", "hostile", desc(hp=26, dmg="7-12 (paslı hançer)", atk="Yakın dövüş — pusu + hırsızlık", dc=14, weak="Cephesel saldırı, gürültü", drops="Çalıntı kese (5-20 gold), paslı hançer, kuru erzak", danger="Orta", lore="Yol kenarında pusu kuran sinsi bir goblin. Önce keseni çalmaya çalışır, " "başaramazsa hançerine sarılır.")), ("Kanlı Yarasa", "Uçan Haşere", "hostile", desc(hp=12, dmg="3-6 (kan emme)", atk="Havadan — dalış + kan emer", dc=11, weak="Ateş, geniş savurma", drops="Yarasa kanadı ×1-2, kan kesesi", danger="Düşük", lore="Mağara ağızlarında ve yaşlı ağaç kovuklarında asılı duran kan emici. " "Sürü halinde daldığında kanını damla damla çeker.")), ("Bataklık Sülüğü", "Asalak", "hostile", desc(hp=14, dmg="3-7 (yapışıp emer)", atk="Yakın dövüş — yapışır, sürekli hasar", dc=12, weak="Tuz, ateş", drops="Sülük özü, iyileştirici salgı ×1", danger="Düşük-Orta", lore="Ormanın nemli bataklık kıyısında saklanan dev sülük. Bacağına yapışır, " "koparana dek yavaşça kan emer. Salgısı ilginç şekilde şifalıdır.")), ("Lanetli Karga Sürüsü", "Uçan Sürü", "hostile", desc(hp=30, dmg="5-9 (gaga yağmuru)", atk="Alan — sürü halinde gözlere dalış", dc=15, weak="Alan silahları, ateş, ses", drops="Kara tüy ×3, lanetli boncuk, bazen gümüş ore", danger="Orta-Yüksek", lore="Tek bir kötü ruhu paylaşan bir karga sürüsü. Ayrı ayrı zayıf ama " "birlikte gökten kara bir bulut gibi iner, gözleri hedef alır.")), ] async def main(): dsn = settings.DATABASE_URL.replace("+asyncpg", "") c = await asyncpg.connect(dsn) forests = [r["id"] for r in await c.fetch( "SELECT id FROM map_tags WHERE kind='region' AND category='forest' ORDER BY id" )] if not forests: print("UYARI: forest region bulunamadı, map_tag_id NULL bırakılıyor.") added = updated = 0 for i, (name, role, stance, description) in enumerate(ENEMIES): tag = forests[i % len(forests)] if forests else None existing = await c.fetchval("SELECT id FROM npcs WHERE name=$1", name) if existing: await c.execute( "UPDATE npcs SET role=$2, stance=$3, description=$4, map_tag_id=$5, " "updated_at=now() WHERE id=$1", existing, role, stance, description, tag) updated += 1 else: await c.execute( "INSERT INTO npcs(name,role,stance,description,map_tag_id) " "VALUES($1,$2,$3,$4,$5)", name, role, stance, description, tag) added += 1 total = await c.fetchval("SELECT count(*) FROM npcs WHERE stance='hostile'") print(f"Eklendi: {added}, güncellendi: {updated}. Toplam hostile NPC: {total}") await c.close() asyncio.run(main())