Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Gaius

Pages: [1]
1
Graphics / Re: Drawing Text Access Violation
« on: August 14, 2014, 07:50:15 pm »
Thanks, That helped me solve the issue.

2
Graphics / Re: Drawing Text Access Violation
« on: August 14, 2014, 06:35:24 pm »
I'm fairly new to SFML, so the problem most likely lies with my code.
Minimal working example:

#include <SFML/Graphics.hpp>
class Text : public sf::Drawable
{
        sf::Font font;
        sf::Text text;
public:
        Text(std::string string){
                if (!font.loadFromFile("segoeuil.ttf")) // Nothing wrong with loading the font
                        std::exit(-1);
                text = sf::Text(string, font, 30);
        }
        Text(){ }

        virtual void draw(sf::RenderTarget & target, sf::RenderStates states) const{
                target.draw(text);
        }
};

int main(){
        sf::RenderWindow window(sf::VideoMode(800, 600), "Text test");

        Text text("Text");
    //Text text; -> same result

        // Error (on draw) -> Access Violation
        // Commenting this line out, solves the problem
        text = Text("Text");
       

        while (window.isOpen()){
                window.clear();
                window.draw(text);
                window.display();
        }

        return 0;
}

3
Graphics / Re: Drawing Text Access Violation
« on: August 14, 2014, 04:37:55 am »
I am using Visual Studio 2013 Ultimate. I compiled SFML with cmake and I have been using that version for a few months now.

I changed my code now so that I don't use my Text class anymore but I am still getting the same error.

Button.cpp seems to be causing it now with it's derived draw method:
#include "init.h"


ui::Button::Button(std::string text, size_t textSize, sf::Vector2f pos, sf::Color textColor, sf::Color buttonFaceColor, std::string font){
        this->font.loadFromFile(font);
        this->text.setFont(this->font);
        setString(text);
        setPosition(pos);
        setTextColor(textColor);
        setButtonFaceColor(buttonFaceColor);
}

std::string ui::Button::getString() const{
        return text.getString();
}

EntityR ui::Button::getButtonFaceObj() const{
        return buttonFace;
}

sf::Vector2f ui::Button::getPosition() const{
        return buttonFace.getPosition();
}

sf::Vector2f ui::Button::getButtonFaceSize() const{
        return buttonFace.getSize();
}

size_t ui::Button::getTextSize() const{
        return text.getCharacterSize();
}

sf::Color ui::Button::getTextColor() const{
        return text.getColor();
}

sf::Color ui::Button::getButtonFaceColor() const{
        return buttonFace.getColor();
}

void ui::Button::setString(std::string text){
        this->text.setString(text);
        setTextSize(getTextSize());
}

void ui::Button::setPosition(sf::Vector2f pos){
        buttonFace.setPosition(pos);
        text.setPosition(pos + sf::Vector2f(10, 10));
}

void ui::Button::setTextSize(size_t textSize){
        text.setCharacterSize(textSize);
        float width = 100;//text.getLocalBounds().width;
        float height = 50;//text.getLocalBounds().height;
        buttonFace.setSize(width + 20, height + 20);
}

void ui::Button::setTextColor(sf::Color color){
        text.setColor(color);
}

void ui::Button::setButtonFaceColor(sf::Color color){
        buttonFace.setColor(color);
}

bool ui::Button::isPressed(sf::Event event){
        if (event.type == sf::Event::MouseButtonPressed){
                if (event.mouseButton.button == sf::Mouse::Button::Left){
                        sf::Vector2f mouseLoc(event.mouseButton.x, event.mouseButton.y);
                        if (cd::doesIntersect(mouseLoc, buttonFace))
                                return true;
                }
        }
        return false;
}

bool ui::Button::isHovering(sf::Mouse mouse){
        if (cd::doesIntersect((sf::Vector2f)mouse.getPosition(), buttonFace))
                return true;
        return false;
}

void ui::Button::draw(sf::RenderTarget & target, sf::RenderStates states) const{
        target.draw(buttonFace);
        target.draw(text);
}

Here's Button.h as well:
#pragma once

namespace ui{

        class Button : public sf::Drawable
        {
                EntityR buttonFace;
                sf::Font font;
                sf::Text text;
        public:
                Button(std::string text, size_t textSize, sf::Vector2f pos, sf::Color textColor, sf::Color buttonFaceColor, std::string font);
                Button(){}

                std::string getString() const;
                EntityR getButtonFaceObj() const;
                sf::Vector2f getPosition() const;
                sf::Vector2f getButtonFaceSize() const;
                size_t getTextSize() const;
                sf::Color getTextColor() const;
                sf::Color getButtonFaceColor() const;

                void setString(std::string text);
                void setPosition(sf::Vector2f pos);
                void setTextSize(size_t textSize);
                void setTextColor(sf::Color color);
                void setButtonFaceColor(sf::Color color);

                bool isPressed(sf::Event event);
                bool isHovering(sf::Mouse mouse);

                virtual void draw(sf::RenderTarget & target, sf::RenderStates states) const;
        };
}
 


And here is where it is called from (Missile_Command.cpp):
void Missile_Command::init(){
//...
        _quit = ui::Button("Quit", 32, sf::Vector2f(200, 200), sf::Color::Black, sf::Color(255, 255, 255, 128), "fonts/segoeuil.ttf");
        _start = ui::Button("Start", 32, sf::Vector2f(200, 300), sf::Color::Black, sf::Color(255, 255, 255, 128), "fonts/segoeuil.ttf");
}

void Missile_Command::run(){
        while (_window.isOpen()){
                //...
                _window.draw(_quit);
         _window.draw(_start);
//...
        }
}

4
Graphics / Drawing Text Access Violation
« on: August 14, 2014, 02:00:38 am »
call stack:

>   sfml-graphics-d-2.dll!std::_Tree<std::_Tmap_traits<unsigned int,sf::Font::Page,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,sf::Font::Page> >,0> >::_Lbound(const unsigned int & _Keyval) Line 2109   C++
    sfml-graphics-d-2.dll!std::_Tree<std::_Tmap_traits<unsigned int,sf::Font::Page,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,sf::Font::Page> >,0> >::lower_bound(const unsigned int & _Keyval) Line 1575   C++
    sfml-graphics-d-2.dll!std::map<unsigned int,sf::Font::Page,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,sf::Font::Page> > >::operator[](const unsigned int & _Keyval) Line 226   C++
    sfml-graphics-d-2.dll!sf::Font::getGlyph(unsigned int codePoint, unsigned int characterSize, bool bold) Line 295   C++
    sfml-graphics-d-2.dll!sf::Text::ensureGeometryUpdate() Line 269   C++
    sfml-graphics-d-2.dll!sf::Text::getLocalBounds() Line 214   C++
    Engine.exe!Text::getLocalBounds() Line 41   C++
    Engine.exe!Button::setTextSize(unsigned int textSize) Line 56   C++
    Engine.exe!Button::setString(std::basic_string<char,std::char_traits<char>,std::allocator<char> > text) Line 47   C++
    Engine.exe!Button::Button(std::basic_string<char,std::char_traits<char>,std::allocator<char> > text, unsigned int textSize, sf::Vector2<float> pos, sf::Color textColor, sf::Color buttonFaceColor) Line 7   C++
    Engine.exe!Missile_Command::initButtons() Line 56   C++
    Engine.exe!Missile_Command::init() Line 29   C++
    Engine.exe!Missile_Command::Missile_Command() Line 6   C++
    Engine.exe!main() Line 4   C++
    [External Code]   
    [Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]

code:
void Text::draw(sf::RenderTarget & target, sf::RenderStates states) const{
        target.draw(text);
}

which was called by:

void Button::draw(sf::RenderTarget & target, sf::RenderStates states) const{
        target.draw(buttonFace);
        target.draw(text);
}

No pointers were used to my knowledge.

The exact error is as follows:

Unhandled exception at 0x0F21F95C (sfml-graphics-d-2.dll) in Engine.exe: 0xC0000005: Access violation reading location 0x0000000D.

I've been trying to solve this for quite some time now. I think it has something to do with my sf::Text object not being properly initialized or something, but I couldn't find anything.

Here's the my Text class if anyone's interested:

(click to show/hide)

"text" and "font" are private class members.

5
Graphics / Re: How to make Class drawable?
« on: August 11, 2014, 05:16:24 pm »
Don't forget the documentation, it contains an exemple of what you want to achieve in its detailed description. http://www.sfml-dev.org/documentation/2.1/classsf_1_1Drawable.php

That's what I meant by tutorial :P.

6
Graphics / Re: How to make Class drawable?
« on: August 11, 2014, 05:15:14 pm »
Now I guess that you wonder what to put in the draw function. It's simple: draw all the drawable SFML objects that your class has (VertexArray, Sprite, Text, Shape).

void MyEntity::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    target.draw(m_sprite, states);
    target.draw(m_text, states);
    ....
}

Yeah, that was what I was stuck at. Thanks, I think I get it now.

7
Graphics / How to make Class drawable?
« on: August 11, 2014, 04:48:33 pm »
I'd like to make a class I wrote in C++ drawable by typing:

Class class;
window.draw(class);

instead of:

class.draw(window);

I read through the tutorial on how to do this, but I don't really understand how to implement it into my code.
It would really help me out if someone could briefly explain what is meant in the tutorial or could possibly give me an example of how to do this.

8
General / Snake code review request
« on: July 31, 2014, 07:12:23 pm »
I've recently finished programming a clone of the popular "Snake" game in C++ with SFML and Visual Studio 12 and I would like to know if there is anything about the code or it's structure that I could improve. This is the second post I've made on this forum, the first was a Pong review request.
 
Game:
 

 
 
Files:
 


 
Code:
 
main.cpp
 
(click to show/hide)

Snake.h
(click to show/hide)

Snake.cpp
(click to show/hide)

Food.h
(click to show/hide)

Food.cpp
(click to show/hide)

Entity.h
(click to show/hide)

Entity.cpp
(click to show/hide)

SoundEffect.h
(click to show/hide)

SoundEffect.cpp
(click to show/hide)

globals.h
(click to show/hide)

9
General / Re: Pong code review request
« on: July 26, 2014, 12:35:48 am »
I'd strongly advise you to take the code you have here, and improve it with a design using classes.
Will do.

Speaking of loops, I'd read this article and see if you can implement a fixed-timestep game loop. It improves your game immensely.
I tried to implement that, but I can't seem to find a default high resolution timer for c++.
Edit: It looks like sf::Time could be what I'm looking for.

Careful mixing real-time input (sf::Mouse::getPosition()) and events. Personally, I would check for the MouseMove event and move the paddle with those values.
Was I mixing? I'll check out the MouseMove event.

  • Do you really need <Windows.h> and the ClipCursor? If so, you could put it in a separate file and compile conditionally, to keep portability
How would I compile a file conditionally?

10
General / Re: Pong code review request
« on: July 25, 2014, 08:06:01 pm »
  • You don't use classes
  • You use global variables

I thought, since this is a relatively small scale game, I could use global variables and one .cpp file without much consequence. Wouldn't it just over complicate the code to separate it into different files if there are only near 300 lines?

11
General / Pong code review request
« on: July 25, 2014, 07:21:33 pm »
So, I wrote a simple Pong game in C++ with SFML and Visual Studio 2013 and was wondering if somebody could tell me if there is anything I could improve upon in the code.
 
Noteworthy:
    - The collision between the ball and the top and bottom of the player panel seems to be slightly bugged
    - The full solution for visual studio can be found here: https://www.dropbox.com/s/558as04io6jr2b5/Pong.zip
    - To quit the game, press escape
 
Game:
 

 
 
Files:
 

 
Code: (main.cpp)

#include <iostream>
#include <string>
#include <vector>
#include <SFML\Graphics.hpp>
#include <SFML\Audio.hpp>
#include <Windows.h>
#pragma warning(disable: 4244)

#define SCRW    1000    // Screen width
#define SCRH    600             // Screen height
#define BALLW   20              // Ball width
#define BALLH   20              // Ball height 
#define PADDLEW 20              // Paddle width
#define PADDLEH 100             // Paddle height
#define BSPEED  -0.1f   // Ball origin speed
#define ESPEED  0.08f   // Enemy origin speed (player is mouse controlled)

sf::RenderWindow window(sf::VideoMode(SCRW, SCRH), "Pong");
sf::RectangleShape ball(sf::Vector2f(BALLW, BALLH));
sf::RectangleShape player(sf::Vector2f(PADDLEW, PADDLEH));
sf::RectangleShape enemy(sf::Vector2f(PADDLEW, PADDLEH));
float ballSpeedX = BSPEED;
float ballSpeedY = BSPEED;
float enemySpeed = ESPEED;
bool gameOver = false;
int playerPoints = 0;
int enemyPoints = 0;
sf::Font font;
sf::Text plScoreTxt;
sf::Text enScoreTxt;

void draw();
void moveBall();
void enemyAI();
bool intersect(sf::Vector2f, sf::Vector2f, sf::Vector2f);
float intersectPos(float, sf::RectangleShape);
void reset();

int main(){
        // Font and text stuff
        std::string fontname = "segoeuil.ttf";
        if (!font.loadFromFile(fontname))
        {
                std::cerr << "Font " << "\"" << fontname << "\" couldn't be/wasn't loaded. Please try again.\n";
        }
        plScoreTxt.setFont(font);
        plScoreTxt.setCharacterSize(200);
        plScoreTxt.setPosition(sf::Vector2f(window.getSize().x / 2 - 250, window.getSize().y / 2 - 150));
        enScoreTxt.setFont(font);
        enScoreTxt.setColor(sf::Color(255, 255, 255, 32));
        enScoreTxt.setCharacterSize(200);
        enScoreTxt.setPosition(sf::Vector2f(window.getSize().x / 2 + 150, window.getSize().y / 2 - 150));

        // Setting up the window ant entities
        window.setMouseCursorVisible(false);
        ball.setPosition(sf::Vector2f(window.getSize().x / 2 - BALLW / 2, window.getSize().y / 2 - BALLH / 2));
        enemy.setPosition(sf::Vector2f(window.getSize().x - PADDLEW, window.getSize().y / 2 - PADDLEH / 2));

        // Mouse confinement stuff
        RECT rect;
        rect.bottom = window.getPosition().y + window.getSize().y + 32 - PADDLEH / 2;
        rect.top = window.getPosition().y + 31 + PADDLEH / 2;
        rect.left = window.getPosition().x + 50;
        rect.right = window.getPosition().x + window.getSize().x - 42;

        ClipCursor(&rect);

        // Game loop
        while (window.isOpen()){
                sf::Event event;
                while (window.pollEvent(event)){
                        if (event.type == sf::Event::Closed){
                                window.close();
                        }
                        if (event.type == sf::Event::KeyPressed){
                                // Press escape to close the window
                                if (event.key.code == sf::Keyboard::Escape)
                                        window.close();
                        }
                }

                moveBall(); // Move the ball and test for collision
                player.setPosition(sf::Vector2f(player.getPosition().x, sf::Mouse::getPosition(window).y - PADDLEH / 2)); // Move the player paddle to mouse position
                if (ballSpeedX > 0) // Perform enemy AI stuff when the ball is heading in it's direction
                        enemyAI();
                if (gameOver){ // if someone scored, reset the game
                        reset();
                }

                // Text stuff
                plScoreTxt.setString(std::to_string(playerPoints));
                enScoreTxt.setString(std::to_string(enemyPoints));
                plScoreTxt.setColor(sf::Color(ball.getFillColor().r, ball.getFillColor().g, ball.getFillColor().b, 32));
                enScoreTxt.setColor(sf::Color(ball.getFillColor().r, ball.getFillColor().g, ball.getFillColor().b, 32));
                player.setFillColor(ball.getFillColor());
                enemy.setFillColor(ball.getFillColor());

                draw(); // draw everything to the screen

                // If somebody wins, the window closes (the window can also be closed by pressing escape)
                if (playerPoints > 9 || enemyPoints > 9)
                        window.close();
        }
        return 0;
}

void reset(){
        ball.setPosition(sf::Vector2f(window.getSize().x / 2 - BALLW / 2, window.getSize().y / 2 - BALLH / 2));
        enemy.setPosition(sf::Vector2f(window.getSize().x - PADDLEW, window.getSize().y / 2 - PADDLEH / 2));
        if (ballSpeedX < 0)
                ballSpeedX = BSPEED;
        else
                ballSpeedX = BSPEED * -1;
        ballSpeedY = BSPEED;
        enemySpeed = ESPEED;
        gameOver = false;
}

void draw(){
        window.clear();
        window.draw(plScoreTxt);
        window.draw(enScoreTxt);
        window.draw(player);
        window.draw(enemy);
        window.draw(ball);
        window.display();
}

void increaseSpeed(){
        if (ballSpeedY > 0)
                ballSpeedY += (rand() % 15 + 1) / 1000.0;
        else
                ballSpeedY -= (rand() % 15 + 1) / 1000.0;
        if (ballSpeedX > 0)
                ballSpeedX += (rand() % 20 + 1) / 1000.0;
        else
                ballSpeedX -= (rand() % 20 + 1) / 1000.0;
}

void moveBall(){
        ball.setPosition(sf::Vector2f(ball.getPosition().x + ballSpeedX, ball.getPosition().y + ballSpeedY));

        // Ball -> Wall
        if (ball.getPosition().x < 0){
                enemyPoints++;
                gameOver = true;
                return;
        }
        if (ball.getPosition().x + BALLW > window.getSize().x){
                playerPoints++;
                gameOver = true;
                return;
        }
        if (ball.getPosition().y < 0 || ball.getPosition().y + BALLH > window.getSize().y){
                ballSpeedY *= -1;
                return;
        }

        // Ball -> Both paddles (Side)
        if (intersect(sf::Vector2f(ballSpeedX, ballSpeedY), sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y),
                sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y + PADDLEH)) ||
                intersect(sf::Vector2f(ballSpeedX, ballSpeedY), enemy.getPosition(),
                sf::Vector2f(enemy.getPosition().x, enemy.getPosition().y + PADDLEH)))
        {
                ballSpeedX *= -1;
                ball.setFillColor(sf::Color(rand() % 156 + 100, rand() % 156 + 100, rand() % 156 + 100));
                increaseSpeed();
                return;
        }

        // Player - Top
        if (intersect(sf::Vector2f(ballSpeedX, ballSpeedY), player.getPosition(),
                sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y)))
        {
                ballSpeedY += 0.02f;
                if (ballSpeedX < 0)
                        ballSpeedX *= -1;
                if (ballSpeedY > 0)
                        ballSpeedY *= -1;
                return;
        }

        // Player - Bottom
        if (intersect(sf::Vector2f(ballSpeedX, ballSpeedY),
                sf::Vector2f(player.getPosition().x, player.getPosition().y + PADDLEH),
                sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y + PADDLEH)))
        {
                if (ballSpeedX < 0)
                        ballSpeedX *= -1;
                if (ballSpeedY < 0)
                        ballSpeedY *= -1;
                ballSpeedY += 0.02f;
        }
}

void enemyAI(){
        float Ypos = intersectPos(ballSpeedY, ball);
        // Move enemy to optimal location
        if (enemy.getPosition().y + PADDLEH / 2 > Ypos){
                enemy.setPosition(sf::Vector2f(enemy.getPosition().x, enemy.getPosition().y - enemySpeed));
        }
        else if (enemy.getPosition().y + PADDLEH / 2 < Ypos){
                enemy.setPosition(sf::Vector2f(enemy.getPosition().x, enemy.getPosition().y + enemySpeed));
        }
        // Check if enemy collided with the walls
        if (enemy.getPosition().y < 0)
                enemy.setPosition(enemy.getPosition().x, 0);
        else if (enemy.getPosition().y + PADDLEH > window.getSize().y)
                enemy.setPosition(enemy.getPosition().x, window.getSize().y - PADDLEH);
}

bool lineIntersect(sf::Vector2f LinePointA, sf::Vector2f LinePointB, sf::Vector2f pointMoving, float diam){
        if (LinePointA.x == LinePointB.x && pointMoving.x >= LinePointA.x - diam / 2 && pointMoving.x <= LinePointA.x + diam / 2 &&
                pointMoving.y >= LinePointA.y && pointMoving.y <= LinePointB.y)
                return true;
        if (LinePointA.y == LinePointB.y && pointMoving.y >= LinePointA.y - diam / 2 && pointMoving.y <= LinePointA.y + diam / 2 &&
                pointMoving.x >= LinePointA.x && pointMoving.x <= LinePointB.x)
                return true;
        return false;
}

bool intersect(sf::Vector2f vel, sf::Vector2f LinePointA, sf::Vector2f LinePointB){
        double m, b;
        m = ((ball.getPosition().y + ball.getSize().y / 2.0 + vel.y) - (ball.getPosition().y + ball.getSize().y / 2.0)) /
                ((ball.getPosition().x + ball.getSize().x / 2.0 + vel.x) - (ball.getPosition().x + ball.getSize().x / 2.0));
        b = (ball.getPosition().y + ball.getSize().y / 2.0 + vel.y) - m * (ball.getPosition().x + ball.getSize().x / 2.0 + vel.x);

        // If the line is on the y axis, solve for y in order to have highest accuracy rate
        if (vel.x > 0 && LinePointA.x == LinePointB.x){
                for (double x = 0; x <= vel.x; x += 0.1){
                        if (lineIntersect(LinePointA, LinePointB, sf::Vector2f(x + ball.getPosition().x + ball.getSize().x / 2, (int)(m * (x + ball.getPosition().x + ball.getSize().x / 2.0) + b)), ball.getSize().x))
                                return true;
                }
        }
        else if (vel.x < 0 && LinePointA.x == LinePointB.x){
                for (double x = 0; x >= vel.x; x -= 0.1){
                        if (lineIntersect(LinePointA, LinePointB, sf::Vector2f(x + ball.getPosition().x + ball.getSize().x / 2, (int)(m * (x + ball.getPosition().x + ball.getSize().x / 2.0) + b)), ball.getSize().x))
                                return true;
                }
        }

        // if the line is on the x axis, solve for x in order to have highest accuracy rate
        else if (vel.y > 0 && LinePointA.y == LinePointB.y){
                for (double y = 0; y <= vel.y; y += 0.1){
                        if (lineIntersect(LinePointA, LinePointB, sf::Vector2f((int)((y + ball.getPosition().y + ball.getSize().y / 2.0 - b) / m), y + ball.getPosition().y + ball.getSize().y / 2.0), ball.getSize().y))
                                return true;
                }
        }
        else if (vel.y < 0 && LinePointA.y == LinePointB.y){
                for (double y = 0; y >= vel.y; y -= 0.1){
                        if (lineIntersect(LinePointA, LinePointB, sf::Vector2f((int)((y + ball.getPosition().y + ball.getSize().y / 2.0 - b) / m), y + ball.getPosition().y + ball.getSize().y / 2.0), ball.getSize().y))
                                return true;
                }
        }
        return false;
}

// This function is for the enemy to determine where it has to go
// It basically returns a 'y' coordinate of where the ball is going to end up for the enemy's 'x' coordinate
// If the ball hits either the ceiling or the floor before it reaches it's goal, the function is called recursively until the ball stays within bounds
float intersectPos(float tmpBallSpeedY, sf::RectangleShape tmpBall){
        float x = window.getSize().x - PADDLEW;
        float m = (((tmpBall.getPosition().y + BALLH / 2) + tmpBallSpeedY) - (tmpBall.getPosition().y + BALLH / 2)) /
                (((tmpBall.getPosition().x + BALLW / 2) + ballSpeedX) - (tmpBall.getPosition().x + BALLW / 2));
        float b = (tmpBall.getPosition().y) - m * (tmpBall.getPosition().x);
        float y = m * x + b;

        if (y <= 0){
                tmpBall.setPosition(sf::Vector2f((0 - b) / m, 0));
                y = intersectPos(tmpBallSpeedY*-1, tmpBall);
        }
        else if (y >= window.getSize().y - BALLH){
                tmpBall.setPosition(sf::Vector2f(((window.getSize().y - BALLH) - b) / m, window.getSize().y - BALLH));
                y = intersectPos(tmpBallSpeedY*-1, tmpBall);
        }

        return y;
}

Pages: [1]