commit cd8549349525e6cf62360208b80aff26a257c7b2 Author: kts Date: Sat Dec 27 19:48:24 2014 -0800 Initial commit of LD31 release of Petite Juliet. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..26a1f50 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +BITS ?= $(shell getconf LONG_BIT) +ifeq ($(BITS),64) + CXXFLAGS=-m64 + LDFLAGS=-m64 + LIB_DIR=lib64 +else + CXXFLAGS=-m32 + LDFLAGS=-m32 + LIB_DIR=lib +endif +CXX=g++ +DEBUG=-g +CXXFLAGS+=$(DEBUG) -Wall `sdl2-config --cflags` -c +#LDFLAGS+=-Wall `sdl2-config --libs` -lSDL2_image -lSDL2_mixer -lGLEW -lGL +#LDFLAGS+= -Wall -L/usr/$(LIB_DIR) -Wl,-rpath=$(LIB_DIR)/ -lSDL2 -lpthread -Wl,--no-undefined -lm -ldl -lpthread -lrt -Wl,-Bstatic -lSDL2_image -lSDL2_mixer -lGLEW -Wl,-Bdynamic -lGL +LDFLAGS+= -Wall -L/export/home/kts/Devel/sdl/$(LIB_DIR) -Wl,-rpath=$(LIB_DIR)/ -lSDL2 -lpthread -Wl,--no-undefined -lm -ldl -lpthread -lrt -Wl,-Bstatic -lSDL2_image -lSDL2_mixer -lGLEW -Wl,-Bdynamic -lGL +VPATH=src +BINARY=juliet$(BITS) +OBJ=main.o v_common.o a_common.o g_common.o shader.o texture.o World.o Sprite.o Entity.o Juliet.o Enemy.o game.o +OBJ_DIR=src/obj + +$(BINARY): $(patsubst %,$(OBJ_DIR)/%,$(OBJ)) + $(CXX) $^ -o $@ $(LDFLAGS) + +release: + make clean + BITS=32 make install + make clean + BITS=64 make install + +install: $(BINARY) + mkdir -p build/ + mkdir -p build/$(LIB_DIR)/ + cp -f /usr/$(LIB_DIR)/libpng14.so.14.12.0 build/$(LIB_DIR)/libpng14.so.14 + cp -f /usr/$(LIB_DIR)/libSDL2-2.0.so.0.2.1 build/$(LIB_DIR)/libSDL2-2.0.so.0 + cp -p $(BINARY) build/ + cp -rf gfx/ build/ + cp -rf sfx/ build/ + cp -rf shaders build/ + +$(OBJ_DIR)/%.o: %.cpp + $(CXX) $(CXXFLAGS) $< -o $@ + +clean: + rm -f $(OBJ_DIR)/*.o && rm -f $(BINARY) diff --git a/gfx/doubt.ase b/gfx/doubt.ase new file mode 100644 index 0000000..30a85c5 Binary files /dev/null and b/gfx/doubt.ase differ diff --git a/gfx/doubt.png b/gfx/doubt.png new file mode 100644 index 0000000..b3288de Binary files /dev/null and b/gfx/doubt.png differ diff --git a/gfx/health.png b/gfx/health.png new file mode 100644 index 0000000..0ec968f Binary files /dev/null and b/gfx/health.png differ diff --git a/gfx/juliet.ase b/gfx/juliet.ase new file mode 100644 index 0000000..5e25d15 Binary files /dev/null and b/gfx/juliet.ase differ diff --git a/gfx/juliet.png b/gfx/juliet.png new file mode 100644 index 0000000..ad0573b Binary files /dev/null and b/gfx/juliet.png differ diff --git a/gfx/juliet_palette.ase b/gfx/juliet_palette.ase new file mode 100644 index 0000000..1f89972 Binary files /dev/null and b/gfx/juliet_palette.ase differ diff --git a/gfx/screens.png b/gfx/screens.png new file mode 100644 index 0000000..79c7b94 Binary files /dev/null and b/gfx/screens.png differ diff --git a/gfx/waves.ase b/gfx/waves.ase new file mode 100644 index 0000000..3a7a980 Binary files /dev/null and b/gfx/waves.ase differ diff --git a/gfx/waves.png b/gfx/waves.png new file mode 100644 index 0000000..892b9bd Binary files /dev/null and b/gfx/waves.png differ diff --git a/gfx/world.png b/gfx/world.png new file mode 100644 index 0000000..5c41365 Binary files /dev/null and b/gfx/world.png differ diff --git a/sfx/hit.wav b/sfx/hit.wav new file mode 100644 index 0000000..a958399 Binary files /dev/null and b/sfx/hit.wav differ diff --git a/sfx/jump.wav b/sfx/jump.wav new file mode 100644 index 0000000..8b0bed0 Binary files /dev/null and b/sfx/jump.wav differ diff --git a/sfx/kick.wav b/sfx/kick.wav new file mode 100644 index 0000000..6da4c06 Binary files /dev/null and b/sfx/kick.wav differ diff --git a/sfx/punch.wav b/sfx/punch.wav new file mode 100644 index 0000000..4f8bdd7 Binary files /dev/null and b/sfx/punch.wav differ diff --git a/sfx/whoosh.wav b/sfx/whoosh.wav new file mode 100644 index 0000000..3517aa9 Binary files /dev/null and b/sfx/whoosh.wav differ diff --git a/sfx/wiff.wav b/sfx/wiff.wav new file mode 100644 index 0000000..de942a0 Binary files /dev/null and b/sfx/wiff.wav differ diff --git a/shaders/sprite_fs.glsl b/shaders/sprite_fs.glsl new file mode 100644 index 0000000..dc07dbc --- /dev/null +++ b/shaders/sprite_fs.glsl @@ -0,0 +1,9 @@ +#version 120 +// uniforms +uniform sampler2D texture_sampler; +// input +varying vec2 frag_uv; + +void main() { + gl_FragColor = texture2D(texture_sampler, frag_uv); +} diff --git a/shaders/sprite_vs.glsl b/shaders/sprite_vs.glsl new file mode 100644 index 0000000..1fb868d --- /dev/null +++ b/shaders/sprite_vs.glsl @@ -0,0 +1,15 @@ +#version 120 +// input +uniform vec2 resolution; // display resolution +attribute vec3 vp; // vertex position +uniform vec2 vp_offset; // vertex offset (draw position) +attribute vec2 uv; // UV map +uniform vec2 uv_offset; // offset within the UV map +// output +varying vec2 frag_uv; + +void main() { + vec2 pos = vp.xy + vp_offset; + gl_Position = vec4(pos/resolution, 1.0, 1.0); + frag_uv = vec2(uv.s+uv_offset.x, 1.0 - uv.t+uv_offset.y); +} diff --git a/shaders/world_fs.glsl b/shaders/world_fs.glsl new file mode 100644 index 0000000..9c8d84e --- /dev/null +++ b/shaders/world_fs.glsl @@ -0,0 +1,23 @@ +#version 120 +varying vec2 frag; +// input +uniform sampler2D texture_sampler; +uniform vec2 seed; +uniform int iteration; + +void main() { + vec2 z = frag; + int i; + for (i = 0; i < iteration; i++) { + float x = (z.x * z.x - z.y * z.y) + seed.x; + float y = (z.y * z.x + z.x * z.y) + seed.y; + if ((x * x + y * y) > 4.0) break; + z.x = x; + z.y = y; + } + if (i == iteration) { + discard; + } else { + gl_FragColor = texture2D(texture_sampler, vec2(float(i)) / 100.0, 0.25); + } +} diff --git a/shaders/world_vs.glsl b/shaders/world_vs.glsl new file mode 100644 index 0000000..9acf958 --- /dev/null +++ b/shaders/world_vs.glsl @@ -0,0 +1,12 @@ +#version 120 +// input +attribute vec2 vp; +uniform vec2 vp_offset; +uniform vec2 size; +// output +varying vec2 frag; + +void main() { + gl_Position = vec4(vp+vp_offset, 0.5, 1.0); + frag = vp/size; +} diff --git a/src/Enemy.cpp b/src/Enemy.cpp new file mode 100644 index 0000000..ece9ce3 --- /dev/null +++ b/src/Enemy.cpp @@ -0,0 +1,127 @@ +#include "Enemy.hpp" +#include "g_common.hpp" +#include "a_common.hpp" +#include + +Enemy::Enemy() { + target = NULL; + type = ENTITY_ENEMY; + flags = ENTITY_LEFT; + max_force = 3.0f; + speed = 1.0f; + damage = 1; +} +Enemy::~Enemy() { + +} +void Enemy::doThink(int delta) { + if (target == NULL) { + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + if ((*entity_it)->type == ENTITY_JULIET) { + target = (*entity_it); + break; + } + } + } else { + if (flags & ENTITY_DIE) { + if (sprite.frame >= 10) { + flags |= ENTITY_DESTROY; + } + } else if (flags & ENTITY_HURT) { + if (!(flags & ENTITY_HAS_HURT)) { + sprite.frame = 0; + sprite.setAnimation(2); + flags |= ENTITY_HAS_HURT; + } else { + if (sprite.frame >= 6) { + sprite.frame = 0; + sprite.setAnimation(0); + flags &= ~ENTITY_HAS_HURT; + flags &= ~ENTITY_HURT; + } + } + } else if (flags & ENTITY_ATTACK) { + if (sprite.frame == 6) { // frame 6 is hit + if (!(flags & ENTITY_HAS_HIT)) { + float diff_x = target->x - x; + float diff_y = target->y - y; + if (diff_y > -48 && diff_y < 48) { + if (diff_x > 0 && diff_x < 32) { + Mix_PlayChannel(-1, a_sfx[SFX_HIT], 0); + target->takeDamage(damage); + flags |= ENTITY_HAS_HIT; + target->addForce((float)damage*3, (float)damage*3); + } else if (diff_x <= 0 && diff_x > -32) { + Mix_PlayChannel(-1, a_sfx[SFX_HIT], 0); + target->takeDamage(damage); + flags |= ENTITY_HAS_HIT; + target->addForce(-(float)damage*3, (float)damage*3); + } + } + } + } else if (sprite.frame >= 10) { + sprite.setAnimation(0); + sprite.frame = 0; + flags &= ~ENTITY_ATTACK; + flags &= ~ENTITY_HAS_HIT; + frame_time = 100; + } + } else { + float diff_x = target->x - x; + float diff_y = target->y - y; + if (diff_x > -48 && diff_x < 48 && diff_y > -48 && diff_y < 48) { + sprite.setAnimation(1); + sprite.frame = 0; + frame_time = 75; + flags |= ENTITY_ATTACK; + } + if (target->x > x) { + if (!(flags & ENTITY_RIGHT)) { + flags |= ENTITY_RIGHT; + flags &= ~ENTITY_LEFT; + sprite.flipSprite(); + } + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(speed, 0); + } else { + addForce(speed/2.0f, 0); + } + flags |= ENTITY_WALK; + } + } else if (target->x < x) { + if (!(flags & ENTITY_LEFT)) { + flags |= ENTITY_LEFT; + flags &= ~ENTITY_RIGHT; + sprite.flipSprite(); + } + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(-speed, 0); + } else { + addForce(-(speed/2.0f), 0); + } + flags |= ENTITY_WALK; + } + } + if (target->y > y+32) { + if (!(flags & ENTITY_FALLING)) { + flags |= ENTITY_JUMP; + addForce(0, 2); + } + } + } + } + frame_elapsed += delta; + if (frame_elapsed >= frame_time) { + frame_elapsed = 0; + sprite.loopFrame(); + } + if (health <= 0) { + sprite.frame = 0; + sprite.setAnimation(3); + flags |= ENTITY_DIE; + health = 100; // cheat + } +} diff --git a/src/Enemy.hpp b/src/Enemy.hpp new file mode 100644 index 0000000..9db3b20 --- /dev/null +++ b/src/Enemy.hpp @@ -0,0 +1,14 @@ +#ifndef ENEMY_HPP +#define ENEMY_HPP +#include "Entity.hpp" + +class Enemy : public Entity { + public: + Enemy(); + ~Enemy(); + void doThink(int delta); + private: + Entity *target; +}; + +#endif diff --git a/src/Entity.cpp b/src/Entity.cpp new file mode 100644 index 0000000..ec8aa7c --- /dev/null +++ b/src/Entity.cpp @@ -0,0 +1,57 @@ +#include "Entity.hpp" +#include + +Entity::Entity() { + frame_time = 100; + frame_elapsed = 0; + x = y = 0; + vel_x = vel_y = 0.0f; + max_force = 5.0f; + col_box = NULL; + type = ENTITY_NONE; + flags = 0; + speed = 5.0f; + health = 10.0f; + damage = 0; +} +Entity::~Entity() { + if (col_box != NULL) free(col_box); +} +void Entity::setSprite(Sprite new_sprite) { + sprite = new_sprite; + if (col_box != NULL) free(col_box); + col_box = (float*)malloc(4*sprite.width*sprite.height*sizeof(float)); +} +void Entity::addForce(float x_, float y_) { + //if (vel_x + x_ <= max_force) { + vel_x += x_; + //} else { + //vel_x = max_force; + //} +// if (vel_y + y_ <= max_force) { + vel_y += y_; +// } else { +// vel_y = max_force; +// } +} +void Entity::setPosition(float x_, float y_) { + x = x_; + y = y_; +} +void Entity::doThink(int delta) { +} +float Entity::checkCol(int x, int y, int component) { + if (x < 0 || y < 0) return 0.0f; + int pos = y*sprite.height+x+component; + return(col_box[pos]); +} +void Entity::takeDamage(int damage) { + health -= damage; + flags |= ENTITY_HURT; + sprite.frame = 0; +} + +bool destroyEntity(Entity *entity) { + if (entity->flags & ENTITY_DESTROY) return true; + return false; +} diff --git a/src/Entity.hpp b/src/Entity.hpp new file mode 100644 index 0000000..89f827d --- /dev/null +++ b/src/Entity.hpp @@ -0,0 +1,54 @@ +#ifndef ENTITY_HPP +#define ENTITY_HPP +#include "Sprite.hpp" + +#define ENTITY_NONE 0 +#define ENTITY_JULIET 1 +#define ENTITY_ENEMY 2 + +#define ENTITY_NONE 0 +#define ENTITY_STAND 1 +#define ENTITY_WALK 2 +#define ENTITY_LEFT 4 +#define ENTITY_RIGHT 8 +#define ENTITY_ATTACK 16 +#define ENTITY_JUMP 32 +#define ENTITY_ATTACK2 64 +#define ENTITY_FALLING 128 +#define ENTITY_HAS_HIT 256 +#define ENTITY_HURT 512 +#define ENTITY_HAS_HURT 1024 +#define ENTITY_CAN_JUMP 2048 +#define ENTITY_DESTROY 4096 +#define ENTITY_DIE 8192 + +class Entity { + protected: + public: + int frame_time; + int frame_elapsed; + Sprite sprite; + Entity(); + ~Entity(); + int type; + int flags; + void setSprite(Sprite new_sprite); + virtual void doThink(int delta); + void setPosition(float x, float y); + void addForce(float x_, float y_); + float checkCol(int x, int y, int component); + virtual void takeDamage(int damage); + float vel_x; + float vel_y; + float x; + float y; + float pos[18]; // okay, this is what we get for not using matrices. + float *col_box; + float max_force; // self-applied force only applies up to this + // + float speed; + float health; + int damage; +}; +bool destroyEntity(Entity *entity); +#endif diff --git a/src/Juliet.cpp b/src/Juliet.cpp new file mode 100644 index 0000000..ea0dbe0 --- /dev/null +++ b/src/Juliet.cpp @@ -0,0 +1,275 @@ +#include "Juliet.hpp" +#include "Entity.hpp" +#include "g_common.hpp" +#include "a_common.hpp" +#include + +Juliet::Juliet() { + frame_time = 75; + state = 0; + flags = ENTITY_LEFT; + state = ENTITY_STAND; + max_force = 4.0f; + speed = 4.0f; + damage = 2; + type = ENTITY_JULIET; + health = 20.0f; +} +Juliet::~Juliet() { + +} + +void Juliet::setState(int new_state) { +} +void Juliet::removeState(int rem_state) { +} +void Juliet::doThink(int delta) { + frame_elapsed += delta; + if (frame_elapsed >= frame_time) { + frame_elapsed = 0; + sprite.loopFrame(); + } + if (state & JULIET_VICTORY) { + sprite.setAnimation(JULIET_ANIM_VICTORY); + return; + } + + if (flags & ENTITY_ATTACK) { + if (flags & ENTITY_ATTACK2) { + flags &= ~ENTITY_ATTACK2; + state &= ~JULIET_KICKING; + sprite.setAnimation(JULIET_ANIM_PUNCH); + sprite.frame = 0; + flags &= ~ENTITY_HAS_HIT; + frame_time = 75; + } + if (state & JULIET_PUNCHING) { + if (sprite.frame == 4) { + if (!(flags & ENTITY_HAS_HIT)) { + Mix_PlayChannel(-1, a_sfx[SFX_WIFF], 0); + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = (*entity_it); + if (entity->type == ENTITY_ENEMY) { + float diff_x = entity->x - x; + float diff_y = entity->y - y; + if (diff_y > -48 && diff_y < 48) { + float force = 0; + if (diff_x > 0 && diff_x < 48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce((float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } else if (diff_x <= 0 && diff_x > -48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce(-(float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } + } + } + } + flags |= ENTITY_HAS_HIT; + } + } else if (sprite.frame == 5) { + flags &= ~ENTITY_HAS_HIT; + } else if (sprite.frame == 7) { + if (!(flags & ENTITY_HAS_HIT)) { + Mix_PlayChannel(-1, a_sfx[SFX_WIFF], 0); + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = (*entity_it); + if (entity->type == ENTITY_ENEMY) { + float diff_x = entity->x - x; + float diff_y = entity->y - y; + if (diff_y > -48 && diff_y < 48) { + float force = 0; + if (diff_x > 0 && diff_x < 48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce((float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } else if (diff_x <= 0 && diff_x > -48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce(-(float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } + } + } + } + flags |= ENTITY_HAS_HIT; + } + } else if (sprite.frame >= 10) { + state &= ~JULIET_PUNCHING; + flags &= ~ENTITY_ATTACK; + flags &= ~ENTITY_HAS_HIT; + frame_time = 100; + } + } else { + state |= JULIET_PUNCHING; + sprite.setAnimation(JULIET_ANIM_PUNCH); + sprite.frame = 0; + frame_time = 75; + } + } + if (flags & ENTITY_ATTACK2) { + if (flags & ENTITY_ATTACK) { + flags &= ~ENTITY_ATTACK; + state &= ~JULIET_PUNCHING; + sprite.setAnimation(JULIET_ANIM_KICK); + sprite.frame = 0; + frame_time = 75; + flags &= ~ENTITY_HAS_HIT; + } + if (state & JULIET_KICKING) { + if (sprite.frame == 5) { + if (!(flags & ENTITY_HAS_HIT)) { + Mix_PlayChannel(-1, a_sfx[SFX_WHOOSH], 0); + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = (*entity_it); + if (entity->type == ENTITY_ENEMY) { + float diff_x = entity->x - x; + float diff_y = entity->y - y; + if (diff_y > -48 && diff_y < 48) { + float force = 0; + if (diff_x > 0 && diff_x < 60) { + Mix_PlayChannel(-1, a_sfx[SFX_KICK], 0); + entity->addForce((float)damage*4, (float)damage*4); + entity->takeDamage(damage); + } else if (diff_x <= 0 && diff_x > -60) { + Mix_PlayChannel(-1, a_sfx[SFX_KICK], 0); + entity->addForce(-(float)damage*4, (float)damage*4); + entity->takeDamage(damage); + } + } + } + } + flags |= ENTITY_HAS_HIT; + } + } else if (sprite.frame >= 10) { + state &= ~JULIET_KICKING; + flags &= ~ENTITY_ATTACK2; + flags &= ~ENTITY_HAS_HIT; + frame_time = 100; + } + } else { + state |= JULIET_KICKING; + sprite.setAnimation(JULIET_ANIM_KICK); + sprite.frame = 0; + frame_time = 75; + } + } + + if ((flags & ENTITY_JUMP) && (flags & ENTITY_CAN_JUMP)) { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_JUMP); + } + if (!(state & JULIET_HOLD_JUMP)) { + if (sprite.frame == 2) { + addForce(0, speed*3); + Mix_PlayChannel(-1, a_sfx[SFX_JUMP], 0); + if (state & JULIET_LEFT) { + addForce(-speed/2, 0); + } else if (state & JULIET_RIGHT) { + addForce(speed/2, 0); + } + flags &= ~ENTITY_CAN_JUMP; + flags &= ~ENTITY_JUMP; + flags |= ENTITY_FALLING; + } else if (sprite.frame >= 9) { + flags &= ~ENTITY_JUMP; + flags &= ~ENTITY_CAN_JUMP; + } + } + } else if (flags & ENTITY_FALLING) { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_JUMP); + if (sprite.frame < 4) { + sprite.frame = 4; + } else if (sprite.frame > 7) { + sprite.frame = 4; + } + } + flags &= ~ENTITY_JUMP; + if (state & JULIET_LEFT) { + addForce(-speed/2, 0); + } else if (state & JULIET_RIGHT) { + addForce(speed/2, 0); + } + } else if (flags & ENTITY_HURT) { + flags &= ~ENTITY_JUMP; + flags &= ~ENTITY_HAS_HIT; + flags &= ~ENTITY_ATTACK; + flags &= ~ENTITY_ATTACK2; + state &= ~JULIET_PUNCHING; + state &= ~JULIET_KICKING; + sprite.setAnimation(JULIET_ANIM_HURT); + frame_time = 25; + if (sprite.frame >= 6) { + flags &= ~ENTITY_HURT; + sprite.frame = 0; + frame_time = 100; + } + } else if (flags & ENTITY_WALK) { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_WALK); + } + if (flags & ENTITY_LEFT) { + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(-speed, 0); + } else { + addForce(-(speed/2), 0); + } + } + } else if (flags & ENTITY_RIGHT) { + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(speed, 0); + } else { + addForce((speed/2), 0); + } + } + } + } else { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_STAND); + } + } + if (state & JULIET_JUMP && flags & ENTITY_CAN_JUMP) { + flags |= ENTITY_JUMP; + sprite.frame = 0; + if (!(state & JULIET_HOLD_JUMP)) { + state |= JULIET_HOLD_JUMP; + } + } else if (state & JULIET_HOLD_JUMP && (flags & ENTITY_CAN_JUMP)) { + state &= ~JULIET_HOLD_JUMP; + } + if (state & JULIET_LEFT) { + if (!(flags & ENTITY_LEFT)) { + sprite.flipSprite(); + flags |= ENTITY_LEFT; + flags &= ~ENTITY_RIGHT; + } + if (!(flags & ENTITY_FALLING)) { + flags |= ENTITY_WALK; + } + } else { + if (!(state & JULIET_RIGHT)) flags &= ~ENTITY_WALK; + } + if (state & JULIET_RIGHT) { + if (!(flags & ENTITY_RIGHT)) { + sprite.flipSprite(); + flags |= ENTITY_RIGHT; + flags &= ~ENTITY_LEFT; + } + if (!(flags & ENTITY_FALLING)) { + flags |= ENTITY_WALK; + } + } else { + if (!(state & JULIET_LEFT)) flags &= ~ENTITY_WALK; + } + if (state & JULIET_PUNCH) { + flags |= ENTITY_ATTACK; + } else if (state & JULIET_KICK) { + flags |= ENTITY_ATTACK2; + } +} diff --git a/src/Juliet.hpp b/src/Juliet.hpp new file mode 100644 index 0000000..8c76b5f --- /dev/null +++ b/src/Juliet.hpp @@ -0,0 +1,39 @@ +#ifndef JULIET_HPP +#define JULIET_HPP +#include "Entity.hpp" + +#define JULIET_STAND 0 +#define JULIET_LEFT 1 +#define JULIET_RIGHT 2 +#define JULIET_WALK 4 +#define JULIET_JUMP 8 +#define JULIET_PUNCH 16 +#define JULIET_KICK 32 +#define JULIET_BUSY 64 +#define JULIET_ACTIVE 128 +#define JULIET_FALLING 256 +#define JULIET_HOLD_JUMP 512 +#define JULIET_KICKING 1024 +#define JULIET_PUNCHING 2048 +#define JULIET_VICTORY 4096 + +#define JULIET_ANIM_STAND 0 +#define JULIET_ANIM_WALK 1 +#define JULIET_ANIM_VICTORY 2 +#define JULIET_ANIM_JUMP 3 +#define JULIET_ANIM_KICK 4 +#define JULIET_ANIM_PUNCH 5 +#define JULIET_ANIM_HURT 6 + +class Juliet : public Entity { + public: + Juliet(); + ~Juliet(); + void setState(int new_state); + void removeState(int rem_state); + void doThink(int delta); + int state; + private: +}; + +#endif diff --git a/src/Sprite.cpp b/src/Sprite.cpp new file mode 100644 index 0000000..a88eb71 --- /dev/null +++ b/src/Sprite.cpp @@ -0,0 +1,80 @@ +#include +#include "Sprite.hpp" + +Sprite::Sprite(unsigned int s_cols, unsigned int s_rows, unsigned int s_width, unsigned int s_height) { + cols = s_cols; + rows = s_rows; + width = s_width; + height = s_height; + frame = animation = 0; + x = y = 0; + offset[0] = offset[1] = texture = 0; + // create the sprite quad + float fwidth = s_width; + float fheight = s_height; + quad[0] = -fwidth; quad[1] = -fheight; quad[2] = 0.0f; + quad[3] = fwidth; quad[4] = -fheight; quad[5] = 0.0f; + quad[6] = -fwidth; quad[7] = fheight; quad[8] = 0.0f; + quad[9] = fwidth; quad[10] = fheight; quad[11] = 0.0f; + quad[12] = fwidth; quad[13] = -fheight; quad[14] = 0.0f; + quad[15] = -fwidth; quad[16] = fheight; quad[17] = 0.0f; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); + // get our sprite texture UV out of spritesheet + sprite_w = fwidth/(float)(width*cols); + sprite_h = fheight/(float)(height*rows); + quad_uv[0] = 0.0; quad_uv[1] = 0.0; + quad_uv[2] = sprite_w; quad_uv[3] = 0.0; + quad_uv[4] = 0.0; quad_uv[5] = sprite_h; + quad_uv[6] = sprite_w; quad_uv[7] = sprite_h; + quad_uv[8] = sprite_w; quad_uv[9] = 0.0; + quad_uv[10] = 0.0; quad_uv[11] = sprite_h; + glGenBuffers(1, &uv); + glBindBuffer(GL_ARRAY_BUFFER, uv); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad_uv), quad_uv, GL_STATIC_DRAW); +} +Sprite::~Sprite() { + +} +void Sprite::setFrame(unsigned int new_frame) { + if (new_frame > cols) { + new_frame = cols; + } else if (new_frame < 0) { + new_frame = 0; + } + frame = new_frame; + offset[0] = (float)frame * sprite_w; +} +void Sprite::incFrame() { + if (frame+1 < cols) frame++; + offset[0] = (float)frame * sprite_w; +} +void Sprite::decFrame() { + if ((int)frame-1 >= 0) frame--; + offset[0] = (float)frame * sprite_w; +} +void Sprite::loopFrame() { + if (frame+1 < cols) frame++; + else frame = 0; + offset[0] = (float)frame * sprite_w; +} +void Sprite::setAnimation(unsigned int new_animation) { + if (new_animation >= rows) { + new_animation = rows; + } else if ((int)new_animation <= 0) { + new_animation = 0; + } + animation = new_animation+1; + offset[1] = (float)animation * sprite_h; +} +void Sprite::flipSprite() { + quad[0] = -quad[0]; + quad[3] = -quad[3]; + quad[6] = -quad[6]; + quad[9] = -quad[9]; + quad[12] = -quad[12]; + quad[15] = -quad[15]; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); +} diff --git a/src/Sprite.hpp b/src/Sprite.hpp new file mode 100644 index 0000000..ceea894 --- /dev/null +++ b/src/Sprite.hpp @@ -0,0 +1,39 @@ +#ifndef SPRITE_HPP +#define SPRITE_HPP +#ifdef __APPLE__ +#include +#else +#include +#endif +class Sprite { + private: + unsigned int x; // selected col (x * width) + unsigned int y; // selected row (y * height) + float sprite_w; // sprite's width in UV terms + float sprite_h; // sprite's height in UV terms + // this is dumb, but let's have mock vec2s for GL + float quad[18]; // quad dimensions (width/max_width) + float quad_uv[12]; // uv map + public: + unsigned int width; // width of individual sprite + unsigned int height; // height of individual sprite + unsigned int rows; // rows in sheet + unsigned int cols; // cols in sheet + Sprite(unsigned int s_cols = 0, unsigned int s_rows = 0, unsigned int s_width= 0, unsigned int s_height = 0); + ~Sprite(); + void setFrame(unsigned int frame); + void setAnimation(unsigned int animation); + void incFrame(); + void decFrame(); + void loopFrame(); + void flipSprite(); + GLuint vbo; // vertex buffer object for quad + GLuint uv; + GLuint uv_offset; + GLuint texture; // texture + float offset[2]; // x and y + unsigned int frame; + unsigned int animation; +}; + +#endif diff --git a/src/World.cpp b/src/World.cpp new file mode 100644 index 0000000..42b7094 --- /dev/null +++ b/src/World.cpp @@ -0,0 +1,12 @@ +#include "World.hpp" + +World::World(int iter, float size_x_, float size_y_, float offset_x_, float offset_y_) { + iteration = iter; + size_x = size_x_; + size_y = size_y_; + offset_x = offset_x_; + offset_y = offset_y_; +} +World::~World() { + +} diff --git a/src/World.hpp b/src/World.hpp new file mode 100644 index 0000000..e4eb8a7 --- /dev/null +++ b/src/World.hpp @@ -0,0 +1,17 @@ +#ifndef WORLD_HPP +#define WORLD_HPP + +class World { + protected: + public: + World(int iter, float size_x_, float size_y_, float offset_x_, float offset_y_); + ~World(); + float seed; + int iteration; + float size_x; + float size_y; + float offset_x; + float offset_y; +}; + +#endif diff --git a/src/a_common.cpp b/src/a_common.cpp new file mode 100644 index 0000000..d7d7ffa --- /dev/null +++ b/src/a_common.cpp @@ -0,0 +1,16 @@ +#include "a_common.hpp" + +int a_rate = 22050; +Uint16 a_format = AUDIO_S16SYS; +int a_channels = 2; +int a_buffers = 4096; + +Mix_Chunk *a_sfx[SFX_COUNT] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + diff --git a/src/a_common.hpp b/src/a_common.hpp new file mode 100644 index 0000000..904ed84 --- /dev/null +++ b/src/a_common.hpp @@ -0,0 +1,19 @@ +#ifndef A_COMMON_HPP +#define A_COMMON_HPP +#include "SDL_mixer.h" + +extern int a_rate; +extern Uint16 a_format; +extern int a_channels; +extern int a_buffers; + +#define SFX_KICK 0 +#define SFX_PUNCH 1 +#define SFX_WHOOSH 2 +#define SFX_JUMP 3 +#define SFX_WIFF 4 +#define SFX_HIT 5 +#define SFX_COUNT 6 +extern Mix_Chunk *a_sfx[SFX_COUNT]; + +#endif diff --git a/src/g_common.cpp b/src/g_common.cpp new file mode 100644 index 0000000..b56017a --- /dev/null +++ b/src/g_common.cpp @@ -0,0 +1,20 @@ +#include "g_common.hpp" + +int g_running = 0; +char g_keystate[256]; + +int g_wave = 0; +int g_waves[8] = { + 5, + 10, + 20, + 25, + 30, + 35, + 40, + 45 +}; + +int g_victory = 0; + +std::vector g_entities; diff --git a/src/g_common.hpp b/src/g_common.hpp new file mode 100644 index 0000000..a348e15 --- /dev/null +++ b/src/g_common.hpp @@ -0,0 +1,20 @@ +#ifndef G_COMMON_HPP +#define G_COMMON_HPP +#include +#include "Entity.hpp" +extern int g_running; +extern char g_keystate[256]; + +extern int g_wave; +extern int g_waves[8]; + +extern int g_victory; + +extern std::vector g_entities; + +#define DRAG 0.25 +#define GRAVITY 0.5 +#define MAX_FALL 10 +#define MAX_VELOCITY 4 + +#endif diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..87f9d37 --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,342 @@ +#ifdef _WIN32 +#include +#else +#include +#endif +#include "game.hpp" +#include +#include +#include "g_common.hpp" +#include "v_common.hpp" +#include "World.hpp" +#include "Sprite.hpp" +#include "Entity.hpp" +#include "Juliet.hpp" +#include "Enemy.hpp" + +int runGame() { + g_running = 1; + std::vector world; + world.push_back(new World(64, 0.40, 0.40, 0, 0)); + world.push_back(new World(16, 1.20, 1.00, 0, 0)); + Juliet juliet; + juliet.setSprite(Sprite(11, 7, 32, 32)); + juliet.sprite.texture = v_texture_juliet; + + g_entities.push_back(&juliet); + + Sprite waves(1, 8, 128, 32); + waves.texture = v_texture_waves; + waves.setAnimation(0); + + Sprite health(2, 2, 16, 16); + health.texture = v_texture_health; + health.setAnimation(0); + + Sprite screens(1, 3, 128, 128); + screens.texture = v_texture_screens; + screens.setAnimation(0); + + float max_x = (float)(v_width-16); + float min_x = -(float)(v_width-16); + float max_y = -(float)(v_height-32); + float min_y = (float)(v_height+16); + + unsigned int last_time = SDL_GetTicks(); + unsigned int current_time = last_time; + unsigned int delta_time = 0; + unsigned int accumulator = 0; + + unsigned int last_wave = SDL_GetTicks(); + unsigned int next_wave = SDL_GetTicks(); + int spawn_timer = 0; + int spawn_count = 0; + + while(g_running) { + last_time = current_time; + current_time = SDL_GetTicks(); + delta_time = current_time - last_time; + accumulator += delta_time; + // event handling + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { + g_keystate[event.key.keysym.scancode] = event.key.state; + } else if (event.type == SDL_QUIT) { + g_running = 0; + } else if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_CLOSE) { + g_running = 0; + } + } + } + // world processing + while (accumulator >= 16) { + if (juliet.health <= 0) { + juliet.flags |= ENTITY_DESTROY; + std::vector::iterator entity_it, entity_end; + entity_it = std::remove_if(g_entities.begin(), g_entities.end(), destroyEntity); + g_entities.erase(entity_it, g_entities.end()); + + screens.setAnimation(1); + if (g_keystate[SDL_SCANCODE_SPACE] == SDL_PRESSED) { + g_wave = 0; + waves.setAnimation(0); + spawn_timer = 0; + spawn_count = 0; + g_entities.clear(); + juliet = Juliet(); + juliet.setSprite(Sprite(11, 7, 32, 32)); + juliet.sprite.texture = v_texture_juliet; + g_entities.push_back(&juliet); + screens.setAnimation(0); + } + } else if (g_victory == 1) { + screens.setAnimation(2); + juliet.state = JULIET_VICTORY; + juliet.frame_elapsed += 16; + juliet.frame_time = 200; + if (juliet.frame_elapsed >= juliet.frame_time) { + juliet.frame_elapsed = 0; + juliet.sprite.loopFrame(); + } + if (g_keystate[SDL_SCANCODE_SPACE] == SDL_PRESSED) { + g_victory = 0; + g_wave = 0; + waves.setAnimation(0); + spawn_timer = 0; + spawn_count = 0; + g_entities.clear(); + juliet = Juliet(); + juliet.setSprite(Sprite(11, 7, 32, 32)); + juliet.sprite.texture = v_texture_juliet; + g_entities.push_back(&juliet); + screens.setAnimation(0); + } + } else { + if (g_keystate[SDL_SCANCODE_SPACE] == SDL_PRESSED) { + juliet.state |= JULIET_JUMP; + } else { + juliet.state &= ~JULIET_JUMP; + } + if (g_keystate[SDL_SCANCODE_Z] == SDL_PRESSED) { + juliet.state |= JULIET_KICK; + } else { + juliet.state &= ~JULIET_KICK; + } + if (g_keystate[SDL_SCANCODE_X] == SDL_PRESSED) { + juliet.state |= JULIET_PUNCH; + } else { + juliet.state &= ~JULIET_PUNCH; + } + if (g_keystate[SDL_SCANCODE_LEFT] == SDL_PRESSED) { + juliet.state |= JULIET_LEFT; + } else { + juliet.state &= ~JULIET_LEFT; + } + if (g_keystate[SDL_SCANCODE_RIGHT] == SDL_PRESSED) { + juliet.state |= JULIET_RIGHT; + } else { + juliet.state &= ~JULIET_RIGHT; + } + + spawn_timer += 16; + if (spawn_timer >= 4000) { + if (spawn_count < g_waves[g_wave]) { + spawn_timer = 0; + Enemy *new_enemy = new Enemy(); + new_enemy->setSprite(Sprite(11, 4, 32, 32)); + new_enemy->sprite.texture = v_texture_doubt; + new_enemy->sprite.setAnimation(1); + g_entities.push_back(new_enemy); + spawn_count++; + } else { + if (g_wave+1 >= 8) { + if (g_entities.size() == 1) { + g_victory = 1; + } + } else { + spawn_timer = 0; + spawn_count = 0; + g_wave++; + waves.setAnimation(g_wave); + } + } + } + } + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = *entity_it; + float l = (entity->checkCol(12, 4, 0)+entity->checkCol(12, 28, 0))/2.0f; + float r = (entity->checkCol(20, 4, 0)+entity->checkCol(20, 28, 0))/2.0f; + float t = (entity->checkCol(12, 28, 0)+entity->checkCol(20, 28, 0))/2.0f; + float b = (entity->checkCol(12, 4, 0)+entity->checkCol(20, 4, 0))/2.0f; + if (l > r) entity->vel_x += r*2; + else if (r > l) entity->vel_x -= r*2; + if (b > 0.00) { + entity->vel_y += b; + } + if (b <= 0.03) { + entity->vel_y -= GRAVITY; + if (b > 0.0 && b < 0.01) { + entity->flags |= ENTITY_FALLING; + entity->flags &= ~ENTITY_CAN_JUMP; + } else { + entity->flags &= ~ENTITY_FALLING; + } + } else if (b > 0.05 && b <= 0.1) { + if (entity->vel_y < 0.0f) { + entity->vel_y /= 2; + } + entity->flags &= ~ENTITY_FALLING; + } else { + if (entity->vel_y < 0.05) entity->vel_y = 0; + entity->flags &= ~ENTITY_FALLING; + entity->flags |= ENTITY_CAN_JUMP; + } + if (entity->vel_y < 0) { + entity->vel_y = (entity->vel_y < -MAX_FALL ? -MAX_FALL : entity->vel_y); + } + if (entity->vel_x > 0) { + entity->vel_x = (entity->vel_x > MAX_VELOCITY ? MAX_VELOCITY : entity->vel_x-DRAG); + } else if (entity->vel_x < 0) { + entity->vel_x = (entity->vel_x < -MAX_VELOCITY ? -MAX_VELOCITY : entity->vel_x+DRAG); + } else { + entity->vel_x = 0; + } + float x = entity->x; + float y = entity->y; + //printf("%fvs%f %fvs%f\n", entity->y+entity->vel_y, min_y, entity->y+entity->vel_y, max_y); + if (entity->y+entity->vel_y >= max_y && entity->y+entity->vel_y <= min_y) { + y = entity->y + entity->vel_y; + } + if (entity->x+entity->vel_x >= min_x && entity->x+entity->vel_x <= max_x) { + x = entity->x + entity->vel_x; + } + entity->setPosition(x, y); + + entity->doThink(16); + } + entity_it = std::remove_if(g_entities.begin(), g_entities.end(), destroyEntity); + g_entities.erase(entity_it, g_entities.end()); + accumulator -= 16; + } + // 1. drawing + glViewport(0, 0, v_width, v_height); + glUniform2f(v_unif_sprite_resolution, (float)v_width, (float)v_height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // 1.1. draw world + glUseProgram(v_program_world); + glEnableVertexAttribArray(v_attr_world_vp); + // send quad + glBindBuffer(GL_ARRAY_BUFFER, v_quad_vbo); + glVertexAttribPointer(v_attr_world_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + std::vector::iterator world_it, world_end; + for (world_it = world.begin(), world_end = world.end(); world_it != world_end; ++world_it) { + World *werld = *world_it; + // send seed + float t = (float)SDL_GetTicks() / 10000.0f; + float seed_x = (sin(cos(t/10)*10) + cos(t*2.0)/4.0+sin(t*3.0)/6.0)*0.25; + float seed_y = (cos(sin(t/10)*10) + sin(t*2.0)/4.0+cos(t*3.0)/6.0)*0.25; + int iteration = werld->iteration; + glUniform2f(v_unif_world_seed, seed_x, seed_y); + glUniform2f(v_unif_world_size, werld->size_x, werld->size_y); + glUniform2f(v_unif_world_vp_offset, werld->offset_x, werld->offset_y); + // send iteration + glUniform1i(v_unif_world_iteration, iteration); + // send texture palette + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, v_texture_world); + glUniform1i(v_unif_world_texture, 0); + // draw quad + glDrawArrays(GL_TRIANGLES, 0, 6); + } + glDisableVertexAttribArray(v_attr_world_vp); + // okay, this is weird, but for our entities we grab sections of the world render for pixel-based collision detection on next iteration + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = *entity_it; + int p_x = (int)((entity->x+((float)v_width))/2.0f); + int p_y = (int)((entity->y+((float)v_height)-(float)entity->sprite.height)/2.0f); + glReadPixels(p_x, p_y, (int)entity->sprite.width, (int)entity->sprite.height, GL_RGBA, GL_FLOAT, entity->col_box); + } + // 1.2. draw sprites + glUseProgram(v_program_sprite); + glEnableVertexAttribArray(v_attr_sprite_vp); + glEnableVertexAttribArray(v_attr_sprite_uv); + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + //send mesh + glBindBuffer(GL_ARRAY_BUFFER, (*entity_it)->sprite.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, (*entity_it)->sprite.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, (*entity_it)->sprite.offset[0], (*entity_it)->sprite.offset[1]); + // set draw offset + glUniform2f(v_unif_sprite_vp_offset, (float)(int)(*entity_it)->x, (float)(int)(*entity_it)->y); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, (*entity_it)->sprite.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + glDrawArrays(GL_TRIANGLES, 0, 6); + } + // 1.3. draw waves UI + glBindBuffer(GL_ARRAY_BUFFER, waves.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, waves.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, waves.offset[0], waves.offset[1]); + // set draw offset + glUniform2f(v_unif_sprite_vp_offset, 640-128, 480-32); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, waves.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + glDrawArrays(GL_TRIANGLES, 0, 6); + // 1.4. draw player health + glBindBuffer(GL_ARRAY_BUFFER, health.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, health.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, health.offset[0], health.offset[1]); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, health.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + float i = 0; + for (i = 0; i < juliet.health; i++) { + // set draw offset + glUniform2f(v_unif_sprite_vp_offset, -((juliet.health*32)/2)+(i*32), (-480+20)); + glDrawArrays(GL_TRIANGLES, 0, 6); + } + // 1.5 draw instructions + glBindBuffer(GL_ARRAY_BUFFER, screens.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, screens.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, screens.offset[0], screens.offset[1]); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, screens.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + glUniform2f(v_unif_sprite_vp_offset, 0, (480-150)); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableVertexAttribArray(v_attr_sprite_uv); + glDisableVertexAttribArray(v_attr_sprite_vp); + + SDL_GL_SwapWindow(v_window); + } + return 0; +} diff --git a/src/game.hpp b/src/game.hpp new file mode 100644 index 0000000..904f15d --- /dev/null +++ b/src/game.hpp @@ -0,0 +1,4 @@ +#ifndef GAME_HPP +#define GAME_HPP +int runGame(); +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..763457f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,126 @@ +#ifdef __APPLE__ +#include +#include "CoreFoundation/CoreFoundation.h" +#else +#include +#endif +#include +#include +#include +#include +#include "v_common.hpp" +#include "a_common.hpp" +#include "shader.hpp" +#include "texture.hpp" +#include "game.hpp" + +int main(int argc, char *argv[]) { +#ifdef __APPLE__ + char path[PATH_MAX]; + CFURLRef res = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()); + CFURLGetFileSystemRepresentation(res, TRUE, (UInt8 *)path, PATH_MAX); + CFRelease(res); + chdir(path); +#endif + printf("Petite Juliet...!\n"); + + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL_Init failure", SDL_GetError(), NULL); + return 1; + } + // load sdl image + int flags = IMG_INIT_PNG; + if (!(IMG_Init(flags) & flags)) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "IMG_Init failure", IMG_GetError(), NULL); + return 1; + } + // load sdl mixer + flags = MIX_INIT_OGG; + if (!(Mix_Init(flags) & flags)) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Mix_Init failure", Mix_GetError(), NULL); + // let it run, I guess; + } + if (Mix_OpenAudio(a_rate, a_format, a_channels, a_buffers) != 0) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Mix_OpenAudio failure", Mix_GetError(), NULL); + // let it run, I guess; + } + // open audio files + a_sfx[SFX_KICK] = Mix_LoadWAV("sfx/kick.wav"); + a_sfx[SFX_PUNCH] = Mix_LoadWAV("sfx/punch.wav"); + a_sfx[SFX_WHOOSH] = Mix_LoadWAV("sfx/whoosh.wav"); + a_sfx[SFX_WIFF] = Mix_LoadWAV("sfx/wiff.wav"); + a_sfx[SFX_HIT] = Mix_LoadWAV("sfx/hit.wav"); + a_sfx[SFX_JUMP] = Mix_LoadWAV("sfx/jump.wav"); + + if ((v_window = SDL_CreateWindow("Petite Juliet", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, v_width, v_height, SDL_WINDOW_OPENGL)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL_CreateWindow failure", SDL_GetError(), NULL); + return 1; + } + SDL_ShowCursor(0); + if ((v_glcontext = SDL_GL_CreateContext(v_window)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL_GL_CreateContext failure", SDL_GetError(), NULL); + return 1; + } + #ifndef __APPLE__ + glewExperimental = GL_TRUE; + glewInit(); + #endif + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + SDL_GL_MakeCurrent(v_window, v_glcontext); + + glViewport(0, 0, v_width, v_height); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + SDL_GL_SwapWindow(v_window); + // compile shaders + GLuint vs, fs; + vs = compileShader("shaders/sprite_vs.glsl", GL_VERTEX_SHADER); + fs = compileShader("shaders/sprite_fs.glsl", GL_FRAGMENT_SHADER); + v_program_sprite = glCreateProgram(); + glAttachShader(v_program_sprite, vs); + glAttachShader(v_program_sprite, fs); + linkProgram(v_program_sprite); + glDeleteShader(vs); + glDeleteShader(fs); + v_attr_sprite_vp = glGetAttribLocation(v_program_sprite, "vp"); + v_unif_sprite_vp_offset = glGetUniformLocation(v_program_sprite, "vp_offset"); + v_attr_sprite_uv = glGetAttribLocation(v_program_sprite, "uv"); + v_unif_sprite_uv_offset = glGetUniformLocation(v_program_sprite, "uv_offset"); + v_unif_sprite_texture = glGetUniformLocation(v_program_sprite, "texture_sampler"); + v_unif_sprite_resolution = glGetUniformLocation(v_program_sprite, "resolution"); + // world shader + vs = compileShader("shaders/world_vs.glsl", GL_VERTEX_SHADER); + fs = compileShader("shaders/world_fs.glsl", GL_FRAGMENT_SHADER); + v_program_world = glCreateProgram(); + glAttachShader(v_program_world, vs); + glAttachShader(v_program_world, fs); + linkProgram(v_program_world); + glDeleteShader(vs); + glDeleteShader(fs); + v_attr_world_vp = glGetAttribLocation(v_program_world, "vp"); + v_unif_world_vp_offset = glGetUniformLocation(v_program_world, "vp_offset"); + v_unif_world_size = glGetUniformLocation(v_program_world, "size"); + v_unif_world_texture = glGetUniformLocation(v_program_world, "texture_sampler"); + v_unif_world_seed = glGetUniformLocation(v_program_world, "seed"); + v_unif_world_iteration = glGetUniformLocation(v_program_world, "iteration"); + // create our quad vertex buffer object for sprite drawing + glGenBuffers(1, &v_quad_vbo); + glBindBuffer(GL_ARRAY_BUFFER, v_quad_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(v_quad), v_quad, GL_STATIC_DRAW); + + // load graphics + v_texture_juliet = loadTexture("gfx/juliet.png"); + v_texture_doubt = loadTexture("gfx/doubt.png"); + v_texture_world = loadTexture("gfx/world.png"); + v_texture_waves = loadTexture("gfx/waves.png"); + v_texture_health = loadTexture("gfx/health.png"); + v_texture_screens = loadTexture("gfx/screens.png"); + // load sound + // load music + + runGame(); + + return 0; +} diff --git a/src/shader.cpp b/src/shader.cpp new file mode 100644 index 0000000..c10770f --- /dev/null +++ b/src/shader.cpp @@ -0,0 +1,58 @@ +#include "shader.hpp" +#include +#include +#include +#include +GLuint compileShader(const char *filename, GLenum type) { + std::string data; + std::ifstream filestream(filename, std::ios::in); + if(!filestream.is_open()) { + std::cerr << "compileShader: " << filename << " does not exist." << std::endl; + return 0; + } + std::string line; + while(!filestream.eof()) { + std::getline(filestream, line); + data.append(line + "\n"); + } + filestream.close(); + const char *buffer = data.c_str(); + // create and compile shader + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &buffer, NULL); + glCompileShader(shader); + // let's do some error checking + int params = -1; + glGetShaderiv(shader, GL_COMPILE_STATUS, ¶ms); + if (params != GL_TRUE) { + // TODO: replace with throw? + std::cout << "compileShader: shader " << shader << " failed to compile" << std::endl; + char log[2048]; + int len = 0; + glGetShaderInfoLog(shader, 2048, &len, log); + std::cout << log << std::endl; + return 0; + } + // free buffer and return our shader index + std::cout << "compileShader: built " << filename << " as " << shader << std::endl; + return shader; +} +/* ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, + linkProgram(GLuint program) + GLSL Shader program linking. It is left to the user to first create the program and attach the desired shaders. + ```````````````````````````````````````````````````````````````` */ +GLuint linkProgram(GLuint program) { + int params = -1; + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, ¶ms); + if (params != GL_TRUE) { + std::cout << "linkProgram: program " << program << " failed to link" << std::endl; + char log[2048]; + int len = 0; + glGetProgramInfoLog(program, 2048, &len, log); + std::cout << log << std::endl; + return 0; + } + std::cout << "linkProgram: successfully linked " << program << std::endl; + return program; +} diff --git a/src/shader.hpp b/src/shader.hpp new file mode 100644 index 0000000..7168493 --- /dev/null +++ b/src/shader.hpp @@ -0,0 +1,17 @@ +/* ================================================================ +Shader-related functions +```````````````` +This header file and accompanying source provides functions for reading, compiling, and linking GLSL shaders. +*/ +#ifndef SHADER_HPP +#define SHADER_HPP +#include +#include +#ifdef __APPLE__ +#include +#else +#include +#endif +GLuint compileShader(const char *filename, GLenum type); +GLuint linkProgram(GLuint program); +#endif diff --git a/src/texture.cpp b/src/texture.cpp new file mode 100644 index 0000000..d13c404 --- /dev/null +++ b/src/texture.cpp @@ -0,0 +1,51 @@ +#include "texture.hpp" +#if defined(_WIN32) || defined(__APPLE__) +#include +#include +#else +#include +#include +#endif + +GLuint loadTexture(const char *filename) { + // load image + SDL_Surface *image; + if ((image = IMG_Load(filename)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "loadTexture:IMG_Load", IMG_GetError(), NULL); + return 0; + } + // generate texture + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + // apply filtering (?) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + // + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); + SDL_FreeSurface(image); + return texture; +} + +GLuint loadTexture1D(const char *filename) { + // load image + SDL_Surface *image; + if ((image = IMG_Load(filename)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "loadTexture:IMG_Load", IMG_GetError(), NULL); + return 0; + } + // generate texture + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_1D, texture); + // apply filtering (?) + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); + // + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, image->w, 0, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); + SDL_FreeSurface(image); + return texture; + +} diff --git a/src/texture.hpp b/src/texture.hpp new file mode 100644 index 0000000..efbe98a --- /dev/null +++ b/src/texture.hpp @@ -0,0 +1,12 @@ +#ifndef TEXTURE_HPP +#define TEXTURE_HPP +#ifdef __APPLE__ +#include +#else +#include +#endif + +GLuint loadTexture(const char *filename); +GLuint loadTexture1D(const char *filename); + +#endif diff --git a/src/v_common.cpp b/src/v_common.cpp new file mode 100644 index 0000000..30aef81 --- /dev/null +++ b/src/v_common.cpp @@ -0,0 +1,39 @@ +#include "v_common.hpp" + +SDL_Window *v_window = NULL; +SDL_GLContext v_glcontext = 0; +unsigned int v_width = 640; +unsigned int v_height = 480; + +GLuint v_program_world = 0; +GLuint v_attr_world_vp = 0; +GLuint v_unif_world_texture = 0; +GLuint v_unif_world_seed = 0; +GLuint v_unif_world_iteration = 0; +GLuint v_unif_world_size = 0; +GLuint v_unif_world_vp_offset = 0; + +GLuint v_program_sprite = 0; +GLuint v_attr_sprite_vp = 0; +GLuint v_unif_sprite_vp_offset = 0; +GLuint v_attr_sprite_uv = 0; +GLuint v_unif_sprite_uv_offset = 0; +GLuint v_unif_sprite_texture = 0; +GLuint v_unif_sprite_resolution = 0; + +GLfloat v_quad[18] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, +}; +GLuint v_quad_vbo = 0; + +GLuint v_texture_world = 0; +GLuint v_texture_juliet = 0; +GLuint v_texture_doubt = 0; +GLuint v_texture_waves = 0; +GLuint v_texture_health = 0; +GLuint v_texture_screens = 0; diff --git a/src/v_common.hpp b/src/v_common.hpp new file mode 100644 index 0000000..a5fad6d --- /dev/null +++ b/src/v_common.hpp @@ -0,0 +1,40 @@ +#ifndef V_COMMON_HPP +#define V_COMMON_HPP +#ifdef __APPLE__ +#include +#else +#include +#endif +#include + +extern SDL_Window *v_window; +extern SDL_GLContext v_glcontext; +extern unsigned int v_width; +extern unsigned int v_height; + +extern GLuint v_program_world; +extern GLuint v_attr_world_vp; +extern GLuint v_unif_world_texture; +extern GLuint v_unif_world_seed; +extern GLuint v_unif_world_iteration; +extern GLuint v_unif_world_size; +extern GLuint v_unif_world_vp_offset; +extern GLuint v_program_sprite; +extern GLuint v_attr_sprite_vp; +extern GLuint v_unif_sprite_vp_offset; +extern GLuint v_attr_sprite_uv; +extern GLuint v_unif_sprite_uv_offset; +extern GLuint v_unif_sprite_texture; +extern GLuint v_unif_sprite_resolution; + +extern GLfloat v_quad[18]; +extern GLuint v_quad_vbo; + +extern GLuint v_texture_world; +extern GLuint v_texture_juliet; +extern GLuint v_texture_doubt; +extern GLuint v_texture_waves; +extern GLuint v_texture_health; +extern GLuint v_texture_screens; + +#endif diff --git a/todo.ktx b/todo.ktx new file mode 100644 index 0000000..f98f8e4 --- /dev/null +++ b/todo.ktx @@ -0,0 +1,20 @@ +1. frame buffer coloring object - additive layer that fades out. colors are added by players and special fx(blood,etc.) + +2. throwing entities (can throw players!) + +3. multiplayer, _NETWORKED_ and local + +4. game is in: stages->waves + each stage is a separate color, is shown as a separate FBO in the background. when current stage end, it zooms up to replace the current stage + waves then occur + +5. explosion/gore + +6. multiple enemy types + also boss - each boss is a shadow of the player, probably + +7. some sort of block/counter attack + +8. items (parasole, etc.) + +9. music and more sfx