예제 #1
0
 /**
  * Inicia el Visor OpenGL indicando la instancia de partido guardado, las dimensiones de la
  * pantalla (sx,sy), si Se ejecuta en pantalla completa(fullscreen), e indicando la instancia del
  * jframe Principal(dejar nulo)
  */
 public VisorOpenGl(
     PartidoGuardado partido, int sx, int sy, boolean fullscreen, PrincipalFrame principal)
     throws SlickException {
   pg = (PartidoGuardado) partido;
   guardado = true;
   progreso = true;
   inicio = 0;
   fin = pg.getIterciones() - 1;
   // System.out.println("Reproduciendo partido guardado...");
   this.partido = partido;
   this.sx = sx;
   this.sy = sy;
   this.dxsaque = (sx + 300 * 2) / 75;
   sx2 = sx / 2;
   sy2 = sy / 2;
   this.principal = principal;
   AppGameContainer container = new AppGameContainer(this);
   container.setForceExit(false);
   container.setDisplayMode(sx, sy, fullscreen);
   container.start();
   SoundStore.get().clear();
   InternalTextureLoader.get().clear();
 }
예제 #2
0
  /** Renderiza el juego, uso interno */
  @Override
  public void render(GameContainer gc, Graphics g) throws SlickException {
    double escalaTemporal = 0;
    if (autoescala) {
      escalaTemporal = escala;
      if (!noAutoEscalar) {
        escala = Math.min(escala, escalaAjustada);
        if (partido.getIteracion() < 50) {
          escalaGradual = escalaGradual + (escala - escalaGradual) * 0.01;
        } else {
          escalaGradual = escalaGradual + (escala - escalaGradual) * 0.2;
        }
      }

      escala = escalaGradual;
    }
    Position p = new Position(px, py);
    if (entorno) {
      pc.pintaEntorno(g, p, escala);
    }
    pc.pintaCancha(g, p, escala);
    Position[][] pos = partido.getPosiciones();
    if (!partido.esGol() && partido.estanSacando()) {
      double zoom = 1 * escala * (1 + 0.02 * (double) iterSaca);
      rel =
          Transforma.transform(
              pos[2][0],
              Constants.centroCampoJuego,
              -Transforma.transform(px, escala) + sx2,
              -Transforma.transform(py, escala) + sy2,
              escala);
      g.drawImage(
          xImage.getScaledCopy((int) zoom, (int) zoom),
          rel[0] - (int) (zoom / 2),
          rel[1] - (int) (zoom / 2));
    }
    Position ball = partido.getPosVisibleBalon();
    for (int i = 0; i < 11; i++) {
      rel =
          Transforma.transform(
              pos[0][i],
              Constants.centroCampoJuego,
              -Transforma.transform(px, escala) + sx2,
              -Transforma.transform(py, escala) + sy2,
              escala);
      pjLocal.pintaSombra(i, iteraciones[i][0], angVisible[i][0], escala, rel[0], rel[1], g);
      rel =
          Transforma.transform(
              pos[1][i],
              Constants.centroCampoJuego,
              -Transforma.transform(px, escala) + sx2,
              -Transforma.transform(py, escala) + sy2,
              escala);
      pjVisita.pintaSombra(i, iteraciones[i][1], angVisible[i][1], escala, rel[0], rel[1], g);
    }
    z = partido.getAlturaBalon(); // 16*Math.sin(Math.abs(ang % Math.PI));

    if (partido.estaRebotando() || z == 0) {
      ang = -balon0.angle(ball);
      velgiro = balon0.distance(ball) * 1.5;
    }
    if (partido.estanRematando()) {
      ang = rand.nextDouble() * Math.PI * 2;
      velgiro = rand.nextDouble();
    }

    balon0 = ball;
    giro = giro + velgiro * 1.5;
    if (giro < 0) {
      giro = 6 + giro;
    }
    rel =
        Transforma.transform(
            ball,
            Constants.centroCampoJuego,
            -Transforma.transform(px, escala) + sx2,
            -Transforma.transform(py, escala) + sy2,
            escala);
    pb.pintaSombra(escala, rel[0], rel[1], z, g);
    if (partido.getAlturaBalon() <= 2) {
      pb.pintaBalon((int) (giro), ang, escala, rel[0], rel[1], z * 2, g);
    }
    pp.pintaPublicidad(g, p, escala);

    if (jugador3d) {
      lista.clear();
      for (int i = 0; i < 11; i++) {
        rel =
            Transforma.transform(
                pos[0][i],
                Constants.centroCampoJuego,
                -Transforma.transform(px, escala) + sx2,
                -Transforma.transform(py, escala) + sy2,
                escala);
        lista.add(
            new Object[] {pjLocal, i, iteraciones[i][0], angVisible[i][0], escala, rel[0], rel[1]});
        rel =
            Transforma.transform(
                pos[1][i],
                Constants.centroCampoJuego,
                -Transforma.transform(px, escala) + sx2,
                -Transforma.transform(py, escala) + sy2,
                escala);
        lista.add(
            new Object[] {
              pjVisita, i, iteraciones[i][1], angVisible[i][1], escala, rel[0], rel[1]
            });
      }
      Object[] tmp1, tmp2;
      for (int i = 0; i < lista.size() - 1; i++) {
        for (int j = i + 1; j < lista.size(); j++) {
          tmp1 = lista.get(i);
          tmp2 = lista.get(j);
          if ((Integer) tmp1[6] > (Integer) tmp2[6]) {
            lista.set(i, tmp2);
            lista.set(j, tmp1);
          }
        }
      }

      for (Object obj[] : lista) {
        PintaJugador pj = (PintaJugador) obj[0];
        pj.pintaJugador(
            (Integer) obj[1],
            (Integer) obj[2],
            (Double) obj[3],
            (Double) obj[4],
            (Integer) obj[5],
            (Integer) obj[6],
            g);
      }

      for (int i = 0; i < 11; i++) {
        rel =
            Transforma.transform(
                pos[0][i],
                Constants.centroCampoJuego,
                -Transforma.transform(px, escala) + sx2,
                -Transforma.transform(py, escala) + sy2,
                escala);
        if (tipoTexto == 3 || (pos[0][i].distance(pos[2][0]) < 8 && tipoTexto == 1)) {
          pjLocal.pintaNumero(i, rel[0], rel[1], g);
        }
        if (tipoTexto == 4 || (pos[0][i].distance(pos[2][0]) < 8 && tipoTexto == 2)) {
          pjLocal.pintaNombre(i, rel[0], rel[1], g);
        }
        rel =
            Transforma.transform(
                pos[1][i],
                Constants.centroCampoJuego,
                -Transforma.transform(px, escala) + sx2,
                -Transforma.transform(py, escala) + sy2,
                escala);
        if (tipoTexto == 3 || (pos[1][i].distance(pos[2][0]) < 8 && tipoTexto == 1)) {
          pjVisita.pintaNumero(i, rel[0], rel[1], g);
        }
        if (tipoTexto == 4 || (pos[1][i].distance(pos[2][0]) < 8 && tipoTexto == 2)) {
          pjVisita.pintaNombre(i, rel[0], rel[1], g);
        }
      }
    } else {
      for (int i = 0; i < 11; i++) {
        rel =
            Transforma.transform(
                pos[0][i],
                Constants.centroCampoJuego,
                -Transforma.transform(px, escala) + sx2,
                -Transforma.transform(py, escala) + sy2,
                escala);
        pjLocal.pintaJugador(i, iteraciones[i][0], angVisible[i][0], escala, rel[0], rel[1], g);
        if (tipoTexto == 3 || (pos[0][i].distance(pos[2][0]) < 8 && tipoTexto == 1)) {
          pjLocal.pintaNumero(i, rel[0], rel[1], g);
        }
        if (tipoTexto == 4 || (pos[0][i].distance(pos[2][0]) < 8 && tipoTexto == 2)) {
          pjLocal.pintaNombre(i, rel[0], rel[1], g);
        }
        rel =
            Transforma.transform(
                pos[1][i],
                Constants.centroCampoJuego,
                -Transforma.transform(px, escala) + sx2,
                -Transforma.transform(py, escala) + sy2,
                escala);
        pjVisita.pintaJugador(i, iteraciones[i][1], angVisible[i][1], escala, rel[0], rel[1], g);
        if (tipoTexto == 3 || (pos[1][i].distance(pos[2][0]) < 8 && tipoTexto == 1)) {
          pjVisita.pintaNumero(i, rel[0], rel[1], g);
        }
        if (tipoTexto == 4 || (pos[1][i].distance(pos[2][0]) < 8 && tipoTexto == 2)) {
          pjVisita.pintaNombre(i, rel[0], rel[1], g);
        }
      }
    }

    rel =
        Transforma.transform(
            ball,
            Constants.centroCampoJuego,
            -Transforma.transform(px, escala) + sx2,
            -Transforma.transform(py, escala) + sy2,
            escala);
    if (partido.getAlturaBalon() > 2) {
      pb.pintaBalon((int) (giro), ang, escala, rel[0], rel[1], z * 2, g);
    }
    pc.pintaArcos(g, p, escala);
    if (estadio) {
      pc.pintaEstadio(g, p, escala);
    }
    if (marcador) {
      pm.pintaMarcador(
          partido.getGolesLocal(),
          partido.getGolesVisita(),
          partido.getIteracion(),
          partido.getPosesionBalonLocal(),
          g);
    }
    if (golIter > 0) {
      double zoom = 1 + 0.05 * (golIter % 3);
      g.drawImage(
          golImage.getScaledCopy((int) (361d * zoom), (int) (81d * zoom)),
          sx2 - (int) (180d * zoom),
          sy2 - (int) (40d * zoom));
    }

    if (offSideIter > 0) {
      g.drawImage(offSideImage, sx2 - 70, sy2 - 20);
      // g.drawImage(offSideImage, offSideIter - 300, sy2 - 20);
    }

    if (saqueIter > 0) {
      g.drawImage(cambioImage, saqueIter - 300, sy2 - 20);
    }
    if (autoescala) {
      escala = escalaTemporal;
    }
    if (guardado && iteracionControl > 0) {
      Image img = paImage;
      if (incremento > 0) {
        img = avImage;
      }
      if (incremento < 0) {
        img = reImage;
      }
      g.drawImage(img, sx2 - 10, sy2 - 10);
      g.setColor(Color.white);
      g.drawString("" + incremento + "x", sx2 + 20, sy2 - 10);
    }
    if (guardado && progreso) {
      g.setColor(Color.black);
      g.drawRect(sx - 20, 20, 10, sy - 40);
      g.setColor(Color.darkGray);
      g.fillRect(sx - 19, 21, 8, sy - 42);
      int valor =
          (int) (((double) sy - 41.0) * ((double) pg.getTiempo() / (double) pg.getIterciones()));
      int valorInicio =
          (int) (((double) sy - 42.0) * ((double) inicio / (double) pg.getIterciones()));
      int valorFin = (int) (((double) sy - 42.0) * ((double) fin / (double) pg.getIterciones()));
      g.setColor(Color.red);
      g.fillRect(sx - 19, sy - 21 - valor, 8, valor);
      g.setColor(Color.white);
      g.drawLine(sx - 19, sy - 22 - valorFin, sx - 12, sy - 22 - valorFin);
      g.drawLine(sx - 19, sy - 21 - valorFin, sx - 18, sy - 21 - valorFin);
      g.drawLine(sx - 13, sy - 21 - valorFin, sx - 12, sy - 21 - valorFin);
      g.drawLine(sx - 19, sy - 22 - valorInicio, sx - 12, sy - 22 - valorInicio);
      g.drawLine(sx - 19, sy - 23 - valorInicio, sx - 18, sy - 23 - valorInicio);
      g.drawLine(sx - 13, sy - 23 - valorInicio, sx - 12, sy - 23 - valorInicio);
    }
    if (showTexto) {
      if (partido.getIteracion() < 50) {
        g.setColor(Color.black);
        g.drawString(partido.getDetalleVisita().getTacticName() + " (Visita)", sx2 + 11, sy2 + 11);
        g.drawString("vs", sx2 + 41, sy2 + 41);
        g.drawString(partido.getDetalleLocal().getTacticName() + " (Local)", sx2 + 71, sy2 + 71);
        g.setColor(Color.white);
        g.drawString(partido.getDetalleVisita().getTacticName() + " (Visita)", sx2 + 10, sy2 + 10);
        g.drawString("vs", sx2 + 40, sy2 + 40);
        g.drawString(partido.getDetalleLocal().getTacticName() + " (Local)", sx2 + 70, sy2 + 70);
      }
    }
    if (showTexto) {
      if (partido.getIteracion() > Constants.ITERACIONES) {
        g.setColor(Color.black);
        g.drawString("Gana", sx2 + 11, sy2 + 11);
        if ((partido.getGolesLocal() > partido.getGolesVisita())
            || (partido.getGolesLocal() == partido.getGolesVisita()
                && partido.getPosesionBalonLocal() >= .5d)) {
          g.drawString(partido.getDetalleLocal().getTacticName(), sx2 + 41, sy2 + 41);
        } else {
          g.drawString(partido.getDetalleVisita().getTacticName(), sx2 + 41, sy2 + 41);
        }
        g.setColor(Color.white);
        g.drawString("Gana", sx2 + 10, sy2 + 10);
        if ((partido.getGolesLocal() > partido.getGolesVisita())
            || (partido.getGolesLocal() == partido.getGolesVisita()
                && partido.getPosesionBalonLocal() >= .5d)) {
          g.drawString(partido.getDetalleLocal().getTacticName(), sx2 + 40, sy2 + 40);
        } else {
          g.drawString(partido.getDetalleVisita().getTacticName(), sx2 + 40, sy2 + 40);
        }
      }
    }
    /*if (isRain) {
        g.setColor(lluvia);
        for (int i = 0; i < 200; i++) {
            double an = rand.nextDouble() * Math.PI * 2d;
            double rad = rand.nextDouble() * 1024;
            int x0 = (int) (sx2 + Math.sin(an) * rad);
            int y0 = (int) (sy2 + Math.cos(an) * rad);
            int x1 = (int) (sx2 + Math.sin(an) * rad * 1.1d);
            int y1 = (int) (sy2 + Math.cos(an) * rad * 1.1d);
            g.drawLine(x0, y0, x1, y1);
        }
        if (rand.nextDouble() < 0.05) {
            g.setColor(relampago);
            g.fillRect(0, 0, sx, sy);
        }
        isRain = rand.nextDouble() < 0.995;
    } else {
        isRain = rand.nextDouble() < 0.005;
    }*/
  }
예제 #3
0
  /** Actualiza el juego, uso interno */
  @Override
  public void update(GameContainer gc, int fps) throws SlickException {
    Input i = gc.getInput();
    if (guardado && progreso) {
      if (i.isMouseButtonDown(0)) {
        double y =
            1 - (double) (Math.max(20, Math.min(i.getMouseY(), sy - 20)) - 20) / (double) (sy - 40);
        PartidoGuardado pguardado = (PartidoGuardado) partido;
        pguardado.setTiempo((int) (y * (double) pguardado.getIterciones()));
      }
    }
    if (i.isKeyDown(Input.KEY_SUBTRACT)) {
      escala = escala * 0.970;
    } else if (i.isKeyDown(Input.KEY_ADD)) {
      escala = escala / 0.970;
    }
    if (i.isKeyPressed(Input.KEY_F1)) {
      estadio = !estadio;
    }
    if (i.isKeyPressed(Input.KEY_F2)) {
      entorno = !entorno;
    }
    if (i.isKeyPressed(Input.KEY_F3)) {
      showfps = !showfps;
      gc.setShowFPS(showfps);
    }
    if (i.isKeyPressed(Input.KEY_F4)) {
      tipoTexto = (tipoTexto + 1) % 5;
    }
    if (i.isKeyPressed(Input.KEY_F5)) {
      marcador = !marcador;
    }
    if (i.isKeyDown(Input.KEY_ESCAPE)) {
      gc.setSoundVolume(0);
      gc.setSoundOn(false);
      stop();
      gc.exit();
    }
    if (i.isKeyDown(Input.KEY_P)) {
      pause = !pause;
      if (pause) {
        gc.pause();
      } else {
        gc.resume();
      }
    }
    if (guardado && i.isKeyPressed(Input.KEY_LEFT)) {
      if (incremento == 0) {
        incremento = -1;
      } else if (incremento == -1) {
        incremento = -2;
      } else if (incremento == -2) {
        incremento = -4;
      } else if (incremento == -4) {
        incremento = -4;
      } else if (incremento == 1) {
        incremento = 0;
      } else if (incremento == 2) {
        incremento = 1;
      } else if (incremento == 4) {
        incremento = 2;
      }
      iteracionControl = 30;
    }
    if (guardado && i.isKeyPressed(Input.KEY_RIGHT)) {
      if (incremento == 0) {
        incremento = 1;
      } else if (incremento == 1) {
        incremento = 2;
      } else if (incremento == 2) {
        incremento = 4;
      } else if (incremento == 4) {
        incremento = 4;
      } else if (incremento == -1) {
        incremento = 0;
      } else if (incremento == -2) {
        incremento = -1;
      } else if (incremento == -4) {
        incremento = -2;
      }
      iteracionControl = 30;
    }
    paso = 0;
    if (guardado && i.isKeyPressed(Input.KEY_UP)) {
      incremento = 0;
      paso = 1;
    }
    if (guardado && i.isKeyPressed(Input.KEY_DOWN)) {
      incremento = 0;
      paso = -1;
    }
    if (guardado && i.isKeyPressed(Input.KEY_HOME)) {
      inicio = pg.getTiempo();
    }
    if (guardado && i.isKeyPressed(Input.KEY_END)) {
      fin = pg.getTiempo();
    }
    if (guardado && i.isKeyPressed(Input.KEY_DELETE)) {
      if (JOptionPane.showConfirmDialog(
              null,
              "Desea eliminar los frames seleccionados?",
              "Eliminar Frames",
              JOptionPane.YES_NO_OPTION)
          == 0) {
        if (inicio < fin) {
          pg.delete(inicio, fin);
          fin = inicio;
        } else {
          pg.delete(inicio, pg.getIterciones() - 1);
          pg.delete(0, fin);
          inicio = 0;
          fin = pg.getIterciones() - 1;
          pg.setTiempo(0);
        }
      }
    }
    if (guardado && i.isKeyPressed(Input.KEY_S)) {
      if (JOptionPane.showConfirmDialog(
              null, "Desea gardar el partido?", "Guardar Partido", JOptionPane.YES_NO_OPTION)
          == 0) {
        // System.out.println(pg.getURL().getProtocol());
        if (jfc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
          try {
            pg.save(jfc.getSelectedFile());
          } catch (Exception e) {
            JOptionPane.showMessageDialog(null, "Error al guardar partido...");
            if (principal != null) {
              try {
                principal.addGuardadoLocal(new File[] {jfc.getSelectedFile()});
              } catch (Exception ex) {
              }
            }
          }
        }
      }
    }
    if (iteracionControl > 0) {
      iteracionControl--;
    }

    vx = (partido.getPosiciones()[2][0].getX() - px) / Constants.SEGUIMIENTO_CAMARA;
    vy = (partido.getPosiciones()[2][0].getY() - py) / Constants.SEGUIMIENTO_CAMARA;

    px = px + vx;
    py = py + vy;

    for (int j = 0; j < 11; j++) {
      posPrev[j][0] = posActu[0][j];
      posPrev[j][1] = posActu[1][j];
    }

    if (partido != null) {
      if (guardado) {
        try {
          pg.setTiempo(pg.getTiempo() + incremento + paso);
        } catch (Exception ex) {
          logger.error("Error al establecer tiempo de partido guardo", ex);
          throw new SlickException(ex.getLocalizedMessage());
        }
      } else {
        try {
          partido.iterar();
        } catch (Exception ex) {
          logger.error("Error al iterar partido", ex);
          throw new SlickException(ex.getLocalizedMessage());
        }
      }

      boolean oldSound = sonidos;
      if (guardado && incremento == 0) {
        sonidos = false;
      }

      if (audioAmbiente == audioAmbienteIdx) {
        if (sonidos) {
          ambiente[audioIdx].play(pinch(), volumenAmbiente);
        }
        audioAmbiente = 50 + rand.nextInt(20);
        audioAmbienteIdx = 0;
        audioIdx = rand.nextInt(ambiente.length);
      }
      audioAmbienteIdx++;

      if (partido.estanSilbando()) {
        if (sonidos) {
          silbato.play(pinch(), volumenCancha);
        }
      }
      if (partido.estanRematando()) {
        if (sonidos) {
          remate[rand.nextInt(2)].play(pinch(), volumenCancha);
        }
      }
      if (partido.estanOvacionando()) {
        if (sonidos) {
          ovacion[rand.nextInt(2)].play(pinch(), volumenAmbiente);
        }
      }
      if (partido.esGol()) {
        if (sonidos) {
          gol.play(pinch(), volumenAmbiente);
        }
        golIter = 1;
      }

      if (golIter > 0) {
        golIter++;
        if (golIter == 50) {
          golIter = 0;
        }
      }

      if (partido.isLibreIndirecto()) {
        if (sonidos) {
          silbato.play(pinch(), volumenAmbiente);
        }
      }

      if (partido.isOffSide()) offSideIter = 1;

      if (offSideIter > 0) {
        offSideIter += dxsaque;
        if (offSideIter > 800) {
          offSideIter = 0;
        }
      }

      if (partido.cambioDeSaque()) {
        if (sonidos) {
          silbato.play(pinch(), volumenAmbiente);
        }
        saqueIter = 1;
      }

      if (saqueIter > 0) {
        saqueIter = saqueIter + dxsaque;
        if (saqueIter > sx + 2 * 177) {
          saqueIter = 0;
        }
      }
      if (partido.estaRebotando()) {
        if (sonidos) {
          rebote.play(pinch(), volumenCancha);
        }
      }
      if (partido.esPoste()) {
        if (sonidos) {
          poste[rand.nextInt(2)].play(pinch(), volumenCancha);
          ovacion[rand.nextInt(2)].play(pinch(), volumenAmbiente);
        }
      }
      sonidos = oldSound;
      if (partido.estanSacando()) {
        iterSaca = (iterSaca + 1) % 6;
      }
    }

    posActu = partido.getPosiciones();
    for (int j = 0; j < 11; j++) {
      dx = posPrev[j][0].getX() - posActu[0][j].getX();
      dy = posPrev[j][0].getY() - posActu[0][j].getY();
      if (dy != 0 || dx != 0) {
        iteraciones[j][0] = iteraciones[j][0] + 1;
        angulos[j][0] = posPrev[j][0].angle(posActu[0][j]) * 180 / Math.PI + 90;
        if (incremento < 0) {
          angulos[j][0] = angulos[j][0] + 180;
        }
      } else {
        iteraciones[j][0] = 3;
      }

      dx = posPrev[j][1].getX() - posActu[1][j].getX();
      dy = posPrev[j][1].getY() - posActu[1][j].getY();
      if (dy != 0 || dx != 0) {
        iteraciones[j][1] = iteraciones[j][1] + 1;
        angulos[j][1] = posPrev[j][1].angle(posActu[1][j]) * 180 / Math.PI + 90;
        if (incremento < 0) {
          angulos[j][1] = angulos[j][1] + 180;
        }
      } else {
        iteraciones[j][1] = 3;
      }
    }

    for (int j = 1; j < angulosAnteriores[0][0].length; j++) {
      for (int x = 0; x < 11; x++) {
        for (int y = 0; y < 2; y++) {
          angulosAnteriores[x][y][j - 1] = angulosAnteriores[x][y][j];
        }
      }
    }
    for (int x = 0; x < 11; x++) {
      for (int y = 0; y < 2; y++) {
        angulosAnteriores[x][y][angulosAnteriores[0][0].length - 1] = angulos[x][y];
      }
    }
    boolean ok = true;
    for (int x = 0; x < 11; x++) {
      for (int y = 0; y < 2; y++) {
        angVisible[x][y] = 0;
        for (int j = 0; j < angulosAnteriores[0][0].length; j++) {
          angVisible[x][y] = angVisible[x][y] + angulosAnteriores[x][y][j];
        }
        angVisible[x][y] = angVisible[x][y] / (double) angulosAnteriores[0][0].length;
      }
    }

    if (autoescala) {
      int[] escalas =
          (Transforma.transform(
              partido.getPosVisibleBalon(),
              Constants.centroCampoJuego,
              -Transforma.transform(px, escala),
              -Transforma.transform(py, escala),
              escala));
      escalaAjustada =
          escala
              * Math.min(
                  0.7d * sx2 / (double) Math.abs(escalas[0]),
                  0.7d * sy2 / (double) Math.abs(escalas[1]));
    }
    if (!noAutoEscalar && partido.esGol()) {
      noAutoEscalar = true;
    }
    if (noAutoEscalar && partido.estanRematando()) {
      noAutoEscalar = false;
    }
  }