Generarea personajelor și a obiectelor este una dintre cele mai utilizate tehnici în jocurile moderne, de la RPG-uri care generează milioane de iteme unice până la simulări emergente, unde NPC-urile duc “vieți” organice bazate pe reguli simple.
Un NPC este definit printr-o combinație de atribute statice (nume, clasă, aspect etc.) și atribute dinamice (viață, damage, personalitate). Cheia unui sistem bun nu este generarea de atribute pur aleatorie, ci generare aleatorie controlată (valori generate în intervale cu sens, combinate prin reguli care produc personaje coerente).
În general, clasele de NPC nu sunt echiprobabile (de exemplu, războinicii sunt mai comuni decât magicienii legendari). Distribuția ponderată permite controlul frecvenței fiecărei clase.
Fiecare clasă definește intervale de bază pentru atributele NPC-urilor. Randomizarea se aplică în interiorul acestor intervale, nu global — un Mage va fi mereu mai slab fizic decât un Warrior, indiferent de roll. Exemplu:
| Class | HP | Damage | Armor |
|---|---|---|---|
| Warrior | 80–120 | 15–25 | 10–20 |
| Mage | 40–65 | 30–50 | 2–5 |
| Rogue | 55–80 | 20–35 | 5–10 |
| Archer | 60–85 | 18–30 | 4–8 |
| Paladin | 90–130 | 12–20 | 15–25 |
Trăsătura de personalitate nu este doar cosmetică; aceasta modifică valorile de bază și va influența comportamentul în simulare. Fiecare trăsătură aplică un multiplicator pe atribute. Exemplu:
| Trăsătură | Efect pe atribute | Comportament în simulare |
|---|---|---|
| Aggressive | Damage ×1.3, HP ×0.9 | Atacă primul, preferă duelul direct |
| Coward | HP ×0.8, Armor ×0.7 | Fuge când HP < 40% |
| Brave | HP ×1.1, Damage ×1.1 | Nu fuge niciodată, protejează aliații |
| Cunning | Damage ×1.2, Armor ×1.1 | Atacă NPC-ul cu HP cel mai scăzut |
| Peaceful | HP ×1.15, Damage ×0.7 | Nu inițiază atacuri, ripostează doar |
Raritatea controlează atât puterea unui item (stat budget), cât și frecvența cu care apare. Sistemul clasic cu culori comunică vizual calitatea instantaneu. Exemplu:
| Raritate | Culoare | Probabilitate | Stat multiplier | Abilitate specială |
|---|---|---|---|---|
| Common | Alb | 55% | 1.0× | Nu |
| Uncommon | Verde | 25% | 1.3× | Nu |
| Rare | Albastru | 12% | 1.7× | Nu |
| Epic | Mov | 6% | 2.2× | Da |
| Legendary | Portocaliu | 2% | 3.0× | Da (2 abilități) |
Itemele de raritate mai mare nu au toate statisticile mai mari față de cele comune. În schimb, fiecare item primește un buget de putere total (calculat din raritate), iar acel buget este distribuit aleatoriu între atribute. Astfel, două iteme de aceeași raritate pot fi foarte diferite (de exemplu, unul poate fi axat pe damage, altul pe durabilitate).
Abilitățile speciale se adaugă peste budget pentru Epic și Legendary. A doua abilitate a unui Legendary este aleasă explicit diferit de prima: dacă prima e Poison, a doua e aleasă din restul listei (Lifesteal, FireDamage, IceSlow, Thunder).
GenerateItem(type, rarity):
budget = baseBudget * rarityMultiplier[rarity]
damage = Random(0.4, 0.7) * budget // 40-70% din budget pe damage
durabil = budget - damage // restul pe durabilitate
if rarity >= Epic:
ability = SelectRandom(specialAbilities)
if rarity == Legendary:
ability2 = SelectRandom(specialAbilities - {ability})
Abilitățile speciale sunt definite ca modificatori cu parametri proprii generați în intervale. Exemplu:
| Abilitate | Efect | Parametru generat |
|---|---|---|
| Poison | Damage over time | 3–8 damage/tick, 3–5 tick-uri |
| Lifesteal | Regenerare HP la hit | 15–30% din damage ca HP |
| Fire Damage | Bonus damage + burn | +5–15 damage, 2 tick-uri burn |
| Ice Slow | Reduce viteza țintei | 20–50% slow, 2–4 secunde |
| Thunder | Șansă de stun | 15–35% șansă, 1 secundă stun |
Simularea unei lumi este un exemplu de emergent storytelling. Pe baza unor reguli simple, se crează comportamente complexe și povești unice. Fiecare zi (tick) de simulare aplică regulile tuturor NPC-urilor în ordine, iar rezultatele se acumulează într-un jurnal narativ.
La nivelul acestui laborator, simularea poate fi împărțită în patru componente independente:
La fiecare tick, fiecare NPC parcurge în ordine un arbore de decizie cu un anumit număr de priorități. Prima condiție adevărată câștigă, restul nemaifiind evaluate.
| Prioritate | Nume Acțiune | Condiție | Descriere Acțiune |
|---|---|---|---|
| 1 | Supraviețuire | Personalitate Coward și HP < 30% | Fuge la o locație aleatoare |
| 2 | Atac | Personalitate Aggressive sau Cunning și există dușmani în locație | Atacă: Cunning alege ținta cu HP minim, Aggressive alege aleatoriu |
| 3 | Trade | Există aliați în locație și NPC-ul are iteme | 25% șansă de a da un item unui aliat |
| 4 | Explorare | - | 20% șansă de a se muta în altă locație |
| 5 | Idle | - | Nicio acțiune; relațiile cresc pasiv prin co-prezență |
Trade se verifică înaintea Attack, NPC-urile vor prefera alianțele în fața conflictului și simularea va produce povești cu mai puțini morți și mai multă acumulare de relații pozitive.
Relațiile sunt stocate ca valori float într-un anumit interval predefinit (de exemplu, intervalul [−100, +100]), unde valoarea minimă înseamnă dușmănie absolută și valoarea maximă alianță puternică. Toate relațiile dintre NPC-uri pornesc de la 0 și se modifică prin interacțiuni:
| Eveniment | Modificare relație |
|---|---|
| Atac reușit | −20 pentru cel atacat față de atacant |
| Schimb de iteme | +10 pentru ambii participanți |
| Ucidere (martori prezenți) | −30 față de ucigaș pentru toți martorii |
| Fără conflict sau orice altă interacține | +2 per tick (familiaritate pasivă) |
Locațiile, la rândul lor, nu sunt neutre. Ele modifică statistica de damage și probabilitățile de acțiune ale NPC-urilor prezente. De exemplu, modificatorul de agresivitate se aplică multiplicativ pe damage-ul de bază al NPC-ului pentru durata tick-ului curent, apoi se restaurează:
| Locație | Modificator de agresivitate | Bonus trade | Bonus mișcare |
|---|---|---|---|
| Dungeon | ×1.5 | — | — |
| Tavernă | ×0.6 | +40% | — |
| Piață | ×0.4 | +60% | — |
| Pădure | ×1.2 | — | +20% |
| Câmpie | ×1.0 | — | +10% |
Fiecare acțiune va genera o linie de text prin selectarea aleatoare a unui template și substituirea variabilelor contextuale. Template-urile sunt organizate pe tipuri de acțiuni, cu multiple variante pentru fiecare acțiune pentru a evita repetiția:
| Variabilă | Semnificație |
|---|---|
{attacker}, {target} | Numele NPC-urilor implicate |
{cls} | Clasa NPC-ului care acționează |
{loc} | Numele locației curente |
{dmg} | Damage-ul aplicat (rotunjit) |
{style} | Derivat din personalitate: Aggressive → “brutală”, Cunning → “calculată” |
{hp} | Procentul HP curent (pentru template-urile de fugă) |
Pentru acest laborator, jurnalul va reține și numărul zilei curente, prefixând fiecare intrare cu [Ziua N]. Intrările de tip Idle se înregistrează rar (20% din cazuri) pentru a nu polua textul.
BASE_BUDGET × rarityMultiplier), se generează un număr aleator dintr-un interval predefinit (de exemplu, [0.4, 0.7]) pentru damage, iar restul bugetului merge la durabilitate. Numele itemului se construiește procedural dintr-un prefix aleatoriu și un sufix dependent de tip. Abilitățile speciale se adaugă pentru Epic (una) și Legendary (două distincte).delta × 0.7, ambele cu clamp la intervalul predefinit.tickInterval secunde între tick-uri. La fiecare zi nouă, se iterează printre toți agenții vii, aplică damge-ul agentului, aplică decizia luată de agent. Simularea se termină când rămâne cel mult un NPC în viață sau se epuizează zilele maxime.Advance Day, fiecare NPC execută o acțiune bazată pe personalitate, HP curent și relațiile cu vecinii din aceeași locație.