Commit 221b110b by José Pardo Madera

Agapito ALFA 0.666 MERGE CON MASTER

Agapito ALFA awoooo
parents 8a27233b aee9c1da
/build
*.elf obj/
*.nds
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MakefileSettings">
<option name="linkedExternalProjectsSettings">
<MakefileProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="version" value="2" />
</MakefileProjectSettings>
</option>
</component>
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${vcpkgRoot}/x64-windows/include"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.22621.0",
"compilerPath": "cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}
\ No newline at end of file
No preview for this file type
#ifndef FUENTE_H
#define FUENTE_H
#include <SDL2/SDL_ttf.h>
#include <stdexcept>
/// Encapsula un puntero TTF_Font* para tener RAII
class Fuente {
const char *camino;
int tamano_pt;
TTF_Font* fuente;
public:
Fuente(const char *camino, int tamano_pt) : camino(camino), tamano_pt(tamano_pt) {};
TTF_Font *operator*() {
if (!fuente) {
fuente = TTF_OpenFont(camino, tamano_pt);
if (fuente == nullptr) {
throw std::runtime_error(SDL_GetError());
}
}
return fuente;
}
~Fuente() {
if (fuente) {
TTF_CloseFont(fuente);
}
}
};
#endif // FUENTE_H
#include "GestorPantallas.h"
#include <SDL2/SDL_render.h>
void Handle::reemplazamePor(shared_ptr<Pantalla> pant){
gestor.pantallas[idPantalla] =pant;
}
void Handle::addPantalla(shared_ptr<Pantalla> pant){
gestor.pantallas.push_back(pant);
}
void Handle::removeme(){
gestor.pantallas.erase(gestor.pantallas.begin()+idPantalla);
}
void Handle::removeAll(){
gestor.pantallas.clear();
}
void cambiar_buffers(SDL_Renderer *renderer) {
SDL_RenderPresent(renderer);
}
void GestorPantallas::mainLoop(){
if (!inicializarSDL()) {
return;
}
std::cout << "Se ha iniciicididialci SDL" << std::endl;
while(!pantallas.empty()){
int tamx=0;
int tamy=0;
SDL_GetWindowSize(window,&tamx,&tamy);
Handle hand(*this,pantallas.size()-1,tamx,tamy);
pantallas.back()->manejarEntrada(hand);
int indice=pantallas.size()-1;
while(indice >= 0 && pantallas[indice]->transparente()){
indice--;
}
for(int i=indice;i<pantallas.size();i++){
pantallas[i]->renderizar(renderer,tamx,tamy);
}
cambiar_buffers(renderer);
SDL_Delay(5);
}
}
bool GestorPantallas::inicializarSDL(){
if(SDL_Init(SDL_INIT_VIDEO) < 0){
std::cout << "Error at SDL_Init(SDL_INIT_VIDEO)"<<std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return false;
}
if(IMG_Init(IMG_INIT_PNG) < 0){
std::cout << "Error at IMG_Init(IMG_INIT_PNG);"<<std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return false;
}
if (TTF_Init() < 0) {
std::cout << "Error at TTF_Init();"<<std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return false;
}
window = SDL_CreateWindow(
"SDL2 Window",
SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,
1920,1080,
0
);
if(!window){
std::cout << "Error creating window" << std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return false;
}
SDL_SetWindowResizable(window, SDL_TRUE);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if(!renderer){
std::cout << "Error creating renderer" << std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return false;
}
return true;
}
#pragma once
#include <SDL2/SDL.h>
#include "SDL2/SDL_image.h"
#include "SDL2/SDL_ttf.h"
#include <SDL2/SDL_video.h>
#include <memory>
#include <vector>
#include <iostream>
class Handle;
class Pantalla;
class GestorPantallas;
using namespace std;
class Handle{
int idPantalla;
GestorPantallas &gestor;
int tamx;
int tamy;
friend GestorPantallas;
Handle(GestorPantallas &gest, int id, int tamx_, int tamy_): gestor(gest), idPantalla(id), tamx(tamx_), tamy(tamy_){};
public:
void reemplazamePor(shared_ptr<Pantalla> );
void addPantalla(shared_ptr<Pantalla> );
void removeme();
void removeAll();
int getTamX(){return tamx;}
int getTamY(){return tamy;}
};
class GestorPantallas{
vector<shared_ptr<Pantalla>> pantallas;
SDL_Renderer *renderer;
SDL_Window *window;
friend Handle;
bool inicializarSDL();
public:
GestorPantallas(shared_ptr<Pantalla> pant){pantallas.push_back(pant);}
void mainLoop();
};
class Pantalla{
public:
virtual void manejarEntrada(Handle &) = 0;
virtual void renderizar(SDL_Renderer*, int tamx, int tamy) = 0;
virtual bool transparente() = 0;
virtual ~Pantalla() {};
};
#include "Hitbox.h"
bool Hitbox::colisiona(int x, int z){
if(x<=xMaximo && x>=xMinimo && z<=zMaximo && z>=zMinimo){
std::cout<<"Esta colisionando";
return true;
}else return false;
}
\ No newline at end of file
#ifndef HITBOX_H
#define HITBOX_H
#include <iostream>
class Hitbox{
public:
int xMaximo;
int xMinimo;
int zMaximo;
int zMinimo;
bool colisiona(int x, int z);
Hitbox(int xS, int xI, int zS, int zI): xMaximo(xS), xMinimo(xI), zMaximo(zS), zMinimo(zI){}
};
#endif // HITBOX_H
#include "Objeto.h"
Hitbox & Objeto::getHitbox(){
return colision;
}
void Objeto::renderizar(SDL_Renderer *renderer, int tamx, int tamy){
std::cout<<"Buenos dias, soy el objeto y mi estado es " << estado<<std::endl;
switch (estado){
case normal:
sprite_normal.renderizar(renderer,x,y,w,h, tamx, tamy);
break;
case overworld:
sprite_overworld.renderizar(renderer,x,y,w,h, tamx, tamy);
break;
case overworld_highlight:
sprite_overworld_highlight.renderizar(renderer,x,y,w,h, tamx, tamy);
break;
}
}
#pragma once
#include "Sprite.h" #include "Sprite.h"
#include "Hitbox.h"
#include <iostream>
enum State {
normal = 0,
overworld = 1,
overworld_highlight = 2
};
class Objeto { class Objeto {
Sprite normal; Sprite sprite_normal;
Sprite overworld; Sprite sprite_overworld;
Sprite overworld_highlight; Sprite sprite_overworld_highlight;
}; Hitbox colision;
\ No newline at end of file State estado=normal;
int x;
int y;
int w;
int h;
public:
Hitbox &getHitbox();
Objeto(Sprite sprite_normal_, Sprite sprite_overworld_, Sprite sprite_overworld_highlight_, Hitbox colision_, int _x, int _y, int _w, int _h, State estado_):
sprite_normal(sprite_normal_), sprite_overworld(sprite_overworld_),
sprite_overworld_highlight(sprite_overworld_highlight_), colision(colision_), x(_x), y(_y), w(_w), h(_h), estado(estado_){}
void renderizar(SDL_Renderer *renderer, int tamx, int tamy);
void setEstado(State estado_){estado=estado_;}
State getEstado(){return estado;}
};
#include "PantallaPrincipal.h"
PantallaPrincipal::PantallaPrincipal() :
fuente1("assets/OpenSans-Italic.ttf", 100),
Fondo1("assets/fondoHabitacion.jpg")
{
Sprite sprPlanta("assets/planta.png");
Sprite sprInterfaz("assets/InterfazPrincipal.png");
Sprite sprOjoAbierto("assets/ojoAbierto.png");
Sprite sprOjoCerrado("assets/ojoCerrado.png");
Hitbox hitPlanta(0,-10,1200,-1000000000);
Hitbox hitInterfaz(1920,0,900,700);
Hitbox hitOjo(1630,1248,997,839);
Objeto Ojo(sprOjoCerrado,sprOjoAbierto,sprOjoAbierto,hitOjo,1300,890,400,170,normal);
Objeto Planta(sprPlanta,sprPlanta,sprPlanta,hitPlanta,1200,400,300,400, normal);
Objeto Interfaz(sprInterfaz,sprInterfaz,sprInterfaz,hitInterfaz,0,870,1920,216,normal);
Hitbox hitFondo(1000000,-10000000,550,0);
Hitbox hitCama(500, 0, 1200,-10000000);
Hitbox hitDer(510, 450, 10000000,-10000000);
Hitbox hitIzq(-800, -900, 10000000,-10000000);
objetos.push_back(Interfaz);
indice_ojo = objetos.size();
objetos.push_back(Ojo);
objetos.push_back(Planta);
colisionesPasivas.push_back(hitPlanta);
colisionesInterfaz.push_back(hitOjo);
colisionesActivas.push_back(hitFondo);
colisionesActivas.push_back(hitCama);
colisionesActivas.push_back(hitDer);
colisionesActivas.push_back(hitIzq);
}
void PantallaPrincipal::manejarEntrada(Handle &handle) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_LEFT:
Agapito.move(Izquierda);
break;
case SDLK_RIGHT:
Agapito.move(Derecha);
break;
case SDLK_UP:
Agapito.move(Fondo);
break;
case SDLK_DOWN:
Agapito.move(Frente);
break;
}
for(int i=0;i<colisionesActivas.size();i++){
Agapito.comprobarColision(colisionesActivas[i]);
}
{
bool checkColision=false;
int i=0;
while(!checkColision && i<colisionesPasivas.size()){
checkColision |= Agapito.comprobarColisionPasiva(colisionesPasivas[i]);
i++;
}
if(checkColision){
objetos[indice_ojo].setEstado(overworld);
}else objetos[indice_ojo].setEstado(normal);
}
break;
case SDL_MOUSEBUTTONDOWN:
{
int xRaton=0;
int zRaton=0;
SDL_GetMouseState(&xRaton, &zRaton);
xRaton*=(1920.0/handle.getTamX());
zRaton*=(1080.0/handle.getTamY());
cout<<"Raton x "<<xRaton<<" Raton z "<<zRaton<<endl;
if(colisionesInterfaz[0].colisiona(xRaton, zRaton) && objetos[indice_ojo].getEstado()==overworld){
Agapito.inventario[Agapito.numObjetos]=objetos[2];
objetos.pop_back();
}
}
break;
case SDL_QUIT:
handle.removeAll();
}
}
}
void PantallaPrincipal::renderizar(SDL_Renderer *renderer, int tamx, int tamy) {
Texto texto(fuente1, "AGAPITO 3D", {255, 255, 255});
Fondo1.renderizar(renderer, 0, 0,1920,1080, tamx, tamy);
Agapito.renderizar(renderer, tamx, tamy);
for(int i=0;i<objetos.size();i++){
objetos[i].renderizar(renderer, tamx, tamy);
}
texto.renderizar(renderer, 50, 50, tamx, tamy);
}
#pragma once
#include "GestorPantallas.h"
#include <vector>
#include "Objeto.h"
#include "Sprite.h"
#include "Texto.h"
#include "Fuente.h"
#include "Personaje.h"
class PantallaPrincipal : public Pantalla {
vector<Objeto> objetos;
vector<Hitbox> colisionesActivas;
vector<Hitbox> colisionesPasivas;
vector<Hitbox> colisionesInterfaz;
Fuente fuente1;
Sprite Fondo1;
Personaje Agapito;
int indice_ojo = 0;
public:
PantallaPrincipal();
void manejarEntrada(Handle &) override;
void renderizar(SDL_Renderer *, int tamx, int tamy) override;
bool transparente() override {
return false;
};
};
...@@ -41,7 +41,7 @@ void Personaje::move(Facing direction) { ...@@ -41,7 +41,7 @@ void Personaje::move(Facing direction) {
} }
} }
void Personaje::renderizar(SDL_Renderer *renderer) { void Personaje::renderizar(SDL_Renderer *renderer, int tamx, int tamy) {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto tiempo_desde_ultimo_movimiento = now.time_since_epoch() - momento_ultimo_movimiento.time_since_epoch(); auto tiempo_desde_ultimo_movimiento = now.time_since_epoch() - momento_ultimo_movimiento.time_since_epoch();
...@@ -52,5 +52,30 @@ void Personaje::renderizar(SDL_Renderer *renderer) { ...@@ -52,5 +52,30 @@ void Personaje::renderizar(SDL_Renderer *renderer) {
indice_movimiento = 0; indice_movimiento = 0;
} }
movimientos[estado][indice_movimiento].renderizar(renderer, x, 100, profundidades, profundidades, true); movimientos[estado][indice_movimiento].renderizar(renderer, x, 100, profundidades, profundidades, tamx, tamy, true);
} }
\ No newline at end of file
void Personaje::comprobarColision(Hitbox &hitbox){
if(hitbox.colisiona(x, profundidades)){
switch (estado) {
case Frente:
profundidades -= 5;
break;
case Derecha:
x -= 10;
break;
case Izquierda:
x += 10;
break;
case Fondo:
profundidades += 5;
break;
}
}
}
bool Personaje::comprobarColisionPasiva(Hitbox &hitbox){
if(hitbox.colisiona(x,profundidades)){
return true;
}else return false;
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "Sprite.h" #include "Sprite.h"
#include "Objeto.h" #include "Objeto.h"
#include "Hitbox.h"
using namespace std; using namespace std;
...@@ -19,22 +20,29 @@ enum Facing { ...@@ -19,22 +20,29 @@ enum Facing {
typedef std::chrono::time_point<std::chrono::system_clock> Instant; typedef std::chrono::time_point<std::chrono::system_clock> Instant;
class Personaje { class Personaje {
array<optional<Objeto>, 3> inventario;
array<array<Sprite, 2>, 4> movimientos; array<array<Sprite, 2>, 4> movimientos;
Instant momento_ultimo_movimiento = std::chrono::system_clock::now(); Instant momento_ultimo_movimiento = std::chrono::system_clock::now();
Facing estado = Frente; Facing estado = Frente;
int indice_movimiento = 0; int indice_movimiento = 0;
int x = 100;
int profundidades = 100;
public: public:
array<optional<Objeto>, 3> inventario;
int numObjetos=0;
int x = -100;
int profundidades = 600;
Personaje(); Personaje();
void move(Facing direcion); void move(Facing direcion);
void renderizar(SDL_Renderer *renderer); void renderizar(SDL_Renderer *renderer, int tamx, int tamy);
};
\ No newline at end of file void comprobarColision (Hitbox &hitbox);
bool comprobarColisionPasiva (Hitbox &hitbox);
};
...@@ -11,20 +11,21 @@ SDL_Texture *Sprite::get_textura(SDL_Renderer *Renderer) { ...@@ -11,20 +11,21 @@ SDL_Texture *Sprite::get_textura(SDL_Renderer *Renderer) {
this->texture = IMG_LoadTexture(Renderer, camino.c_str()); this->texture = IMG_LoadTexture(Renderer, camino.c_str());
if (texture == nullptr) { if (texture == nullptr) {
printf("IMG_TEXTURE devuelve nulo lol\n"); printf("IMG_TEXTURE devuelve nulo lol\n");
printf("GET ERROR = ERROR %s\n", SDL_GetError());
} }
} }
return texture; return texture;
} }
void Sprite::renderizar(SDL_Renderer *Renderer, int x, int y, int w, int h, bool centrar) { void Sprite::renderizar(SDL_Renderer *Renderer, int x, int y, int w, int h,int tamx, int tamy, bool centrar) {
printf("RENDER TEXTURA %s\n", camino.c_str()); printf("RENDER TEXTURA %s\n", camino.c_str());
texture = get_textura(Renderer); texture = get_textura(Renderer);
SDL_Rect Dest; SDL_Rect Dest;
Dest.x = x; Dest.x = x;
Dest.y = y; Dest.y = y;
Dest.w = w; Dest.w = w;
Dest.h = h; Dest.h = h;
...@@ -41,10 +42,15 @@ void Sprite::renderizar(SDL_Renderer *Renderer, int x, int y, int w, int h, bool ...@@ -41,10 +42,15 @@ void Sprite::renderizar(SDL_Renderer *Renderer, int x, int y, int w, int h, bool
Dest.y -= delta_h / 2; Dest.y -= delta_h / 2;
} }
Dest.x *= (tamx/1920.0);
Dest.y *= (tamy/1080.0);
Dest.w *= (tamx/1920.0);
Dest.h *= (tamy/1080.0);
SDL_RenderCopy(Renderer, texture, nullptr, &Dest); SDL_RenderCopy(Renderer, texture, nullptr, &Dest);
} }
Sprite::~Sprite() { Sprite::~Sprite() {
SDL_DestroyTexture(this->texture); SDL_DestroyTexture(this->texture);
} }
\ No newline at end of file
...@@ -14,7 +14,7 @@ class Sprite { ...@@ -14,7 +14,7 @@ class Sprite {
public: public:
Sprite(std::string camino); Sprite(std::string camino);
void renderizar(SDL_Renderer *Renderer, int x, int y, int w, int h, bool centrar = false); void renderizar(SDL_Renderer *Renderer, int x, int y, int w, int h, int tamx, int tamy,bool centrar = false);
~Sprite(); ~Sprite();
}; };
......
#include "Texto.h"
SDL_Texture *Texto::get_textura(SDL_Renderer *renderer) {
if (texture == nullptr) {
texture = SDL_CreateTextureFromSurface(renderer, surface);
}
return texture;
}
void Texto::renderizar(SDL_Renderer *renderer, int x, int y, int tamx, int tamy) {
texture = get_textura(renderer);
SDL_Rect dest;
SDL_QueryTexture(texture, nullptr, nullptr, &dest.w, &dest.h);
dest.x = x * (tamx / 1920.0);
dest.y = y - (tamy / 1080.0);
dest.w *= (tamx / 1920.0);
dest.h *= (tamy / 1080.0);
SDL_RenderCopy(renderer, texture, nullptr, &dest);
}
#ifndef TEXTO_H
#define TEXTO_H
#include "Fuente.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_surface.h>
#include <SDL2/SDL_ttf.h>
class Texto {
SDL_Surface *surface;
SDL_Texture *texture = nullptr;
SDL_Texture *get_textura(SDL_Renderer *renderer);
public:
Texto(Fuente &fuente, const char *texto, SDL_Color fg) {
surface = TTF_RenderUTF8_Solid(*fuente, texto, fg);
};
void renderizar(SDL_Renderer *Renderer, int x, int y, int tamx, int tamy);
~Texto() {
SDL_FreeSurface(surface);
if (texture) {
SDL_DestroyTexture(texture);
}
}
};
#endif // TEXTO_H
No preview for this file type
#include <SDL2/SDL.h> #include "GestorPantallas.h"
#include "SDL2/SDL_image.h" #include "PantallaPrincipal.h"
#include "SDL2/SDL_ttf.h" #include <memory>
#include <iostream>
#include "Sprite.h"
#include "Personaje.h"
void blit(SDL_Renderer *Renderer, SDL_Texture *Texture, SDL_Rect *Cut, int x, int y){
SDL_Rect Dest;
Dest.x = x;
Dest.y = y;
SDL_QueryTexture(Texture, NULL, NULL, &Dest.w, &Dest.h);
SDL_RenderCopy(Renderer, Texture, Cut, &Dest);
}
void cambiar_buffers(SDL_Renderer *renderer) {
SDL_RenderPresent(renderer);
}
int main(){ int main(){
GestorPantallas gestor(
std::shared_ptr<Pantalla>((Pantalla *)new PantallaPrincipal())
);
if(SDL_Init(SDL_INIT_VIDEO) < 0){ gestor.mainLoop();
std::cout << "Error at SDL_Init(SDL_INIT_VIDEO)"<<std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return -1;
}
if(IMG_Init(IMG_INIT_PNG) < 0){
std::cout << "Error at IMG_Init(IMG_INIT_PNG);"<<std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return -1;
}
SDL_Window *Window = SDL_CreateWindow(
"SDL2 Window",
SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,
1920,1080,
0
);
if(!Window){
std::cout << "Error creating window" << std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return -1;
}
SDL_Renderer *Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED);
if(!Renderer){
std::cout << "Error creating renderer" << std::endl;
std::cout << "Error: " << SDL_GetError() << std::endl;
return -1;
}
Sprite Fondo1("assets/fondoHabitacion.jpg");
Sprite Planta("assets/planta.png");
Personaje Agapito;
SDL_Event event;
while (true) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_LEFT:
Agapito.move(Izquierda);
break;
case SDLK_RIGHT:
Agapito.move(Derecha);
break;
case SDLK_UP:
Agapito.move(Fondo);
break;
case SDLK_DOWN:
Agapito.move(Frente);
break;
}
break;
case SDL_QUIT:
exit(0);
}
printf("Pito\n");
}
printf("Buclee\n");
Fondo1.renderizar(Renderer, 0, 0, 1920, 1080);
Planta.renderizar(Renderer, 10, 100, 50, 50);
Agapito.renderizar(Renderer);
cambiar_buffers(Renderer);
SDL_Delay(5);
}
return 0; return 0;
} }
CC = g++ CC = g++
CodeFiles = main.cpp Personaje.cpp Sprite.cpp CodeFiles = main.cpp Personaje.cpp Sprite.cpp Objeto.cpp Hitbox.cpp Texto.cpp GestorPantallas.cpp PantallaPrincipal.cpp
bin = Executable
Executable: $(CodeFiles) objdir = obj
$(CC) $(CodeFiles) -w -lSDL2 -lSDL2_image -lSDL2_ttf -o Executable code_files_in_obj = $(CodeFiles:%=$(objdir)/%)
objs = $(code_files_in_obj:.cpp=.o)
deps = $(code_files_in_obj:.cpp=.d)
link_flags = -lSDL2 -lSDL2_image -lSDL2_ttf
compile_flags = -w $(link_flags)
$(bin): $(objs)
$(CC) $(objs) $(compile_flags) -o $@
$(objdir)/%.o : %.cpp | $(objdir)
$(CC) -c -o $@ $< $(compile_flags)
$(objdir)/%.d: %.cpp | $(objdir)
$(CC) -M $^ -MT $(patsubst %.d,%.o,$@) -MF $@
$(objdir):
mkdir obj
.PHONY: clean
clean:
- rm $(bin)
- rm -r obj
include $(deps)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment