C# týden C# týden
Pořádné programy s pořádnou klávesnicí zdarma. Více zde
Pouze tento týden sleva až 80 % na C# .NET

Lekce 6 - C# - Aréna s bojovníky

C# .NET Objektově orientované programování C# - Aréna s bojovníky American English version English version

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Bojovník do arény, jsme si vytvořili třídu bojovníka. Hrací kostku máme hotovou z prvních lekcí objektově orientovaného programování. Dnes tedy dáme vše dohromady a vytvoříme v C# .NET funkční arénu. Tutoriál bude spíše oddechový a pomůže nám zopakovat si práci s objekty.

Potřebujeme napsat nějaký kód pro obsluhu bojovníků a výpis zpráv uživateli. Samozřejmě ho nebudeme bušit rovnou do Program.cs, ale vytvoříme si objekt Arena, kde se bude zápas odehrávat. Program.cs potom jen založí objekty a o zbytek se bude starat objekt Arena. Přidejme k projektu tedy poslední třídu a to Arena.cs.

Třída bude víceméně jednoduchá, jako atributy bude obsahovat 3 potřebné instance: 2 bojovníky a hrací kostku. V konstruktoru se tyto atributy naplní z parametrů. Kód třídy bude tedy následující (komentáře si dopiště):

class Arena
{
    private Bojovnik bojovnik1;
    private Bojovnik bojovnik2;
    private Kostka kostka;

    public Arena(Bojovnik bojovnik1, Bojovnik bojovnik2, Kostka kostka)
    {
        this.bojovnik1 = bojovnik1;
        this.bojovnik2 = bojovnik2;
        this.kostka = kostka;
    }

}

Zamysleme se nad metodami. Z veřejných metod bude určitě potřeba jen ta k simulaci zápasu. Výstup programu na konzoli uděláme trochu na úrovni a také umožníme třídě Arena, aby přímo ke konzoli přistupovala. Rozhodli jsme se, že výpis bude v kompetenci třídy, jelikož se nám to zde vyplatí. Naopak kdyby výpis prováděli i bojovníci, bylo by to na škodu (nebyli by univerzální). Potřebujeme tedy metodu, která vykreslí obrazovku s aktuálními údaji o kole a životy bojovníků. Zprávy o útoku a obraně budeme chtít vypisovat s dramatickou pauzou, aby byl výsledný efekt lepší, uděláme si pro takový typ zprávy ještě pomocnou metodu. Začněme s vykreslením informační obrazovky:

private void Vykresli()
{
    Console.Clear();
    Console.WriteLine("-------------- Aréna -------------- \n");
    Console.WriteLine("Zdraví bojovníků: \n");
    Console.WriteLine("{0} {1}", bojovnik1, bojovnik1.GrafickyZivot());
    Console.WriteLine("{0} {1}", bojovnik2, bojovnik2.GrafickyZivot());
}

Zde asi není co řešit, můžete si ještě obrazovku vyzdobit barevně, když budete chtít. Díky smazání konzole pomocí Clear() docílíme hezké informační obrazovky namísto klasického konzolového textu, kde se plná obrazovka roluje dolů. Metoda je privátní, budeme ji používat jen uvnitř třídy.

Další privátní metodou bude výpis zprávy s dramatickou pauzou:

private void VypisZpravu(string zprava)
{
    Console.WriteLine(zprava);
    Thread.Sleep(500);
}

Kód je zřejmý až na třídu Thread, která umožňuje práci s vlákny. My z ní využijeme pouze metodu Sleep(), která uspí vlákno programu na daný počet milisekund. S vlákny budeme pracovat až na konci kurzu. Aby vše fungovalo, musíme přidat using System.Threading; na začátek souboru Arena.cs.

Obě metody vlastně jen vypisují na konzoli, připadá mi zbytečné je zkoušet, přesuneme se tedy již k samotnému zápasu. Metoda Zapas() nebude mít žádné parametry a nebude ani nic vracet. Uvnitř bude cyklus, který bude na střídačku volat útoky bojovníků navzájem a vypisovat informační obrazovku a zprávy. Metoda by mohla vypadat takto:

public void Zapas()
{
    Console.WriteLine("Vítejte v aréně!");
    Console.WriteLine("Dnes se utkají {0} s {1}! \n", bojovnik1, bojovnik2);
    Console.WriteLine("Zápas může začít...");
    Console.ReadKey();
    // cyklus s bojem
    while (bojovnik1.Nazivu() && bojovnik2.Nazivu())
    {
        bojovnik1.Utoc(bojovnik2);
        Vykresli();
        VypisZpravu(bojovnik1.VratPosledniZpravu()); // zpráva o útoku
        VypisZpravu(bojovnik2.VratPosledniZpravu()); // zpráva o obraně
        bojovnik2.Utoc(bojovnik1);
        Vykresli();
        VypisZpravu(bojovnik2.VratPosledniZpravu()); // zpráva o útoku
        VypisZpravu(bojovnik1.VratPosledniZpravu()); // zpráva o obraně
        Console.WriteLine();
    }
}

Kód vypíše jednoduché informace a po stisku klávesy přejde do cyklu s bojem. Jedná se o while cyklus, který se opakuje, dokud jsou oba bojovníci naživu. První bojovník zaútočí na druhého, jeho útok vnitřně zavolá na druhém bojovníkovi obranu. Po útoku vykreslíme obrazovku s informacemi a dále zprávy o útoku a obraně pomocí naší metody VypisZpravu(), která po výpisu udělá dramatickou pauzu. To samé provedeme i pro druhého bojovníka.

Přesuňme se do Program.cs, vytvořme patřičné instance a zavolejme na aréně metodu Zapas():

    // vytvoření objektů
    Kostka kostka = new Kostka(10);
    Bojovnik zalgoren = new Bojovnik("Zalgoren", 100, 20, 10, kostka);
    Bojovnik shadow = new Bojovnik("Shadow", 60, 18, 15, kostka);
    Arena arena = new Arena(zalgoren, shadow, kostka);
    // zápas
    arena.Zapas();
    Console.ReadKey();
    class Bojovnik
    {
    private string jmeno;
    private int zivot;
    private int maxZivot;
    private int utok;
    private int obrana;
    private Kostka kostka;
    private string zprava;

    public Bojovnik(string jmeno, int zivot, int utok, int obrana, Kostka kostka)
    {
        this.jmeno = jmeno;
        this.zivot = zivot;
        this.maxZivot = zivot;
        this.utok = utok;
        this.obrana = obrana;
        this.kostka = kostka;
    }

    public bool Nazivu()
    {
        return (zivot > 0);
    }

    public string GrafickyZivot()
    {
        string s = "[";
        int celkem = 20;
        double pocet = Math.Round(((double)zivot / maxZivot) * celkem);
        if ((pocet == 0) && (Nazivu()))
        pocet = 1;
        for (int i = 0; i < pocet; i++)
        s += "#";
        s = s.PadRight(celkem + 1);
        s += "]";
        return s;
    }

    public void Utoc(Bojovnik souper)
    {
        int uder = utok + kostka.hod();
        NastavZpravu(String.Format("{0} útočí s úderem za {1} hp", jmeno, uder));
        souper.BranSe(uder);
    }

    public void BranSe(int uder)
    {
        int zraneni = uder - (obrana + kostka.hod());
        if (zraneni > 0)
        {
        zivot -= zraneni;
        zprava = String.Format("{0} utrpěl poškození {1} hp", jmeno, zraneni);
        if (zivot <= 0)
        {
        zivot = 0;
        zprava += " a zemřel";
            }

        } else
        zprava = String.Format("{0} odrazil útok", jmeno);
        NastavZpravu(zprava);
    }

    private void NastavZpravu(string zprava)
    {
        this.zprava = zprava;
    }

    public string VratPosledniZpravu()
    {
        return zprava;
    }

    public override string ToString()
    {
        return jmeno;
    }
    }
using System.Threading;
    class Arena
    {
    private Bojovnik bojovnik1;
    private Bojovnik bojovnik2;
    private Kostka kostka;

    public Arena(Bojovnik bojovnik1, Bojovnik bojovnik2, Kostka kostka)
    {
        this.bojovnik1 = bojovnik1;
        this.bojovnik2 = bojovnik2;
        this.kostka = kostka;
    }

    private void Vykresli()
    {
        Console.Clear();
        Console.WriteLine("-------------- Aréna -------------- \n");
        Console.WriteLine("Zdraví bojovníků: \n");
        Console.WriteLine("{0} {1}", bojovnik1, bojovnik1.GrafickyZivot());
        Console.WriteLine("{0} {1}", bojovnik2, bojovnik2.GrafickyZivot());
    }

    private void VypisZpravu(string zprava)
    {
        Console.WriteLine(zprava);
        Thread.Sleep(500);
    }

    public void Zapas()
    {
        Console.WriteLine("Vítejte v aréně!");
        Console.WriteLine("Dnes se utkají {0} s {1}! \n", bojovnik1, bojovnik2);
        Console.WriteLine("Zápas může začít...");
        Console.ReadKey();
        // cyklus s bojem
        while (bojovnik1.Nazivu() && bojovnik2.Nazivu())
        {
        bojovnik1.Utoc(bojovnik2);
        Vykresli();
        VypisZpravu(bojovnik1.VratPosledniZpravu()); // zpráva o útoku
        VypisZpravu(bojovnik2.VratPosledniZpravu()); // zpráva o obraně
        bojovnik2.Utoc(bojovnik1);
        Vykresli();
        VypisZpravu(bojovnik2.VratPosledniZpravu()); // zpráva o útoku
        VypisZpravu(bojovnik1.VratPosledniZpravu()); // zpráva o obraně
        Console.WriteLine();
        }
    }

    }

Charakteristiky hrdinů si můžete upravit dle libosti. Program spustíme:

Konzolová aplikace
-------------- Aréna --------------

Zdraví bojovníků:

Zalgoren [######              ]
Shadow [                    ]
Shadow útočí úderem za 20 hp
Zalgoren utrpěl poškození 4 hp

Výsledek je docela působivý. Objekty spolu komunikují, grafický život ubývá jak má, zážitek umocňuje dramatická pauza. Aréna má však 2 nedostatky.

  • V cyklu s bojem útočí první bojovník na druhého. Poté však vždy útočí i druhý bojovník, nehledě na to, zda ho první nezabil. Může tedy útočit již jako mrtvý. Podívejte se na screenshot výše, Shadow útočil jako poslední i když byl mrtvý. Až potom se vystoupilo z while cyklu. U prvního bojovníka tento problém není, u druhého musíme před útokem kontrolovat, zda je naživu.
  • Druhým nedostatkem je, že bojovníci vždy bojují ve stejném pořadí, čili zde "Zalgoren" má vždy výhodu. Pojďme vnést další prvek náhody a pomocí kostky rozhodněme, který z bojovníků bude začínat. Jelikož jsou bojovníci vždy dva, stačí hodit kostkou a podívat se, zda padlo číslo menší nebo rovné polovině počtu stěn kostky. Tedy např. pokud padne na desetistěnné kostce číslo do 5ti, začíná 2. bojovník, jinak začíná první. Zbývá zamyslet se nad tím, jak do kódu zanést prohazování bojovníků. Jistě by bylo velmi nepřehledné opodmínkovat příkazy ve while cyklu. Jelikož již víme, že v C# fungují reference, není pro nás problém udělat si 2 proměnné, ve kterých budou instance bojovníků, nazvěme je jednoduše b1 a b2. Do těchto proměnných si na začátku dosadíme bojovníky bojovnik1 a bojovnik2 tak, jak potřebujeme. Můžeme tedy při pozitivním hodu kostkou dosadit do b1 bojovník2 a naopak, výsledkem bude, že začínat bude ten druhý. Kód cyklu se takto vůbec nezmění a zůstane stále přehledný a jednoduchý, jen místo bojovnik bude b.

Změněná verze včetně podmínky, aby nemohl útočit mrtvý bojovník, by mohla vypadat nějak takto:

    public void Zapas()
    {
        // původní pořadí
        Bojovnik b1 = bojovnik1;
        Bojovnik b2 = bojovnik2;
        Console.WriteLine("Vítejte v aréně!");
        Console.WriteLine("Dnes se utkají {0} s {1}! \n", bojovnik1, bojovnik2);
        // prohození bojovníků
        bool zacinaBojovnik2 = (kostka.hod() <= kostka.VratPocetSten() / 2);
        if (zacinaBojovnik2)
        {
        b1 = bojovnik2;
        b2 = bojovnik1;
        }
        Console.WriteLine("Začínat bude bojovník {0}! \nZápas může začít...", b1);
        Console.ReadKey();
        // cyklus s bojem
        while (b1.Nazivu() && b2.Nazivu())
        {
        b1.Utoc(b2);
        Vykresli();
        VypisZpravu(b1.VratPosledniZpravu()); // zpráva o útoku
        VypisZpravu(b2.VratPosledniZpravu()); // zpráva o obraně
            if (b2.Nazivu())
            {
            b2.Utoc(b1);
            Vykresli();
            VypisZpravu(b2.VratPosledniZpravu()); // zpráva o útoku
            VypisZpravu(b1.VratPosledniZpravu()); // zpráva o obraně
            }
        Console.WriteLine();
        }
    }
    class Bojovnik
    {
    private string jmeno;
    private int zivot;
    private int maxZivot;
    private int utok;
    private int obrana;
    private Kostka kostka;
    private string zprava;

    public Bojovnik(string jmeno, int zivot, int utok, int obrana, Kostka kostka)
    {
        this.jmeno = jmeno;
        this.zivot = zivot;
        this.maxZivot = zivot;
        this.utok = utok;
        this.obrana = obrana;
        this.kostka = kostka;
    }

    public bool Nazivu()
    {
        return (zivot > 0);
    }

    public string GrafickyZivot()
    {
        string s = "[";
        int celkem = 20;
        double pocet = Math.Round(((double)zivot / maxZivot) * celkem);
        if ((pocet == 0) && (Nazivu()))
        pocet = 1;
        for (int i = 0; i < pocet; i++)
        s += "#";
        s = s.PadRight(celkem + 1);
        s += "]";
        return s;
    }

    public void Utoc(Bojovnik souper)
    {
        int uder = utok + kostka.hod();
        NastavZpravu(String.Format("{0} útočí s úderem za {1} hp", jmeno, uder));
        souper.BranSe(uder);
    }

    public void BranSe(int uder)
    {
        int zraneni = uder - (obrana + kostka.hod());
        if (zraneni > 0)
        {
        zivot -= zraneni;
        zprava = String.Format("{0} utrpěl poškození {1} hp", jmeno, zraneni);
        if (zivot <= 0)
        {
            zivot = 0;
            zprava += " a zemřel";
        }

        } else
            zprava = String.Format("{0} odrazil útok", jmeno);
            NastavZpravu(zprava);
        }

    private void NastavZpravu(string zprava)
    {
        this.zprava = zprava;
    }

    public string VratPosledniZpravu()
    {
        return zprava;
    }

    public override string ToString()
    {
        return jmeno;
    }

    }

Program vyzkoušejme.

Konzolová aplikace
-------------- Aréna --------------

Zdraví bojovníků:

Zalgoren [#########           ]
Shadow [                    ]
Zalgoren útočí úderem za 27 hp
Shadow utrpěl poškození 11 hp a zemřel

Vidíme, že je vše již v pořádku. Gratuluji vám, pokud jste se dostali až sem a tutoriály opravdu četli a pochopili, máte základy objektového programování a dokážete tvořit rozumné aplikace :)

V příští lekci, Dědičnost a polymorfismus, se podíváme na objektově orientované programování podrobněji. V úvodu jsme si říkali, že OOP stojí na pilířích: zapouzdření, dědičnost a polymorfismus. První umíme již velmi dobře a modifikátor private je nám známý. Další dva nás čekají příště.


 

Stáhnout

Staženo 1322x (33.29 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
35 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Předchozí článek
Bojovník do arény
Miniatura
Následující článek
Dědičnost a polymorfismus
Aktivity (15)

 

 

Komentáře
Zobrazit starší komentáře (46)

Avatar
Vykuk
Člen
Avatar
Vykuk:19.12.2018 18:39

Metodu Zapas() jsem si vytunil, že si na začátku Bojovníci hodí kostkou a začne ten, kdo hodil větší číslo :-)

public void Zapas()
        {
            Console.WriteLine( "Vítejte v Aréně! " );
            Console.WriteLine( "Dnes se utkají {0} a {1}!\n", bojovnik1, bojovnik2 );
            Console.WriteLine( "Stiskni libovolnou klávesu pro výběr pořadí..." );
            Console.ReadKey();
            int hod1 = 0;
            int hod2 = 0;
            while (hod1 == hod2)
            {
                hod1 = kostka.Hod();
                VypisZpravu( String.Format( "Bojovník {0} hodil {1}", bojovnik1, hod1 ) );
                hod2 = kostka.Hod();
                VypisZpravu( String.Format( "Bojovník {0} hodil {1}", bojovnik2, hod2 ) );
                if( hod1 == hod2 )
                    Console.WriteLine( "Remíza, další hod..." );
            }
            Bojovnik b1 = bojovnik1;
            Bojovnik b2 = bojovnik2;
            if( hod2 > hod1 )
            {
                b1 = bojovnik2;
                b2 = bojovnik1;
                Console.WriteLine( "{0} hodil větší číslo a začíná!\n", b1 );
            }
            else
                Console.WriteLine( "{0} hodil větší číslo a začíná!\n", b1 );

            Console.WriteLine( "Stiskem libovolné klávesy můžeme začít" );
            Console.ReadKey();

            //cyklus s vlastním bojem
            while ( b1.NaZivu() && b2.NaZivu() )
            {
                b1.Utoc( b2 );
                VykresliArenu();
                VypisZpravu( b1.VypisPosledniZpravu() );
                VypisZpravu( b2.VypisPosledniZpravu() );
                if (b2.NaZivu())
                {
                    b2.Utoc( b1 );
                    VykresliArenu();
                    VypisZpravu( b2.VypisPosledniZpravu() );
                    VypisZpravu( b1.VypisPosledniZpravu() );
                }
            }
        }
 
Odpovědět 19.12.2018 18:39
Avatar
Tomáš Knor
Člen
Avatar
Tomáš Knor:14. ledna 19:52

Ahoj snažím se vytvořit vylepšení kde si můžeš zvolit zda chceš mága nebo bojovníka ale z nějakého důvodu to nefunguje.

namespace Arena
{
class Program
{
static void Main(string[] args)
{

Kostka kostka = new Kostka(10);
Console.Write­Line("Jak chcete aby se jmenoval váš první bojovník?");
string jmeno1 = Console.ReadLine();
Console.Write­Line("Jaké třídy bude váš bojovník?");
Console.Write­Line("1. Bojovník \n2. Mág");

char volba = Console.ReadKe­y().KeyChar;
switch (volba)
{
case '1':
Bojovnik prvnibojovnik = new Bojovnik(jmeno1, 100, 20, 10, kostka);
break;
case '2':
Bojovnik prvnimag = new Mag(jmeno1, 60, 15, 12, kostka, 30, 45);
break;
}
Console.Write­Line("Jak chcete aby se jmenoval váš druhý bojovník?");
string jmeno2 = Console.ReadLine();
Console.Write­Line("Jaké třídy bude váš bojovník?");
Console.Write­Line("1. Bojovník \n2. Mág");
char vyber = Console.ReadKe­y().KeyChar;
switch (vyber)
{
case '1':
Bojovnik druhybojovnik = new Bojovnik(jmeno1, 100, 20, 10, kostka);
break;
case '2':
Bojovnik druhymag = new Mag(jmeno1, 60, 15, 12, kostka, 30, 45);
break;
}

Console.ReadKey();
}
}
}

 
Odpovědět 14. ledna 19:52
Avatar
Tomáš Knor
Člen
Avatar
Odpovídá na Tomáš Knor
Tomáš Knor:14. ledna 20:00

když chci později zadat něco prvnibojovnik tak mito píše že nexistuje. :-(

 
Odpovědět 14. ledna 20:00
Avatar
Odpovídá na Tomáš Knor
Andy Scheuchzer:14. ledna 20:35

Já bych teda založil bojovníka ještě před case a neměnil bych název v případě, že je to mág.

Odpovědět  +1 14. ledna 20:35
Člověk, co si myslí, že snědl všechnu moudrost světa, i když tomu tak není.
Avatar
Tomáš Knor
Člen
Avatar
Odpovídá na Andy Scheuchzer
Tomáš Knor:14. ledna 21:13

A když ho zalozim jeste před case tak ho budu moct změnit na Maga?

 
Odpovědět 14. ledna 21:13
Avatar
Odpovídá na Tomáš Knor
Andy Scheuchzer:16. ledna 19:01
Bojovnik b1;
switch (vyber) {
    case '1': b1 = new Bojovnik(/*...*/); break;
    case '2': b1 = new Mag(/*...*/); break;
}
Odpovědět 16. ledna 19:01
Člověk, co si myslí, že snědl všechnu moudrost světa, i když tomu tak není.
Avatar
vajkuba1234
Člen
Avatar
Odpovídá na Tomáš Knor
vajkuba1234:16. ledna 19:25

Ano, maji stejneho predka... K vytvareni instanci si precti clanky na tema "Factory design pattern", ktery k tomu slouzi :)

Odpovědět 16. ledna 19:25
No hope, no future, JUST WAR! For world peace Israel must be DESTROYED!
Avatar
Patrik Pastor:18. ledna 23:20

chtěl jsem se zeptat, nejede mi ten náhodný výběr bojovníka. Pokaždé to je ten první ale nikdy druhý (zkoušel jsem to hodněkrát). Zde je kód:

public void Zapas()
{
Console.Write­Line("vítejte v aréně, zde bojovnící umírají...a co vy?");
Console.Write­Line("utkání mezi {0} a {1}", bojovnik1, bojovnik2);
Console.Write­Line("PRESS ENTER");

Bojovnik b1 = bojovnik1;
Bojovnik b2 = bojovnik2;

int hod1 = 0;
int hod2 = 0;
while (hod1==hod2)
{
hod1 = kostka.hod();
hod2 = kostka.hod();
}

if (hod1 > hod2)
{
bojovnik1 = b2;
bojovnik2 = b1;

Console.Write­Line("začínat bude bojovnik {0}", b1);
Console.ReadKey();
}
else
Console.Write­Line("začínat bude bojovnik {0}", b1);

while (b1.naZivu() && b2.naZivu())
{
b1.Utoc(b2);
Vykresli();
VypisZpravu(b1­.VratZpravu());
VypisZpravu(b2­.VratZpravu());

b2.Utoc(b1);
if (b2.naZivu())
{
Vykresli();
VypisZpravu(b2­.VratZpravu());
VypisZpravu(b1­.VratZpravu());

}

}
}

 
Odpovědět 18. ledna 23:20
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Patrik Pastor
pocitac770:19. ledna 0:59
if (hod1 > hod2)
{
        bojovnik1 = b2;
        bojovnik2 = b1;

        Console.WriteLine("začínat bude bojovnik {0}", b1);
        Console.ReadKey();
}
else
        Console.WriteLine("začínat bude bojovnik {0}", b1);

Hlavní problém máš v tom, že sji prohoil dosazování bojovníků. Proměnné které používáš pro boj jsou b1 a b2. Ty jsi místo toho přepsal původní proměnné s bojovníky v instanci. Takhle kdyby jsi například dělal druhé kolo, a cely cyklus zapasu jsi spustil znovu, tak by jsi najednou v

Console.WriteLine("utkání mezi {0} a {1}", bojovnik1, bojovnik2);

zjistil, že bojovníci jsou opačně než předtím.
A jinak je zbytečné toto¨

        Console.WriteLine("začínat bude bojovnik {0}", b1);
        Console.ReadKey();
}
else
        Console.WriteLine("začínat bude bojovnik {0}", b1);

Místo toho ten jeden řádek můžeš dát úplně mimo if/else, nijak se nemění v závislosti na tom zda podmínka projde nebo ne.
Nakonec, používej tlačítko pro přidádání kódu nad textovým polem, takto to je velice nečitelné

Editováno 19. ledna 1:00
 
Odpovědět 19. ledna 0:59
Avatar
Odpovídá na pocitac770
Patrik Pastor:19. ledna 10:58

dik všemu rozumím, kromě toho tlačítka, myslíš komentáře?

 
Odpovědět 19. ledna 10:58
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 56. Zobrazit vše