Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Return arvot

Sivun loppuun

mika132 [08.03.2010 18:48:57]

#

Eli en keksinyt muuta ratkaisua musiikkien lataus systeemiksi kuin seuraavan tyylin. (Siis se on väärin ja jos asiaa ei voi noin toteuttaa niin voisitte kertoa miten) ensin yritin niin, että lataus samaan MusiikinHallinta kanssa. No se toimi, mutta koska MusiikinHallinta pitää olla while silmukassa, jotta katsoo koko ajan huoneen niin koko kone kaatui kun musiikki ladattiin muistiin aivan tolkuttoman monta kertaa sitten yritin erikseen tavallisilla void funktioilla niin tuli MusiikinHallinta funktiosta virhe, että ei tiedä mikä on on Tausta1. No sitten päätin yrittää tätä return temppua, mutta en ihan hallitse sitä. Funktio MusiikinLataus on esitelty ohjelma.hpp tiedostossa.

//ohjelma.cpp
static int ohjelma::MusiikinLataus(Musa Tausta1) {

    Musa Tausta1("data/musiikki/kirjaudu_sisaan.wav", 1); //joo ensimmäinen kappale mikä löyty. Eli windowsin oma ääni.
    return Tausta1;
}

void MusiikinHallinta(int room, bool musiikitladattu, Musa Tausta1) {
        if (room == 1)
        {
                Tausta1.Voimakkuus(100);
                Tausta1.ToistaHaivyttaen(2000, -1);
        }
}

Jokotai [08.03.2010 19:27:20]

#

https://www.ohjelmointiputka.net/oppaat/opas.php?tunnus=cpp_ohj_05

mika132 [09.03.2010 10:58:52]

#

Jos ymmärsin oikein tuon return jutun niin sitten joku muu on väärin, mutta kirjoitin nyt näin:

//ohjelma.cpp
static bool ohjelma::MusiikinLataus() {

    Musa Tausta1("data/musiikki/metallica-nothing_else_matters.wav", 1);
    return Tausta1;
}

void MusiikinHallinta(ohjelma::MusiikinLataus()) {
        Tausta1.Voimakkuus(100);
        Tausta1.ToistaHaivyttaen(2000, -1);
}

Errori:
ohjelma.hpp:24: error: 'bool ohjelma::MusiikinLataus()' used but never defined


Eli funktio on esitelty ohjelma.hpp tiedostossa ja sieltä tulee errori:

//ohjelma.hpp tiedostossa oleva esittely.
namespace ohjelma {
    //Täsä o paljo muitaki funktioita, mut en laittanu niitä et pysyy selkeenä.
    static bool MusiikinLataus();
}

vehkis91 [09.03.2010 11:38:11]

#

EI noin!!!

Musa ohjelma::MusiikinLataus() {

    Musa Tausta1("data/musiikki/metallica-nothing_else_matters.wav", 1);
    return Tausta1;
}

Noin se menee, sun pitää siis palauttaa Musa-tyyppiä oleva arvo!

temu92 [09.03.2010 13:06:31]

#

Mikään C++ guru en ole, mutta eikös tuossa kannattaisi mieluummin käyttää osoittimia hyödyksi?

Jokotai [09.03.2010 16:09:01]

#

Nuo bool tai int, funktion määrittelyn ensimmäisellä rivillä tarkoittaa että funktio palauttaa bool tai int arvon. esim.

static int ohjelma::MusiikinLataus(Musa Tausta1) {

Tarkoittaa että määittelet staattisen int arvon palauttavan metodin oliolle ohjelma.

mika132 [09.03.2010 18:12:40]

#

No juu. En vieläkään saa toimimaan. Siirtelin esittelyä joka paikkaan, mutta ei auttanut yritin muuttaa sitä erillaiseksi, mutta ei auttanut. Eli mikä tässä menee pieleen:

//ohjelma.hpp
//HUOM!! Tässä ei ole kaikkia mitä on vaan tuohon musiikkiin liittyvät asiat.
namespace ohjelma {
    void alku();
    void loppu();
}
//ohjelma.cpp
//HUOM!! Tässä ei ole kaikkia mitä on vaan tuohon musiikkiin liittyvät asiat.
namespace ohjelma {
	static SDL_Surface *ruutu;
	void piirra_peli(peli::alue const& alue, peli::pelaaja& pelaaja, peli::paussinvalitsin const& v, peli::exitvalitsin& e, peli::slot_valitsin& l);
	static Musa MusiikinLataus();
    void MusiikinHallinta(ohjelma::MusiikinLataus()); //Tämä on siis rivi 16 eli tuo errori rivi
}

void ohjelma::alku() {
	std::clog << "ohjelma::alku()" << std::endl;
	if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) != 0) {
		throw std::runtime_error(SDL_GetError());
	}
	if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1) {
    fprintf(stderr, "SDL_Mixerin alustus ei onnistunut.");
    }
	ruutu = SDL_SetVideoMode(800, 600, 32, SDL_DOUBLEBUF|SDL_FULLSCREEN);
	if (!ruutu) {
		throw std::runtime_error(SDL_GetError());
	}
	SDL_WM_SetCaption("Timo ja Tomi - Saarella", "Timo ja Tomi - Saarella");
    Musa Tausta1 = ohjelma::MusiikinLataus();

	sekunnit(true);
}

Musa ohjelma::MusiikinLataus() {

    Musa Tausta1("data/musiikki/metallica-nothing_else_matters.wav", 1);
    return Tausta1;
}

void ohjelma::MusiikinHallinta(ohjelma::MusiikinLataus()) //Tämä on toinen error rivi 111.
{
        Tausta1.Voimakkuus(100);
        Tausta1.ToistaHaivyttaen(2000, -1);
}
//Tiedetään hieman oudossa funktiossa tuo musiikin kutsi, mutta tehty mikä tehty.
void ohjelma::piirra_peli(peli::alue const& alue, peli::pelaaja& pelaaja, peli::paussinvalitsin const& v, peli::exitvalitsin& e, peli::slot_valitsin& l) {

    if (pelaaja.huone == 1)
    {
        ohjelma::MusiikinHallinta(ohjelma::MusiikinLataus());
    }
}

Errorit:
ohjelma.cpp:16: error: variable or field `MusiikinHallinta' declared void
ohjelma.cpp:16: error: cannot convert `Musa' to `int' in initialization
ohjelma.cpp:111: error: variable or field `MusiikinHallinta' declared void
ohjelma.cpp:111: error: redefinition of `int ohjelma::MusiikinHallinta'
ohjelma.cpp:16: error: `int ohjelma::MusiikinHallinta' previously defined here

os [09.03.2010 18:36:29]

#

Mitä tarkoitat seuraavalla rivillä?

mika132 kirjoitti:

void MusiikinHallinta(ohjelma::MusiikinLataus()); //Tämä on siis rivi 16 eli tuo errori rivi

Ilmeisesti haluat määritellä funktion, jonka nimi on MusiikinHallinta, mutta mitä tarkoitat kohdalla ohjelma::MusiikinLataus(). Kuten sinun jotakin tämän vaikeustason ohjelmaa tehdessäsi todellakin pitäisi tietää, tässä kuuluisi C++-kielen syntaksin mukaan olla lista funktion parametreien tyypeistä (ja mahdollisesti nimistä).

Sinun tosiaan täytyy nähdä hieman vaivaa, jotta saat Musa-tyyppisen olion (jonka on ilmeisesti tarkoitus sisältää ladattu ääni) palautettua funktiosta. Siinä vaiheessa, kun ohjelmasi suurin ongelma on oikeasti tämä, voit ruveta etsimään ratkaisua esimerkiksi std::auto_ptr:n avulla.

mika132 [09.03.2010 19:08:16]

#

Siis ongelma on se, että en tiedä mihin pitäisi sijoittaa tuo MusiikinLataus(); jotta saan sen paluuarvon. Jos lisään sen näin:

void ohjelma::MusiikinHallinta() {
        ohjelma::MusiikinLataus().Voimakkuus(100);
        ohjelma::MusiikinLataus().ToistaHaivyttaen(2000, -1);
}

Tuolla kylläkin pelini vie koko RAM muistin. Eli suomeksi. Koska tuo funktio on while silmukassa se lataa kappaleen tuhat kertaa muistiin ja näin kuormittaa erittäin ikävästi koneen kuin koneen.

Sitten suunnittelin tekeväni niin, että funktio antaa huoneen numeron musiikin toiso funktiolle, mutta sitten tuli mieleen, että MusiikinHallinta on silloinkin While silmukan sisällä. sen on pakko pysyä While silmukassa, jotta musiikkia voidaan muuttaa, mutta miten saan sen niin että se ei kuormittaisi järjestelmää?

vehkis91 [09.03.2010 19:18:32]

#

namespace ohjelma
{
  //Kaikki musat laitetaan tänne.
  std::vector<Musa> musat;
}
//Tolla ladataan musiikit
Musa ohjelma::LataaBiisi(std::string file)
{
    Musa musa(file.c_str(), 1);
    return musa;
}

//Täällä ladataan kaikki musiikit
void ohjelma::MusiikinLataus()
{
   ohjelma::musat.push_back(LataaBiisi("BIISIN_NIMI");
   //tänne lisää noita...
}

//Sit pitää tehdä funkkari, jossa vapautetaan musat, kun niitä ei enää tarvita, saat itse tehdä sen.

void ohjelma::MusiikinHallinta(short int index)
{
   //säädetään indexin määrittämän biisin tietoja
   ohjelma::musat.at(index).Voimakkuus(100);
}

Tuo on heitetty päästä hetken mielijohteesta, joten voi olla virheitä ja luultavasti onkin.

Edit: ja tosiaan ne biisit kannattaa ladata ennen sitä while-silmukkaa...

EDIT2: ja mielestäni noiden namespacejen käyttö turhaan sekoittaa ohjelmaa, ainakin sun tapauksessasi.

Legu [09.03.2010 19:20:42]

#

Ei sen koodin siirtäminen funktioon ratkaise ongelmaa millään tavalla.

Kun nyt mietit, miten sen äänisysteemin pitäisi toimia, päädyt toivottavasti seuraavaan ajatukseen:

1. Alussa ladataan musiikki tiedostosta
while(peli) {
   2. Kun SAAVUTAAN huoneeseen, sammutetaan vanha musiikki ja toistetaan tämän uuden huoneen musiikki
}
3. Ohjelman lopussa pysäytetään musiikki ja vapautetaan muisti

Eli siis toista()-funktiota (tms.) kutsutaan vain kerran per musiikki, koska äänikirjastosi hoitaa äänen siirtämisen puskuriin aina tarvittaessa, joten äänen toistaminen jatkuu, vaikka kutsumista ei tapahtuisikaan juuri sillä ohjelmakierrolla:

Musa m = LataaMusiikki("musa.wav");
m.toista();
while(peli) {
  ...
  if (esc_painettu) peli = false;
  ...
  piirto(); jne();
}
m.vapauta();

Voit vaikka kokeilla noin ja huomata, että asia todellakin on näin.

mika132 [09.03.2010 20:23:11]

#

Juu tuon tiesinkin jo, mutta kun pitäisi saada vaihdettua musiikki. Esimerkiksi viimeisessä kentässä on oma musiikkinsa ja kaikissa muissa randomilla kolme eri musiikkia. Sehän on silloin pakko tehdä While silmukan sisällä jotta se vaihtaa musiikin. Tai jos kutsun while silmukan sisällä esim:

Musa m = LataaMusiikki("musa.wav");
Musa a = LataaMusiikki("musa2.wav");
m.toista(); //Alkuvalikkoon tulee nyt tämä
while(peli) {
  ...
  if (esc_painettu) peli = false;
  ...
  piirto(); jne();
  if (kentta==2)
  {
    KentanKaksiPiirto();
    a.toista(); //Tästä tulee errori että ei tiedä mikä on a.
//Eli: error: `a' was not declared in this scope
  }
}
m.vapauta();

Nyt toivottavasti selitin oikein mitä ajan takaa. Miten saan vaihdettua musiikkia kesken pelin, kun pelin kaikki ominaisuudet ovat funktioiden sisällä eli myös musiikit on laitettava funktioon jolloin se ei löydä arvoaan. Jos taas käytän lataukseen erillistä funktiota joka palauttaa kuvan arvon tilttaa koko kone syystä, että keskusmuisti on niin käytössä kuin se vain kykenee.

mika132 [09.03.2010 22:48:10]

#

Tämä ei kyllä helpottanut. Nyt on koko engine sekaisin, mutta siis ei olisi mitään muuta tapaa tehdä samaa kuin Vehkis91 neuvoma kun en näe järkeä laittaa koodia josta en ymmärrä yhtään riviä.

Jokotai [09.03.2010 22:56:17]

#

Mitä et Vehkis91:sen koodista ymmärrä.

mika132 [09.03.2010 23:48:45]

#

Mitä ihmeitä tässä ovat kaikki:
ohjelma::musat.at(index).Voimakkuus(100);


ja tässä:
ohjelma::musat.push_back(LataaBiisi("BIISIN_NIMI­");


E: Ja tuo std::vector<Musa> musat anta erroria:

ohjelma.cpp:11: error: expected constructor, destructor, or type conversion before '<' token

Legu [10.03.2010 00:45:28]

#

Ei tuo vehkiksen koodi poista ongelmaasi millään tavalla.

Jos kerran ongelma on "error: `a' was not declared in this scope", niin määritä a vaikka sitten globaaliksi. Se on yksinkertaisin tapa, mikä varmaan sopii sinulle parhaiten...
Entä kuvat? Miten käsittelet niitä? Ethän niitäkään lataile missään while-loopissa koko ajan pelin aikana? Käsittele noita Musa-objekteja samaan tapaan kuin SDL_Surfacejakin, paitsi että kuvat piirretään joka kierroksella kun taas musiikki toistetaan vain kerran.

mika132 [10.03.2010 15:47:52]

#

Jes. Sain sen toimimaan, kylläkin samalla konstilla kuin kuvia käsitellään. Eli kiitos paljon. =)

Olisi vielä kaksi asiaa:
Luin netistä en muista enää mistä, että mixeri voi soittaa MP3 tiedostojakin erillisellä dll kirjastolla. Latasin kirjaston, mutta en tiedä mikä se funktio on? MP3 tiedostot vain on likemmäs 10x pienempiä kuin Wav niin pelin koko kasvaa aivan uskomattomiin mittoihin hyvin nopeasti.

Toinen kysymykseni on kun tein "Työkalun" jossa voi liikuttaa eri hahmoja. Eli suomeksi työkalu jolla voi tehdä välianimaatioita ilman, että animoitsisin ne erikseen. Suunnitelmassa oli ottaa Frasp nimisellä ohjelmalla videokuvaa välipätkistä ja editoida ne Movie Makerillä. Muistaakseni Movie Maker ei tee .avi tiedostoja vaan .WMV eli miten saan näytettyä tuollaisen frormaatin pätkän? Jos onnistuu vain .avi tiedostoille niin voihan tuon aina muuttaa ohjelmilla eri formaattiin, mutta siitä ei ole toistaiseksi paljon hyötyä kun ei ole mitään tietoa millä funktioilla saadaan "video" toistettua pelissä.

mika132 [10.03.2010 17:27:35]

#

hmm... Olen nyt koittanut tehdä ampumista (tällä hetkellä ampuminen tapahtuu pelkästään x suuntaan koska haluan sen toimimaan ennen kuin pistän sen menemään suunta kohtaan ja osaan tehdä sen, mutta en tätä)

//ohjelma.cpp on seuraavsti:
        if (pelaaja.shooting>=1)
        {
            for (float lb=pelaaja.shooting; lb >= 1; lb--)
            {
                piirra_kuva(kuvat::bull, b.x, b.y);
                b.x+=b.panoksennopeus;
                if (b.x >= 600)
                {
                    pelaaja.shooting-=1;
                    b.x=pelaaja.x;
                    b.y=pelaaja.y;
                }
            }
        }
        if (pelaaja.shooting == 0)
        {
            b.x=pelaaja.x;
            b.y=pelaaja.y;
        }
//peli.cpp on seuraavsti. Funktiot on esitelty kyllä namespace peli:ssä:
peli::bullet peli::uusi_panos(int aseena, pelaaja& m) {
	std::clog << "peli::pelaaja peli::uusi_panos(" << aseena << ")" << std::endl;
	bullet b;
	b.panoksensuunta=m.suunta;
	b.x=m.x;
	b.y=m.y;
	b.panoksennopeus=1;
	return b;
}


//tässä ampumis systeemi ja se on sisälletty funktioon "PelaajanLiikutus"
        int napittt, x, y;
        napittt=SDL_GetMouseState(&x, &y);
        if(napittt & SDL_BUTTON(1)) {
            bullet b = peli::uusi_panos(m.ase, m);
            m.shooting+=1;
            SDL_Delay(100); //Estää sen että tulee kaksi panosta kerralla.
        }
//peli.hpp sisältää sitten nämä oliot:

    struct bullet {
        float panoksensuunta, x, y, panoksennopeus;
    };
    struct pelaaja {
        struct timo {
            float x, y;
            float x0, y0;
			timo *nenampi, *hannampi;
        };
        float x, y, paikkax, paikkay, seuraavapalikka, seuraavapalikkax, katsottutama;
        bool pistooli, haulikko, uzi, kirves, life;
        int pistooli_panokset, pistooli_panokset_max, haulikko_panokset, haulikko_panokset_max, uzi_panokset, uzi_panokset_max;
        timo *nena, *hanta;
        int huone;
        float suunta, vaihe, elama, ase, shooting;
    };

Ongelmaan:
Se kyllä ampuu panoksen ja se menee x akselia kohtaan 600 ja sitten "katoaa" koska sitä ei kuulu enää piirtää. (Ei kylläkään poisteta vielä muistista, mutta teen sen kyllä) sitten kun painan uudestaan hiiren ykkös nappia tulee todellakin taas panos kuten pitää ja tekee saman, mutta jos painan hiiren nappia ennen kuin panos saavuttaa "tuhoutumis" kohteensa ei tule uutta panosta vaan sillä hetkellä näkyvissä olevan panoksen nopeus kasvaa ja kun se saavuttaa määränpään katoaa jonka jälkeen tulee ilman että teen mitään uusi panos, joka menee myös määränpäähän ja tuhoutuu.

Eli kuvassa ei voi olla kahta panosta samaan aikaan, koska käytän olioita väärin, mutta enpä ole täysin varma miten käytän niitä nyt väärin.

vehkis91 [10.03.2010 18:09:39]

#

Sun pitää luoda aina uusi olio, kun ammut.

mika132 [10.03.2010 19:04:05]

#

No olen nyt väsännyt jotain ja mielesäni nyt pitäisi tulla uutta oliota, mutta sitä panos oliota ei näy missään joten en tiedä että liikkuuko se edes johonkin.

//ohjelma.cpp pelin piirto funktiossa oleva koodi:
        if (pelaaja.shooting >= 1)
        {
        for (peli::bullet::pistoolin_panos *p = b.hanta; p != 0; p = p->nenampi) {
            b.x+=1;
            piirra_kuva(kuvat::bull, b.x * p->x, b.y * p->y, false);
            if (b.x>=700)
            {
                pelaaja.shooting-=1;
            }
        }
        }
        if (pelaaja.shooting == 0)
        {
            b.x=pelaaja.x;
            b.y=pelaaja.y;
        }
//peli.cpp
peli::bullet peli::uusi_panos(int aseena, pelaaja& m) {
	std::clog << "peli::pelaaja peli::uusi_panos(" << aseena << ")" << std::endl;
	bullet b;
	b.panoksensuunta=m.suunta;
	b.x=m.x;
	b.y=m.y;
	b.panoksennopeus=1;
	b.nena = b.hanta = new bullet::pistoolin_panos;
	b.nena->nenampi = b.hanta->hannampi = 0;
	b.nena->x = b.nena->x0 = (pelialue.x0 + pelialue.x1) / 2;
	b.nena->y = b.nena->y0 = (pelialue.y0 + pelialue.y1) / 2;

    UusiPanos(b);

	return b;
}

void peli::UusiPanos(bullet& b) {
    bullet::pistoolin_panos *p = new bullet::pistoolin_panos;
	*p = *b.nena;
	p->hannampi = b.nena;
	b.nena->nenampi = p;
	b.nena = p;
}

//hahmon liikutus funktiossa:
        int napittt, x, y;
        napittt=SDL_GetMouseState(&x, &y);
        if(napittt & SDL_BUTTON(1)) {
            m.shooting+=1;
            UusiPanos(b);
            SDL_Delay(100);
        }

//aja funktio:
int peli::aja(float uusi_vai_vanha) {
    pelaaja m = uusi_pelaaja(uusi_vai_vanha);
    bullet b = peli::uusi_panos(m.ase, m);
}

vehkis91 [10.03.2010 19:12:08]

#

Eli laitat vectoriin tai listiin niitä panoksia

#include <vector>

//tämä sisältää kaikki pelin panokset, ja tämä laitetaan peli namespaceen
std::vector<bullet::pistoolin_panos> panokset;

Saat ite miettiä loput, olisi jo aika opetella käyttäämään taulukoita. -.-

Sitä paitsi ei oo hienon näköistä, ku oot kopsannu Metabolixin ohjelmasta koodia, jonka nimitys ei liity millään lailla omaan peliisi.

mika132 [10.03.2010 21:21:23]

#

No yritinpäs jotain..

//peli.cpp:
namespace peli {
    std::vector<bullet::pistoolin_panos> panokset;
    static void UusiPanos(bullet& b, pelaaja& m);

}


void peli::UusiPanos(bullet& b, pelaaja& m) {
    bullet::pistoolin_panos *p = new bullet::pistoolin_panos;
    panokset.push_back(int(m.x));
	panokset.push_back(int(m.y));
}
//ohjelma.cpp
    for (peli::bullet::pistoolin_panos *p = b.hanta; p != 0; p = p->nenampi) {
         vector<string>::iterator panokset = vec.begin();
        std::cout << *panokset++ << " ";
        std::cout << *panokset++ << " ";

        piirra_kuva(kuvat::bull, panokset.x * p->x, panokset.y * p->y, false);
        if (b.x>=700)
        {
            pelaaja.shooting-=1;
        }
    }

Heittää errorin peli.cpp:stä.

error: no matching function for call to `std::vector<peli::bullet::pistoolin_panos, std::allocator<peli::bullet::pistoolin_panos> >::push_back(int)'


Eli missä vika? Mitä teen väärin? käytän ekaa kertaa vectoreita eli tein tuota oppaasta eli en tiedä onko lähellekkään oikein. En kyllä tiedä edes sitä mitä hyödyn tuosta ajatellen x,y akselia ja panoksen liikuttamista sekä sitä että peli näyttää enemmän kuin yhden panoksen.

Teuro [10.03.2010 21:30:02]

#

Kun käännät tuon virheilmoituksen Suomeksi, niin pääset aika hyvin jyvälle, että miksi tuo on väärin. Yrität tunkea peli::bullet::pistoolin_panos tyyppiseen vektoriin int tyyppistä tietoa. Tee ensin vähän harjoituksia tuon ohjelmoinnin kanssa, ennen kuin jatkat tuon SDL:n kanssa.

Siihen vektoriin pitää laittaa noita ammusolioita ei niiden jäsenmuuttujia.

mika132 [10.03.2010 21:47:58]

#

Ei tule mitään olen kaikkia muotoja yrittänyt mitä tiedän:

void peli::UusiPanos(bullet& b, pelaaja& m) {
    bullet::pistoolin_panos *p = new bullet::pistoolin_panos;
    //Tähän yritetty
//Tässä yritykset
peli::panokset.push_back(p=uusi_panos(m.ase, m));
peli::panokset.push_back(int(p));
peli::panokset.push_back(float(p));
peli::panokset.push_back(static(p));
peli::panokset.push_back(struct(p));
peli::panokset.push_back(new bullet::pistoolin_panos);
peli::panokset.push_back(bullet::pistoolin_panos *p = new bullet::pistoolin_panos);
}

Kaikista tulee joku errori.

Metabolix [10.03.2010 21:50:45]

#

Otapa sitten järki käteen. Tässä on äärimmäisen yksinkertainen esimerkki, jota suoraan soveltamalla sinun pitäisi pystyä korjaamaan ongelma.

std::vector<int> vektori;
int luku = 1;
vektori.push_back(luku);

Katso tarkkaan ja ajattele kovasti, niin toivottavasti ymmärrät, mikä yrityksissäsi on pielessä ja mitä niille pitäisi tehdä. Kooditagit oikein kauniisti värittävät koodista ne kohdat, joihin pitäisi erityisesti kiinnittää huomiota.

trilog [10.03.2010 21:50:53]

#

Kannattaisi varmaan ihan oikeasti unohtaa se peli vähäksi aikaa ja palata ihan perusharjoituksien pariin. Ensiksi voisit vaikka opetella tietotyypit ja niiden merkitykset.

Teuro [10.03.2010 22:04:03]

#

Tässä pieni esimerkki vektorin käytöstä.

#include <iostream>
#include <vector>

/* Yksinkertainen malliluokka */

class Omaluokka{
	private:
		int x;
		int y;
	public:
		Omaluokka(int x, int y){
			this->x = x;
			this->y = y;
		}

		int anna_x(){
			return x;
		}

		int anna_y(){
			return y;
		}
};

/* Globaali vektori tyypiltään Omaluokka ei suinkaan int */
std::vector <Omaluokka> Oliot;

int main(){
	Oliot.push_back(Omaluokka(22, 776));
	Oliot.push_back(Omaluokka(856, 7745));

	std::vector<Omaluokka>::iterator selaaja = Oliot.begin();
	std::vector<Omaluokka>::iterator lopussa = Oliot.end();

	while(selaaja != lopussa){
		std::cout << "x = " << selaaja->anna_x() << " y = " << selaaja->anna_y() << std::endl;

		++selaaja;
	}

	return EXIT_SUCCESS;
}

Ongelmasi on selkeä ja edelleen virheilmoitus on aivan helposti todettavissa. Yrität jotakin aivan kummallista tyyppikonversiota, jota ei todellakaan kuulu tehdä.

mika132 [11.03.2010 01:10:34]

#

Tuo pelihän jo melkein käynnistyi. Vaikka Buildaan sen niin se ilmoittaa että peliä ei ole buildattu tehdäänkö nyt ja jos teen niin tulee vain build logiin:

Linking console executable: bin\Debug\Timo ja Tomi - Saarella.exe
obj\Debug\data\valikko.o: In function `_ZSt8_DestroyIPN4peli6bulletEEvT_S3_':
D:/Code Block/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/ext/new_allocator.h:(.bss+0x0): multiple definition of `peli::panokset'
obj\Debug\data\peli.o:D:/Code Block/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_algobase.h:(.bss+0x0): first defined here
obj\Debug\main.o: In function `_ZN9__gnu_cxx13new_allocatorIN4peli6bulletEE10deallocateEPS2_j':
D:/Code Block/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_vector.h:(.bss+0x0): multiple definition of `peli::panokset'
obj\Debug\data\peli.o:D:/Code Block/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_algobase.h:(.bss+0x0): first defined here
obj\Debug\data\ohjelma.o: In function `_ZNSaIN4peli6bulletEED1Ev':
C:/Documents and Settings/Mika.KOTI-8FEC3C6EF1/Työpöytä/tyopoydan_kansiot/CPelit/Timo ja Tomi - Saarella/data/ohjelma.cpp:(.bss+0x0): multiple definition of `peli::panokset'
obj\Debug\data\peli.o:D:/Code Block/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_algobase.h:(.bss+0x0): first defined here
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 1 seconds)
0 errors, 0 warnings

Eli sehän palauttaa arvon yksi välittömästi pelin alettua jos ymmärsin oikein, mutta miksi. Annan nyt tuohon vectoriin liittyvää koodia, koska ongelma tuli heitettyäni seuraavat pätkät tuonne:

//peli.hpp:
#ifndef _PELI_HPP
#define _PELI_HPP

#include "pause.hpp"
#include <vector>

namespace peli {
    struct bullet {
        private:
            float x;
            float y;
        public:

        bullet(int x, int y){
            this->x = x;
            this->y = y;
        }

        int anna_x(){
            return x;
        }

        int anna_y(){
            return y;
        }
    }

    std::vector<bullet> panokset; //Tämä tässä siksi, että pystyn käyttämään sitä ohjelma.cpp:ssä
}
//ohjelma.cpp
//Täällä on namespace ohjelma, mutta siinä ei ole mitään tähän liittyen.
//ja tämä koodi on void piirra_peli(); funktion sisällä. eikä tarvitse siitä
//mainita minkä funktion sisällä se on koska samaa asiaa se funktio tekee kuin //mikä tahansa muu.

std::vector<peli::bullet>::iterator selaaja = peli::panokset.begin();
std::vector<peli::bullet>::iterator lopussa = peli::panokset.end();

    while(selaaja != lopussa)
    {
       std::cout << "x = " << selaaja->anna_x() << " y = " << selaaja->anna_y() << std::endl;
       selaaja++; //eiks tää kuulu näin päin eikä ++selaaja
       //en vielä laittanut mutta kuvan tulostus ilmeisesti tehdään tähän näin:
       //piirra_kuva(kuvat::bull, selaaja->anna_x(), selaaja->anna_y(), false)
    }

Mikä ihme menee väärin ja jos ei mikään niin mistä ihmeestä tuo tulee, että ei muka ole buildattu. :O

Teuro [11.03.2010 07:39:41]

#

Build logissa lukee ihan selkeästi edelleen, että peli::panokset on määritelty useampaan kertaan. Kokeilitko tuota esimerkkiä vektoreista ollenkaan? Se kääntyy varoituksitta ja toimii juuri kuten pitääkin. Pieni detalji tuosta koodistasi vielä. struct ei sisällä funktioita, vaan sinun tulisi käyttää class sanaa. Kannattaa ehkä kuitenkin tehdä vielä noita ihan tekstiohjelmia.

Metabolix [11.03.2010 11:44:02]

#

Teuro: struct ja class toimivat aivan samalla tavalla, paitsi että structissa oletusnäkyvyys on public ja classissa private. Ei ole syytä käyttää juuri tiettyä näistä.

Itse ongelma on, että vektori on määritelty otsikossa, vaikka se pitäisi määritellä jossain cpp-tiedostossa. Kehotan taas kerran palaamaan C++-opassarjan osaan 6.

mika132 [11.03.2010 18:24:17]

#

Kiitos. Sain sen vihdoin toimimaan, mutta kun vectorit ovat täysin uusi asia niin. Mites noita vectoreita tuhotaan?

//Tiedän kyllä suurin piirtein miten olioita tuhotaan, mutta eikös seuraava konsti
//tuhoa minun kaikki bullet oliot eikä vain yhtä.

delete bullet;

//Tässä muutama muu yritys
//Tässä nyt tämä vectiorin muoto:
std::vector<bullet> panokset;
void ohjelma::TuhoaPanos(selaaja& s) {

	delete s;
}

void ohjelma::TuhoaPanos() {

	delete panokset;
}

errorit niistä tuli ja järkikin sanoo nyt että ei voi edes olla oikein, mutta en keksi enkä löydä googlellakaan sitä oikeaa muotoa.

vehkis91 [11.03.2010 19:44:42]

#

std::vector<bullet> panokset;
panokset.clear();

¨

Tuo poistaa kaikki alkiot panokset vectorista, eli tyhjentää sen.

Edit: ehkäpä std::list voisi olla parempi containeri panoksille, sieltä voi poistaa helposti välistä panoksia jne. http://www.cplusplus.com/reference/stl/list/

mika132 [11.03.2010 21:08:17]

#

En halua tehdä koko soppaa uudestaan eli päätin, että teen näin:

Kun kaikki panokset ovat menneet yli tietyn rajan ne tuhotaan.

Yritykseni:

if (selaaja->anna_x() >= 800)
{
    panokset.clear();
}
else if (selaaja->anna_x() <= 0)
{
    panokset.clear();
}
else if (selaaja->anna_y() >= 600)
{
    panokset.clear();
}

else if (selaaja->anna_y() <= 0)
{
    panokset.clear();
}

tilanteesta ei käytännössä tule erroria, koska peli aukeaa ja tekee kyllä kaikkea, mutta kun painan hiiren ykkösnappia eli ammun niin peli kaatuu ja heittää windows errorin 5 joka muistaakseni tarkoitti "käyttö estetty" ja kirjoittaa build logiin seuraavaa:

Checking for existence: C:\Documents and Settings\Mika.KOTI-8FEC3C6EF1\Työpöytä\tyopoydan_kansiot\CPelit\Timo ja Tomi - Saarella\bin\Debug\Timo ja Tomi - Saarella.exe
Executing: "D:\Code Block\CodeBlocks/cb_console_runner.exe" "C:\Documents and Settings\Mika.KOTI-8FEC3C6EF1\Työpöytä\tyopoydan_kansiot\CPelit\Timo ja Tomi - Saarella\bin\Debug\Timo ja Tomi - Saarella.exe"  (in C:\Documents and Settings\Mika.KOTI-8FEC3C6EF1\Työpöytä\tyopoydan_kansiot\CPelit\Timo ja Tomi - Saarella\.)
Process terminated with status -1073741819 (0 minutes, 16 seconds)

mika132 [12.03.2010 00:18:34]

#

Juu tuon lisäksi tuli aivan käsittämätön errori. Tai itseasiassa se ei ilmeisesti ole errori vaikka se on errori, mutta siis asiaan. Se ei kerro mitään riviä missä errori on vaan seuraavaa:

KUVA

Metabolix [12.03.2010 11:12:19]

#

Miksi poistat kaikki panokset, kun yksi menee yli? Luultavasti yrität käyttää edesmenneitä listan osia jollain tavalla tuon poistamisen jälkeen.

Minusta järkevin ratkaisu harjoittelun kannalta olisi toteuttaa panokset linkitettynä listana samalla tavalla kuin madon jaokkeet oppaassa. Jos et ymmärrä, lue lisää oppaita. Kaikki tarvittava neuvotaan C++-oppaissa, ja itse idea selitetään matopelioppaassa. Matopelissä lisätään jaokkeita vain yhteen kohtaan listassa, mutta sinun täytyy pystyä soveltamaan ideaa myös muualle lisäämiseen ja poistamiseen.

mika132 [12.03.2010 18:58:28]

#

MIstähän tälläinen errori teksti voi yleensä johtua:

KUVA

Metabolix [12.03.2010 19:01:37]

#

Useimmiten virheellisestä osoittimesta tai virheellisestä taulukon indeksistä.

Tuo ilmoitus on yksi Windowsin upeimmista käännösvirheistä. Oikea ilmoitus on "memory cannot be read" eli "muistia ei voida lukea". Viestistä on myös written-versio. Millä lie käännetty, käännöskoneellako?

Grez [12.03.2010 19:23:00]

#

Siellä on todennäköisesti kolme eri käännettävää tekstiä, jotka eivät ole kääntäjälle näkyvästi kytköksissä toisiinsa

"Memory cannot be"
"read"
"written"

mika132 [12.03.2010 20:01:35]

#

Ei ole tekstiä vaan.

void ohjelma::LuoPanos(peli::pelaaja& m) {
	peli::pelaaja::panos *j = new peli::pelaaja::panos;
	*j = *m.sijainti;
	j->sijanninpi = m.sijainti;
	m.sijainti->sijanninpi = j;
	m.sijainti = j;
}

if (pelaaja.shooting >= 1)
{
    for (peli::pelaaja::panos *j = pelaaja.hanta; j != 0; j = j->nenampi)
    {
        piirra_kuva(kuvat::bull, pelaaja.x * j->x, pelaaja.y * j->y, false);
    }
}
else if (pelaaja.shooting <= 0)
{

}
int napittt, x, y;
napittt=SDL_GetMouseState(&x, &y);
if(napittt & SDL_BUTTON(1)) //tapahtuu tätä painaessa.
{
   pelaaja.shooting+=1;
   LuoPanos(pelaaja);
   SDL_Delay(100);
}

Metabolix [12.03.2010 20:32:37]

#

Laitahan ensin jotain järkeä noihin muuttujien nimiin. Missään tapauksessa "sijainninpi" ei ole ymmärrettävä nimi, ja lisäksi siinä on kirjoitusvirhe. Madon tapauksessa nenampi ja hannampi olivat hyvät nimet linkitetyn listan osoittimille, koska ne kertoivat, missä kyseinen jaoke sijaitsee edelliseen nähden. Panosten kohdalla voit käyttää nimiä edellinen ja seuraava. (Madon tapauksessa olisi ollut epäselvää, onko "seuraava" nenää vai häntää kohti.)

Piirrä kaavio, jossa on peräkkäin kolme palloa (panosta) ja kaikista menee nuolet edelliseen ja seuraavaan (paitsi niistä, joilla ei ole edellistä tai seuraavaa) ja seuraavaan. Jokainen nuoli kuvastaa yhtä osoitinta. Puuttuvien nuolien tapauksessa osoittimeen pitää sijoittaa arvo 0.

Lisää sitten kaavioon neljäs panos ja piirrä puuttuvat nuolet. Toteuta seuraava funktio (oit katsoa mallia matopelistä):

panos* uusi_panos() {
  panos* p = new panos;
  // Aseta tässä välissä osoittimet järkevästi.
  return p;
}

Nyt voit luoda pelissä panoksen seuraavasti:

if (ammutaan) {
  panos* p = uusi_panos();
  p->sijainti = pelaaja.sijainti;
  // ...
}

Vedä kaavion keskeltä panos yli ja korjaa muiden viivat kohdalleen. Toteuta sitten seuraava funktio:

void poista_panos(panos* p) {
  // Aseta tässä välissä osoittimet järkevästi.
  // Mitkä osoittimet osoittavat panokseen p?
  // Mihin niiden pitää osoittaa, kun p poistetaan?
  // Muista tarkistaa, onko p ensimmäinen tai viimeinen!
  // p:n omilla osoittimilla ei ole merkitystä, koska p tuhotaan.
  delete p;
}

mika132 [12.03.2010 21:34:52]

#

Sulkee pelin valikon jälkeen kun painan "aloita peli". Päätin sitten käyttää noita matopelin nimiä "nenampi, hannanmpi"

struct pelaaja {
    struct panos {
        float x, y;
        float x0, y0;
        float sijaintix, sijaintiy;
	panos *nenampi, *hannampi;
};
panos *nena, *hanta;
float x, y, paikkax, paikkay, seuraavapalikka, seuraavapalikkax, katsottutama;
bool pistooli, haulikko, uzi, kirves, life;
int pistooli_panokset, pistooli_panokset_max, haulikko_panokset, haulikko_panokset_max, uzi_panokset, uzi_panokset_max;
int huone;
float suunta, vaihe, elama, ase, shooting;
    };
peli::pelaaja::panos ohjelma::uusi_panos() {
    peli::pelaaja::panos p;
    peli::pelaaja m;

    m.nena = m.hanta = new peli::pelaaja::panos;
	m.nena->nenampi = m.hanta->hannampi = 0;

    return p;
}
void ohjelma::poista_panos_nyt(peli::pelaaja& m) {


    while (m.nena != m.hanta) {
        // Siirretään nenäosoitinta, tuhotaan vanha jaoke.
        m.nena = m.nena->hannampi;
        delete m.nena->nenampi;
        m.nena->nenampi = 0;
	}
	// Tuhotaan viimeinen jaoke.
	delete m.nena;
	m.nena = m.hanta = 0;
}



//Nämä ovat myös funktiossa, mutta en laittanut sitä enää sotkemaan tätä.




int napittt, x, y;
napittt=SDL_GetMouseState(&x, &y);
if(napittt & SDL_BUTTON(1)) {
    peli::pelaaja::panos  p = uusi_panos();
    p.sijaintix = pelaaja.x;
    p.sijaintiy = pelaaja.y;
    SDL_Delay(100);
}

for (peli::pelaaja::panos *p = pelaaja.hanta; p != 0; p = p->nenampi)
{
    piirra_kuva(kuvat::bull, p->sijaintix * p->x, p->sijaintiy * p->y, false);
}

Metabolix [12.03.2010 21:50:21]

#

Ei tuossa nyt valitettavasti ole mitään järkeä. Mieti uudestaan ja muistele vähän opasta muuttujien elinajoista ym. Mitä esimerkiksi uusi_panos-funktiossa tapahtuu? Entä mihin oikean pelaajan panos*-jäsenet osoittavat? Myöskään panoksen poistaminen ei onnistu samalla tavalla kuin madon tuhoaminen, koska matopelissä poistetaan kaikki kerralla mutta sinun pitäisi poistaa vain yksi välistä.

Mikset voinut tehdä, kuten viestissäni sanoin? Annoin funktioiden tyypit ja luontifunktion käyttöesimerkinkin. Miksi et noudata neuvoja?

mika132 [12.03.2010 21:57:02]

#

Siksi kun heitti erroria en muista enää mitä, kun se * merkki oli siellä jokapaikassa.

Metabolix [12.03.2010 22:00:55]

#

Tiedoksesi, että virhe on silloin ollut omassa koodissasi ja siinä, ettet ymmärrä tehdä siihen tarvittavia muutoksia vaan yrität kopioida kaikki annetut koodit sellaisenaan johonkin väliin. Älä muuta annetuista koodeista olennaisia asioita kuten muuttujien tyyppejä (varsinkaan sitä, mikä on osoitin ja mikä ei), vaan korjaa oma koodisi.

mika132 [12.03.2010 22:21:40]

#

No juu vika oli koodissani alun alkaen mutta olen saanut tämän errorin useasti enkä tiedä aivan tarkkaan miten sen olen korjannut joten nyt kysyn, että mistä johtuu. Ilmeisesti vika on siinä että sitä ei muka lopeteta lainkaan, mutta kyllä siellä on {} merkit:

error: 'peli::pelaaja::panos* ohjelma::uusi_panos()' used but never defined

Metabolix [12.03.2010 22:36:50]

#

Et kai ole määritellyt funktiota staattiseksi? Lue uudestaan C++-oppaan kuudennesta osasta "käännösyksiköt ja liitosalueet".

mika132 [12.03.2010 22:51:39]

#

No miksihän sitten määrittelen ne?
extern vai static?

Tällä hektellä:

static void poista_panos(peli::pelaaja::panos* p);
static peli::pelaaja::panos* uusi_panos();

vehkis91 [12.03.2010 23:07:05]

#

iha vaa funktioksi, ei siin tarvii olla externiä, eikä static.

mika132 [12.03.2010 23:32:42]

#

Ei toimi vieläkään. Yritin laittaa if(pelaaja.shooting >= 1) jos sitten mutta tekee saman painaessani hiirtä ja ilman tuota ei edes käynnistä peliä valikon jälkeen. ELi ongelma on seuraavassa kohdassa ja tuo clog on siellä siksi että näen suorittaako se koko silmukkaa, mutta ei suorita. Se kaatuu tuohon piirra_kuva() funktioon koska tuo oli ennen piirraKuva funktioita ja se näytti sen mutta ei enää sen jälkeistä. En tiedä mitä sitten teen tuossa väärin.

for (peli::pelaaja::panos *p = pelaaja.hanta; p != 0; p = p->nenampi)
{
    piirra_kuva(kuvat::bull, p->sijaintix * p->x, p->sijaintiy * p->y, false);
    std::clog << "FOR silmukan kuva piirretty" << std::endl;
}

Metabolix [13.03.2010 06:51:04]

#

Luultavasti pelaaja.hanta sisältää virheellisen arvon. Kehotin jo aiemmin miettimään, mitä kummaa uusi_panos-funktiossa tapahtuu. (Kirjoititko funktion kommentin paikalle jotain järkevää?) Samalla pitäisi miettiä, millä arvoilla pelaajan panoslista on alustettu vai onko ollenkaan.

Muuten, piirtokoordinaateissasi ei ole paljonkaan järkeä. Miksi ihmeessä panoksella on erikseen x ja sijaintix? Miksi ne yhdistetään kertolaskulla? Haiskahtaa taas siltä, että olet yrittänyt korjata jotain yksinkertaista ongelmaa vain kokeilemalla satunnaisesti täysin epäloogisia vaihtoehtoja.

mika132 [15.03.2010 02:19:06]

#

No tässäpä on kommentoituna:

peli::pelaaja::panos* ohjelma::uusi_panos() {
    peli::pelaaja::panos* p = new peli::pelaaja::panos; //Luodaan uusi olio "panos"
    return p; //Palautetaan tieto.
}
int napittt, x, y; //alustetaan hiiren käsittelyä varten
napittt=SDL_GetMouseState(&x, &y); //laitetaan napittt muuttujaan sisälle hiiren sijainti
if(napittt & SDL_BUTTON(1)) { //jos painetaan hiiren ykkös nappia
    peli::pelaaja::panos* p = uusi_panos(); //tehdään uusi panos
    p->sijaintix = pelaaja.x; //asetetaan sijaintix pelaajan x arvoon
    p->sijaintiy = pelaaja.y; //sama y:lle
    pelaaja.shooting++; //lisätään yksi shooting (en tiedä tarvitaanko tätä)
    SDL_Delay(100); //odotetaan hetki jottei tapahdu kahta kertaa. (tähän minulla on eri systeemi mutta ei vielä käytössä)
}

if (pelaaja.shooting >= 1) //jos shooting on enemmä ko yks tai yhtäpaljo
{
    for (peli::pelaaja::panos *p = pelaaja.hanta; p != 0; p = p->nenampi) //rehellisesti sanoe en tie mitä tää tekee.
    {
        piirra_kuva(kuvat::bull, p->sijaintix * p->sijaintix, p->sijaintiy * p->sijaintiy, false); //piirretään kuva. Tästä olen noin 80% tietoinen mitä tekee
//mutta en varma enää siitä kun siinä kerrotaan jotain ja miksi tuo on muotoa:
//p->sijaintix miksi ei p.sijaintix
    }
}

Edelleen sama ongelma. Peli sammuu kun painaa hiiren ykkösnappia ja suorittaa koodin tuonne piirra_kuva funktioon asti tai for silmukkaan. En ole täysin varma.

Päärynämies [15.03.2010 04:28:06]

#

mika132 kirjoitti:

rehellisesti sanoe en tie mitä tää tekee.

Tämä kommentti tuolta välistä yksistään kertookin jo paljon. Lue ja harjoittele oikeasti noita perusjuttuja. Eihän tuosta mitään tunnu tulevan. Kopioit ja kokeilet vain koodia ymmärtämättä sitä.

Tämänkin taidettu jo pariin kertaan sanoa, eikä syyttä.

Metabolix [15.03.2010 10:38:16]

#

mika132 kirjoitti:

Edelleen sama ongelma.

Ja edelleen sama vastaus. Lue uudestaan antamani koodi:

// Aseta tässä välissä osoittimet järkevästi.

Minne hukkasit tämän rivin? Mikset tehnyt, kuten kommentissa käsketään? Viestissä, jossa esitin koodin, kerroin myös hyvin seikkaperäisesti, miten voisit paperilla tutkia linkitetyn listan toimintaa. Noudatitko neuvoa? Piirrä ja skannaa, tai piirrä koneella, ja muistakin tehdä selkeästi, jotta muutkin näkevät, oletko tehnyt oikein. Jos ei onnistu, yritä uudestaan ja mieti kovasti. Olet saanut monta hyvää selostusta, joiden perusteella ongelmien pitäisi kyllä ratketa.

mika132 kirjoitti:

rehellisesti sanoe en tie mitä tää tekee.

Rehellisesti sanoen et ole siis tainnut lukea sen paremmin C++-opasta kuin matopeliopastakaan. Tarvittavat tiedot nimittäin kerrotaan ensin mainitussa, ja juuri kyseisen rivin tarkoitus kuvaillaan jälkimmäisessä.

mika132 kirjoitti:

piirretään kuva. Tästä olen noin 80% tietoinen mitä tekee mutta en varma enää siitä kun siinä kerrotaan jotain

Melkoista tyhmyyttä: olet itse kirjoittanut tuollaisen oudon kertolaskun, ja sitten et edes tiedä, mitä se tekee. Näinkö ajattelit päästä ohjelmoinnissa eteenpäin?

mika132 [15.03.2010 19:13:13]

#

Tiedän kyllä mitä for silmukka tekee en sitä tarkoittanut, mutta se mitä en nyt tajua niin on se, että mitä arvoja nyt tähän pitäisi saada:

// Aseta tässä välissä osoittimet järkevästi.

Ne nena nenampi jutut eivät auta kun heittää aina erroria että pelaaja * ei ole jotain vaikka se tunnistaa sen valituksi, mutta ei tajua silti käyttää. Ei vain nyt järki leikkaa, että mikä se oikea muoto siihen on.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta