Knights Exemplar

Knights Exemplar

More Protectorate stuff done

Vanquisher
Knights Exemplar Officer

Couple Protectorate solos

Cinerator Officer

Yes, i know the shoulderpads are upside down.. i noticed that when two thirds of the model was done 🙁

Scrutator Potentate Severius

Cardfight!! Vanguard

Praeitą savaitę pabaigiau vieno mažo projektėlio, kurį kapsčiau prieš porą metų, perdarymą. Ir tai buvo Cardfight!! Vanguard žaidimo online versija.

Nuo pat pradžių buvo problema kaip padaryti serverį ir kaip palaikyti žaidimo statusą atmintyje esant shared hostingui, kuriame neina pasukti nei node serverio, nei javos. Sprendimas buvo: padaryk viską, ką gali, su userio requestu, ir lauk sekančio requesto. Userių notification’ams panaudojau pusher lib’ą, prie kurio prisijungia frontend aplikacija ir laukia praneÅ¡imų.

Kadangi nėra kaip gražiai susisteminti visų entities žaidime į db lenteles, nes labai daug kas gali keistis, plėstis, pasinaikinti – visi žaidimo entities yra paprasčiausi masyvų fasadai, kurie iÅ¡ masyvo gražina reikÅ¡mes ir atgal į vidinį masyvą sudeda naujas reikÅ¡mes. Loadinant iÅ¡ duomenų bazės viskas yra iÅ¡pakuojama iÅ¡ json string ir saugant yra atgal supakuojama į json string.

class Card
{
    public function __construct(
        array $data = null
    ) {
        $this->data = $data ?? [];
    }

    public function setType(string $type = null): self
    {
        $this->data[self::TYPE] = $type;

        return $this;
    }

    public function getType(): ?string
    {
        return $this->data[self::TYPE];
    }

    public function getId(): string
    {
        return $this->data[self::ID];
    }

    public function setId(string $id): self
    {
       $this->data[self::ID] = $id;

       return $this;
    }

Sekanti problema susijusi su pasirinkta struktūra buvo: kadangi game state objektas nepasikeitė, o pasikeitė kažkoks vidinis objektas, esantis kažkuris giliai child objektuose, doctrine nenorėjo saugoti pasikeitusio entity, nes doctrine požiūriu niekas nepasikeitė. Teko prieš saugojimą liepti visiems objektams save klonuoti, ir klonuoti visus child objektus.

PrieÅ¡ žaidėjo input apdorojimą iÅ¡saugojus pradinę bÅ«seną, buvo galima sulyginti struktÅ«rų skirtumus, ir juos papushinti visiems besiklausantiems klientams, kas pasikeitė žaidime, ir nusiųsti sekančią užklausą žaidėjui, iÅ¡ kurio laukiamas input’as. Tokiu bÅ«du programos veikimas yra:
žaidėjas pasako, kad nori žaisti -> sukuriam žaidimą, processinam būsena iki kol susiduriam su situacija, kai reikia kažką pasirinkti -> pranešam žaidėjui ko iš jo tikimės
žaidėjas atlieką veiksmą -> apdorojam duomenis, ir arba tesiam procesą iki kol vėl prisireikia žaidėjo input’o, arba praneÅ¡am atgal, kad blogas pasirinkimas ir vėl iÅ¡ žaidėjo tikimės duomenų
Taip vykdome tol, kol kažkuris žaidėjas laimi. Jei nė vienas iš žaidėjų nemato pranešimo, kad iš jo kažko reikalaujama, vadinasi kažkas sugedo 🙁

Kadangi kiekviena korta turi savo abilities ir jos visos skirtingos, reikėjo bÅ«do kažkaip visa tai sistematizuoti, kad neimplementinti kiekvienos kortos atskirai.. who’s got time for that?
Solution: More arrays! Yay!

{
    "id": "V-TD01/002",
    "name": "Stardrive Dragon",
    "grade": 3,
    "skill": "twin-drive",
    "power": 13,
    "critical": 1,
    "clan": "Royal Paladin",
    "image": "https://vignette.wikia.nocookie.net/cardfight/images/a/ad/V-TD01-002EN.png/revision/latest/scale-to-width/273",
    "description": "[AUTO](RC):When it attacks a vanguard, if you have three or more rear-guards, this unit gets [Power]+5000 until end of that battle.",
    "race": "Cosmo Dragon",
    "gift": "force",
    "abilities": [
        {
            "type": "auto",
            "event": "before_attack",
            "conditions": [
                {
                    "type": "is_card_check",
                    "options": {
                        "card": "self",
                        "location": "rearguard"
                    }
                },
                {
                    "type": "is_card_check",
                    "options": {
                        "player": "opponent",
                        "card": "defender",
                        "location": "vanguard"
                    }
                },
                {
                    "type": "is_list_check",
                    "options": {
                        "zone": "field",
                        "location": "rearguard",
                        "min_count": 3
                    }
                }
            ],
            "effects": [
                {
                    "type": "do_mark_card",
                    "options": {
                        "card": "self"
                    }
                },
                {
                    "type": "do_modify_buffer",
                    "options": {
                        "length": "battle",
                        "power": 5
                    }
                },
                {
                    "type": "do_clear_buffer"
                }
            ]
        }
    ]
},

Visi abilities žaidime yra 3 rūšių: activated, auto ir continuous. Tačiau iš principo visi jie veikia taip pat: patikrinam situaciją, sumokam kainą jei yra, ir atliekam kažkokius veiksmus. Ir dažniausiai tie tikrinimai ir veiksmai yra tokie patys, arba smarkiai panašūs, todėl sukūriau processorius, kurie apdoroja kažkokį kortos tikrinimą ar veiksmą ir tiesiog pagal parametrus šiek tiek keičiam logiką.
T.y. jei tikrinimo procesoriaus id yra “is_card_check”, privalomas optionas yra “card”, kuris nurodo kuri korta yra tikrinama, ir optional parametras yra “location”, kurio reikÅ¡mė yra “rearguard” – patikrinam ar šį abilitį turinti korta Å¡iuo metu randasi rearguard circle.
Aišku, kai kurie abilities yra tokie bizzare, kad teko juos implementinti kaip visai atskirą logiką, nes generic sprendimas buvo arba per daug complicated, arba tiesiog nesugalvojau kaip gudriai išspręsti, ir atidėjau tai vėlesniam laikui.

Application frontendas buvo padarytas su react ir redux, bet čia nežinau ką galima gudraus papasakoti, nes frontendas šiek tiek simplistic logikos prasme, visą logiką permetant backendui.

Jei kam nors norėsis pažiÅ«rėti kaip visa tai veikia realybėje, galit nueiti https://cfv.feral.lt ir prisijungti demo useriu. Prisijungus reikia pasirinkti “training”, jei norima pabandyti žaisti prieÅ¡ botą, arba kitame tab’e prisijungti su demo2 useriu, jei norima pažaisti prieÅ¡ save 🙂
Jei norėsis pasikapstyti po kodą, jį galima rasti https://gitlab.com/feral.drood/app-vanguard ir https://gitlab.com/feral.drood/app-vanguard-frontend

Clank! Legacy