1094 lines
36 KiB
C
1094 lines
36 KiB
C
#include "opengl.h"
|
|
#include <stdlib.h>
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_image.h>
|
|
#include <math.h>
|
|
#include "globals.h"
|
|
#include "State_Game.h"
|
|
#include "State_Game_Menu.h"
|
|
#include "State_Travel.h"
|
|
#include "State_Menu.h"
|
|
#include "state.h"
|
|
#include "game_globals.h"
|
|
#include "LiveMap.h"
|
|
#include "Ui.h"
|
|
#include "sprite.h"
|
|
#include "render.h"
|
|
#include "FrameSheet.h"
|
|
#include "AnimData.h"
|
|
#include "Particle.h"
|
|
#include "fifo.h" // for data type consts
|
|
#include "string.h" // for copyString
|
|
#include "Music.h"
|
|
#include "Message.h"
|
|
|
|
#include "MetaBit.h"
|
|
|
|
char *facing[] = {
|
|
"e",
|
|
"se",
|
|
"s",
|
|
"sw",
|
|
"w",
|
|
"nw",
|
|
"n",
|
|
"ne"
|
|
};
|
|
|
|
int openGameState() {
|
|
// default ish
|
|
map_zoom = 1.5f;
|
|
player = &live_map->player;
|
|
// override live map player with our loaded player data
|
|
if (is_new_game) {
|
|
setDefaultPlayerData(&pl_data);
|
|
is_new_game = 0;
|
|
} else {
|
|
pl_data.cyb_hp = pl_data.cyb_max_hp;
|
|
}
|
|
start_exp = pl_data.exp;
|
|
start_level = pl_data.level;
|
|
start_hp = pl_data.cyb_max_hp;
|
|
start_metabits = pl_data.metabits;
|
|
pl_data.active_metabits = 0;
|
|
// set initial camera to player
|
|
cleanVector(&camera);
|
|
camera.x = live_map->player.x - (g_v_width*map_zoom)/2;
|
|
camera.y = live_map->player.y - (g_v_height*map_zoom)/2;
|
|
// set player information
|
|
player->phys.direction.y = 1;
|
|
player->phys.mass = 10.0f;
|
|
player->phys.inverse_mass = -10.0f;
|
|
player->phys.radius = 16.0f;
|
|
|
|
player->phys.position.x = (float)live_map->player.x;
|
|
player->phys.position.y = (float)live_map->player.y;
|
|
|
|
player->walk_id = getAnimSetId(&player->animation, "walk");
|
|
player->idle_id = getAnimSetId(&player->animation, "idle");
|
|
player->curr_id = player->idle_id;
|
|
// temp
|
|
setAnimSet(&player->animation, "idle");
|
|
setAnimFace(&player->animation, "s");
|
|
player->height = player->animation.sheet->surfaces[0]->h;
|
|
|
|
initVoidMan(&projectiles, 8);
|
|
|
|
// create particle manager
|
|
initVoidMan(&particles, 512);
|
|
// create player bullet projectiles
|
|
// load in "fog"
|
|
fog = createSpriteFromFile(NULL, "gfx/mist.png");
|
|
|
|
cmd = 0;
|
|
// Set up popup message manager
|
|
current_message = NULL;
|
|
initVoidMan(&message_queue, 4);
|
|
current_hint = NULL;
|
|
initVoidMan(&hint_queue, 4);
|
|
// ui stuff
|
|
int offset = g_v_height/6;
|
|
text_hp = newTextt(&g_medium_glyphs, ui_colors, "hp: ");
|
|
text_hp->box.x = g_v_width - 256;
|
|
text_hp->box.y = offset;
|
|
offset += text_hp->box.h + 8;
|
|
text_metabits = newTextt(&g_medium_glyphs, ui_colors, "METABITs: ");
|
|
text_metabits->box.x = g_v_width - 256;
|
|
text_metabits->box.y = offset;
|
|
offset += text_metabits->box.h + 8;
|
|
text_exp = newTextt(&g_medium_glyphs, ui_colors, "exp: ");
|
|
text_exp->box.x = g_v_width - 256;
|
|
text_exp->box.y = offset;
|
|
offset += text_exp->box.h + 8;
|
|
text_level = newTextt(&g_medium_glyphs, ui_colors, "next META: ");
|
|
text_level->box.x = g_v_width - 256;
|
|
text_level->box.y = offset;
|
|
//offset += text_level->box.h + 8;
|
|
updateHp();
|
|
addExp(0);
|
|
//
|
|
meta_bit = NULL;
|
|
//initVoidMan(&metabits, 32);
|
|
metabits = newVoidMan(32);
|
|
metabit_status = METABIT_STATUS_HOME;
|
|
spawnMetaBits();
|
|
|
|
updateMeta();
|
|
|
|
// reset accumulator
|
|
accumulator = g_tickrate;
|
|
return 0;
|
|
}
|
|
|
|
int handleGameState(SDL_Event event) {
|
|
switch (event.type) {
|
|
case SDL_KEYDOWN:
|
|
l_input = KEY;
|
|
switch(event.key.keysym.sym) {
|
|
case SDLK_a:
|
|
case SDLK_LEFT:
|
|
cmd |= MOVE_LEFT;
|
|
break;
|
|
case SDLK_d:
|
|
case SDLK_RIGHT:
|
|
cmd |= MOVE_RIGHT;
|
|
break;
|
|
case SDLK_w:
|
|
case SDLK_UP:
|
|
cmd |= MOVE_UP;
|
|
break;
|
|
case SDLK_s:
|
|
case SDLK_DOWN:
|
|
cmd |= MOVE_DOWN;
|
|
break;
|
|
}
|
|
break;
|
|
case SDL_KEYUP:
|
|
l_input = KEY;
|
|
switch(event.key.keysym.sym) {
|
|
case SDLK_a:
|
|
case SDLK_LEFT:
|
|
cmd &= ~MOVE_LEFT;
|
|
break;
|
|
case SDLK_d:
|
|
case SDLK_RIGHT:
|
|
cmd &= ~MOVE_RIGHT;
|
|
break;
|
|
case SDLK_w:
|
|
case SDLK_UP:
|
|
cmd &= ~MOVE_UP;
|
|
break;
|
|
case SDLK_s:
|
|
case SDLK_DOWN:
|
|
cmd &= ~MOVE_DOWN;
|
|
break;
|
|
case SDLK_ESCAPE:
|
|
pushState(g_state_manager, newState(STATE_DEFAULT, openGameMenuState, closeGameMenuState, handleGameMenuState, processGameMenuState, renderGameMenuState));
|
|
break;
|
|
}
|
|
break;
|
|
case SDL_MOUSEMOTION:
|
|
mouse.x = event.motion.x;
|
|
mouse.y = event.motion.y;
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
mouse.x = event.motion.x;
|
|
mouse.y = event.motion.y;
|
|
if (event.button.button == SDL_BUTTON_LEFT) {
|
|
l_input = MOUSE;
|
|
cmd |= MOUSE_MOVE;
|
|
} else if (event.button.button == SDL_BUTTON_RIGHT) {
|
|
cmd |= METABIT_MOVE;
|
|
}
|
|
break;
|
|
case SDL_MOUSEBUTTONUP:
|
|
mouse.x = event.motion.x;
|
|
mouse.y = event.motion.y;
|
|
if (event.button.button == SDL_BUTTON_LEFT) {
|
|
cmd &= ~MOUSE_MOVE;
|
|
} else if (event.button.button == SDL_BUTTON_RIGHT) {
|
|
cmd &= ~METABIT_MOVE;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int processGameState() {
|
|
camera.x = player->phys.position.x - (g_v_width*map_zoom)/2;
|
|
camera.y = player->phys.position.y - (g_v_height*map_zoom)/2;
|
|
// Player Processing ================
|
|
/*
|
|
I want movement to predominantly work by using the awsd keys. However, so as to support the mouse for aiming - which I want - I'm thinking of using the following sort of system.
|
|
|
|
Movement in any cardinal direction is first accomplished by rotating towards the given direction while also applying force. For example, when one holds down the left arrow key or 'a', the character rotates towards the direction while exerting an amount of force influenced by wanted rotation vs. current (force is not as great until you are matching the direction). However, the crosshair influences the rotation in a similar way. If the crosshair is not within a 120(?) degree range of the current rotation, the rotation of the character moves towards the crosshair. Rotation is _only_ applied if the mouse move is more recently than a keypress - that is to say, if the crosshair is outside of the movement range, but the player has not moved the mouse (or has forcibly pressed a key after moving the mouse in the same motion), then rotation is unaffected by the mouse.
|
|
|
|
Will this be any good? Who knows.
|
|
*/
|
|
incAnimFrame(&player->animation);
|
|
player->l_phys = player->phys;
|
|
Vector acceleration = { 0, 0, 0 };
|
|
float force = 0.0f;
|
|
if (cmd & MOUSE_MOVE) {
|
|
Vector moose = mulVector(mouse, map_zoom);
|
|
moose = addVector(moose, camera);
|
|
moose.z = 0;
|
|
turnToVector(&player->phys, moose, 0.1);
|
|
if (player->curr_id != player->walk_id) {
|
|
player->curr_id = getAnimSetId(&player->animation, "walk");
|
|
setAnimSet(&player->animation, "walk");
|
|
setAnimFrame(&player->animation, 0);
|
|
}
|
|
force = 10.0;
|
|
}
|
|
if (cmd & MOVE_UP) {
|
|
if (player->curr_id != player->walk_id) {
|
|
player->curr_id = getAnimSetId(&player->animation, "walk");
|
|
setAnimSet(&player->animation, "walk");
|
|
setAnimFrame(&player->animation, 0);
|
|
}
|
|
if (l_input != MOUSE) player->phys.direction.y -= 0.2;
|
|
force = 10.0;
|
|
} else if (cmd & MOVE_DOWN) {
|
|
if (player->curr_id != player->walk_id) {
|
|
player->curr_id = getAnimSetId(&player->animation, "walk");
|
|
setAnimSet(&player->animation, "walk");
|
|
setAnimFrame(&player->animation, 0);
|
|
}
|
|
if (l_input != MOUSE) player->phys.direction.y += 0.2;
|
|
force = 10.0;
|
|
}
|
|
if (cmd & MOVE_LEFT) {
|
|
if (player->curr_id != player->walk_id) {
|
|
player->curr_id = getAnimSetId(&player->animation, "walk");
|
|
setAnimSet(&player->animation, "walk");
|
|
setAnimFrame(&player->animation, 0);
|
|
}
|
|
if (l_input != MOUSE) player->phys.direction.x -= 0.2;
|
|
force = 10.0;
|
|
} else if (cmd & MOVE_RIGHT) {
|
|
if (player->curr_id != player->walk_id) {
|
|
player->curr_id = getAnimSetId(&player->animation, "walk");
|
|
setAnimSet(&player->animation, "walk");
|
|
setAnimFrame(&player->animation, 0);
|
|
}
|
|
if (l_input != MOUSE) player->phys.direction.x += 0.2;
|
|
force = 10.0;
|
|
}
|
|
if (checkAnimTag(&player->animation, "walk")) {
|
|
force += 60.0;
|
|
}
|
|
|
|
if (force > 70.0f) {
|
|
force = 70.0f;
|
|
}
|
|
// temp
|
|
struct Particle *step = newParticle();
|
|
step->phys = player->phys;
|
|
step->phys.position.y -= player->height/4;
|
|
step->phys.velocity.x /= 4;
|
|
step->phys.velocity.y /= 4;
|
|
step->animation = player->animation;
|
|
step->animation.anim_bool = 0;
|
|
step->time = 250;
|
|
step->a = 0.05f * ((float)pl_data.cyb_hp / (float)pl_data.cyb_max_hp);
|
|
addObject(&particles, step);
|
|
|
|
player->phys.direction = normVector(player->phys.direction);
|
|
// acceleration = (direction * force) / mass
|
|
acceleration = player->phys.direction;
|
|
acceleration = mulVector(acceleration, force);
|
|
acceleration = divVector(acceleration, player->phys.mass);
|
|
//
|
|
acceleration = addVector(acceleration, mulVector(player->phys.velocity, -0.20));
|
|
// update player position first
|
|
//player->phys.position = addVector(player->phys.position, player->phys.velocity);
|
|
// oh my, this is horrible
|
|
player->phys.position.x += player->phys.velocity.x;
|
|
int test_x, test_y;
|
|
test_x = (int)player->phys.position.x/g_tile_w;
|
|
test_y = (int)player->phys.position.y/g_tile_h;
|
|
if (test_x >= 0 && test_x < live_map->width && test_y >= 0 && test_y < live_map->height) {
|
|
struct Circle cell = { test_x*g_tile_w+(g_tile_w/2), test_y*g_tile_h+(g_tile_h/2), (g_tile_w + g_tile_h) };
|
|
if (collPhysCircle(&player->phys, &cell)) {
|
|
if (live_map->cells[test_x][test_y].flags & CELL_BLOCKS_ENTITY) {
|
|
player->phys.position.x -= player->phys.velocity.x;
|
|
}
|
|
}
|
|
}
|
|
player->phys.position.y += player->phys.velocity.y;
|
|
test_x = (int)player->phys.position.x/g_tile_w;
|
|
test_y = (int)player->phys.position.y/g_tile_h;
|
|
if (test_x >= 0 && test_x < live_map->width && test_y >= 0 && test_y < live_map->height) {
|
|
struct Circle cell = { test_x*g_tile_w+(g_tile_w/2), test_y*g_tile_h+(g_tile_h/2), (g_tile_w + g_tile_h) };
|
|
if (collPhysCircle(&player->phys, &cell)) {
|
|
if (live_map->cells[test_x][test_y].flags & CELL_BLOCKS_ENTITY) {
|
|
player->phys.position.y -= player->phys.velocity.y;
|
|
|
|
}
|
|
}
|
|
}
|
|
// end the horror
|
|
if (meta_bit != NULL) {
|
|
if (cmd & METABIT_MOVE) {
|
|
if (metabit_status == METABIT_STATUS_HOME) {
|
|
metabit_status = METABIT_STATUS_TARGETTING;
|
|
setMetaBitTarget(&metabit_target);
|
|
} else if (metabit_status == METABIT_STATUS_TARGETTING) {
|
|
metabit_target = mulVector(mouse, map_zoom);
|
|
metabit_target = addVector(metabit_target, camera);
|
|
} else if (metabit_status == METABIT_STATUS_TARGET) {
|
|
metabit_status = METABIT_STATUS_HOMING;
|
|
setMetaBitTarget(NULL);
|
|
}
|
|
} else {
|
|
if (metabit_status == METABIT_STATUS_TARGETTING) {
|
|
metabit_status = METABIT_STATUS_TARGET;
|
|
} else if (metabit_status == METABIT_STATUS_HOMING) {
|
|
metabit_status = METABIT_STATUS_HOME;
|
|
}
|
|
}
|
|
if (abs(meta_bit->phys.position.x - player->phys.position.x) > 300 || abs(meta_bit->phys.position.y - player->phys.position.y) > 300) {
|
|
metabit_status = METABIT_STATUS_HOMING;
|
|
setMetaBitTarget(NULL);
|
|
}
|
|
}
|
|
|
|
acceleration = mulVector(acceleration, 0.5);
|
|
player->phys.velocity = addVector(player->phys.velocity, acceleration);
|
|
|
|
// get facing direction bah, didn't want atan2
|
|
float test = atan2(player->phys.direction.y, player->phys.direction.x);
|
|
int octant = ((int)roundf(8 * test / (2*M_PI) + 8.0))%8;
|
|
setAnimFace(&player->animation, facing[octant]);
|
|
|
|
// we only change from walking state to idle state if we're in the pass frame
|
|
if (fabs(player->phys.velocity.x)+fabs(player->phys.velocity.y) <= 1.0) {
|
|
if (checkAnimTag(&player->animation, "pass")) {
|
|
setAnimSet(&player->animation, "idle");
|
|
player->curr_id = getAnimSetId(&player->animation, "idle");
|
|
}
|
|
}
|
|
// Collision detect with triggers!
|
|
handlePlayerTriggers();
|
|
|
|
int i;
|
|
while ((i = iterObjecti(&projectiles)) != -1) {
|
|
|
|
}
|
|
|
|
// meta bit
|
|
if (meta_bit != NULL) {
|
|
incAnimFrame(&meta_bit->animation);
|
|
handleMetaBit(meta_bit);
|
|
doVelocity(&meta_bit->phys);
|
|
struct Particle *ghost = newParticle();
|
|
ghost->phys.position = meta_bit->phys.position;
|
|
ghost->animation = meta_bit->animation;
|
|
ghost->time = 50;
|
|
ghost->a = 0.05f;
|
|
addObject(&particles, ghost);
|
|
handleMetaBitTriggers(meta_bit);
|
|
}
|
|
// meta bits
|
|
while ((i = iterObjecti(metabits)) != -1) {
|
|
struct MetaBit *metabit = metabits->object[i];
|
|
incAnimFrame(&metabit->animation);
|
|
|
|
handleMetaBit(metabit);
|
|
doVelocity(&metabit->phys);
|
|
|
|
struct Particle *ghost = newParticle();
|
|
ghost->phys.position = metabit->phys.position;
|
|
ghost->animation = metabit->animation;
|
|
ghost->time = 50;
|
|
ghost->a = 0.05f;
|
|
addObject(&particles, ghost);
|
|
handleMetaBitTriggers(metabit);
|
|
}
|
|
// okay entities
|
|
while ((i = iterObjecti(live_map->entities)) != -1) {
|
|
struct Entity *entity = live_map->entities->object[i];
|
|
incAnimFrame(&entity->animation);
|
|
handleEntity(entity);
|
|
//
|
|
struct Particle *step = newParticle();
|
|
step->phys = entity->phys;
|
|
//step->phys.position.y -= entity->height/4;
|
|
step->phys.velocity.x /= 4;
|
|
step->phys.velocity.y /= 4;
|
|
step->animation = entity->animation;
|
|
step->animation.anim_bool = 0;
|
|
step->time = 250;
|
|
step->a = 0.05f * ((float)entity->hp / (float)entity->max_hp);
|
|
step->phys.position.y -= entity->height;
|
|
addObject(&particles, step);
|
|
if (meta_bit != NULL) {
|
|
struct Circle circle;
|
|
circle.x = meta_bit->phys.position.x;
|
|
circle.y = meta_bit->phys.position.y;
|
|
circle.radius = meta_bit->phys.radius;
|
|
if (collPhysCircle(&entity->phys, &circle)) {
|
|
entity->hp -= pl_data.active_metabits;
|
|
}
|
|
}
|
|
if (entity->hp <= 0) {
|
|
addExp((entity->max_hp+entity->range)/(entity->speed+entity->damage));
|
|
freeEntity(entity);
|
|
delObject(live_map->entities, i);
|
|
}
|
|
}
|
|
|
|
// particles
|
|
while ((i = iterObjecti(&particles)) != -1) {
|
|
struct Particle *particle = particles.object[i];
|
|
if (particle != NULL) {
|
|
if (particle->time <= 0 && (particle->flags & PART_DIE)) {
|
|
delObject(&particles, i);
|
|
} else {
|
|
incAnimFrame(&particle->animation);
|
|
doVelocity(&particle->phys);
|
|
particle->time -= g_tickrate.m;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Map Decor Processing ================
|
|
int w = g_tile_w;
|
|
int h = g_tile_h;
|
|
int max_width = (g_v_width / w) * map_zoom;
|
|
int max_height = (g_v_height / h) * map_zoom;
|
|
int start_x = (camera.x / w) - 6;
|
|
int start_y = (camera.y / h) - 6;
|
|
int end_x = ((camera.x / w) + max_width + 6);
|
|
int end_y = ((camera.y / h) + max_height + 6);
|
|
int x, y;
|
|
for (y = start_y; y < end_y; y++) {
|
|
for (x = start_x; x < end_x; x++) {
|
|
if (x < 0 || x >= live_map->width || y < 0 || y >= live_map->height) continue;
|
|
int d;
|
|
for (d = 0; d < live_map->cells[x][y].decor_count; d++) {
|
|
struct Decor *decor = &live_map->cells[x][y].decor[d];
|
|
addAnimDelta(&decor->animation, g_tickrate.m);
|
|
|
|
//if (decor->animation.delta >= decor->animation.set->fps/1000) {
|
|
if (decor->animation.anim_bool == 1) {
|
|
incAnimFrame(&decor->animation);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process active triggers ================
|
|
processTriggers();
|
|
// Process messages ================
|
|
if (current_message != NULL) {
|
|
processMessage(current_message, g_tickrate.m);
|
|
if (current_message->elapsed >= current_message->time) {
|
|
freeMessage(current_message);
|
|
current_message = NULL;
|
|
int next = getObjecti(&message_queue);
|
|
if (next != -1) {
|
|
current_message = message_queue.object[next];
|
|
remObject(&message_queue, next);
|
|
}
|
|
}
|
|
} else {
|
|
int next = getObjecti(&message_queue);
|
|
if (next != -1) {
|
|
current_message = message_queue.object[next];
|
|
remObject(&message_queue, next);
|
|
}
|
|
}
|
|
// Process hints ================
|
|
if (current_hint != NULL) {
|
|
processMessage(current_hint, g_tickrate.m);
|
|
if (current_hint->elapsed >= current_hint->time) {
|
|
freeMessage(current_hint);
|
|
current_hint = NULL;
|
|
int next = getObjecti(&hint_queue);
|
|
if (next != -1) {
|
|
current_hint = hint_queue.object[next];
|
|
remObject(&hint_queue, next);
|
|
}
|
|
}
|
|
} else {
|
|
int next = getObjecti(&hint_queue);
|
|
if (next != -1) {
|
|
current_hint = hint_queue.object[next];
|
|
remObject(&hint_queue, next);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int renderGameState() {
|
|
int x, y;
|
|
int w = g_tile_w;
|
|
int h = g_tile_h;
|
|
|
|
int max_width = (g_v_width / w) * map_zoom;
|
|
int max_height = (g_v_height / h) * map_zoom;
|
|
|
|
int start_x = (camera.x / w) - 6;
|
|
int start_y = (camera.y / h) - 6;
|
|
int end_x = ((camera.x / w) + max_width + 6);
|
|
int end_y = ((camera.y / h) + max_height + 6);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0.0f, g_v_width*map_zoom, g_v_height*map_zoom, 1.0f, -1.0f, 1.0f);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
// render map!
|
|
for (y = start_y; y < end_y+1; y++) {
|
|
for (x = start_x; x < end_x+1; x++) {
|
|
if (x < 0 || x >= live_map->width || y < 0 || y >= live_map->height) continue;
|
|
int x_pos = x*w-(camera.x)+1;
|
|
int y_pos = y*h-(camera.y)+1;
|
|
//renderLQuad(x_pos, y_pos, w-1, h-1, ui_colors->fg);
|
|
int d;
|
|
for (d = 0; d < live_map->cells[x][y].decor_count; d++) {
|
|
struct Decor *decor = &live_map->cells[x][y].decor[d];
|
|
if (decor == NULL) continue;
|
|
renderFrame(decor->animation.sheet, decor->animation.f, x_pos+decor->x, y_pos+decor->y);
|
|
}
|
|
}
|
|
}
|
|
|
|
// render player
|
|
glColor4f(1, 1, 1, ((float)pl_data.cyb_hp / (float)pl_data.cyb_max_hp));
|
|
renderFrame(player->animation.sheet, player->animation.f, player->phys.position.x-camera.x, player->phys.position.y-camera.y-(player->height/4));
|
|
//
|
|
int i;
|
|
// entities
|
|
while ((i = iterObjecti(live_map->entities)) != -1) {
|
|
struct Entity *entity = live_map->entities->object[i];
|
|
// ... bad magic height number since we don't have collision offsets
|
|
glColor4f(1, 1, 1, ((float)entity->hp / (float)entity->max_hp));
|
|
renderFrame(entity->animation.sheet, entity->animation.f, entity->phys.position.x-camera.x, entity->phys.position.y-camera.y-(entity->height));
|
|
}
|
|
if (meta_bit != NULL) {
|
|
glColor4f(meta_bit->r, meta_bit->g, meta_bit->b, meta_bit->a);
|
|
renderFrame(meta_bit->animation.sheet, meta_bit->animation.f, meta_bit->phys.position.x-camera.x, meta_bit->phys.position.y-camera.y);
|
|
}
|
|
// render particles
|
|
while ((i = iterObjecti(&particles)) != -1) {
|
|
struct Particle *p = particles.object[i];
|
|
glColor4f(p->r, p->g, p->b, p->a);
|
|
renderFrame(p->animation.sheet, p->animation.f, p->phys.position.x-camera.x, p->phys.position.y-camera.y);
|
|
}
|
|
while ((i = iterObjecti(metabits)) != -1) {
|
|
struct MetaBit *p = metabits->object[i];
|
|
glColor4f(p->r, p->g, p->b, p->a);
|
|
renderFrame(p->animation.sheet, p->animation.f, p->phys.position.x-camera.x, p->phys.position.y-camera.y);
|
|
}
|
|
|
|
// reset view
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0.0f, g_v_width, g_v_height, 1.0f, -1.0f, 1.0f);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
// render text
|
|
renderTextt(text_hp);
|
|
renderTextt(text_metabits);
|
|
renderTextt(text_exp);
|
|
renderTextt(text_level);
|
|
// render message ================
|
|
if (current_message != NULL) renderMessage(current_message);
|
|
if (current_hint != NULL) renderMessage(current_hint);
|
|
|
|
// finally, render fog overlay
|
|
glLoadIdentity();
|
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fog->texture);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
glTranslated(0, 0, 1.0f);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2d(0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
|
|
glTexCoord2d(1.0f, 0.0f); glVertex2f(g_v_width, 0.0f);
|
|
glTexCoord2d(1.0f, 1.0f); glVertex2f(g_v_width, g_v_height);
|
|
glTexCoord2d(0.0f, 1.0f); glVertex2f(0.0f, g_v_height);
|
|
glEnd();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void closeGameState() {
|
|
freeSprite(fog);
|
|
clearVoidMan(&particles);
|
|
freeVoidMan(metabits);
|
|
clearVoidMan(&projectiles);
|
|
clearVoidMan(&message_queue);
|
|
clearVoidMan(&hint_queue);
|
|
free(meta_bit);
|
|
meta_bit = NULL;
|
|
freeLiveMap(live_map);
|
|
freeResources(live_animations);
|
|
freeResources(live_sprites);
|
|
freeResources(live_entities);
|
|
freeTextt(text_hp);
|
|
freeTextt(text_metabits);
|
|
freeTextt(text_exp);
|
|
freeTextt(text_level);
|
|
if (is_end_game == 1) {
|
|
pushState(g_state_manager, newState(STATE_DEFAULT, openMenuState, closeMenuState, handleMenuState, processMenuState, renderMenuState));
|
|
} else if (travel_map[0] != '\0') {
|
|
pushState(g_state_manager, newState(STATE_DEFAULT, openTravelState, closeTravelState, handleTravelState, processTravelState, renderTravelState));
|
|
} else {
|
|
pushState(g_state_manager, newState(STATE_DEFAULT, openMenuState, closeMenuState, handleMenuState, processMenuState, renderMenuState));
|
|
}
|
|
report(DEBUG, "GameState", "closed");
|
|
}
|
|
|
|
void updateHp() {
|
|
setTextt(text_hp, "HP: %d/%d", pl_data.cyb_hp, pl_data.cyb_max_hp);
|
|
}
|
|
void addExp(int amount) {
|
|
pl_data.exp += amount;
|
|
while (pl_data.exp >= pl_data.level) {
|
|
pl_data.level *= 2;
|
|
spawnMetaBit(player->phys.position.x, player->phys.position.y);
|
|
pl_data.metabits++;
|
|
setTextt(text_level, "next META: %d", pl_data.level);
|
|
}
|
|
setTextt(text_exp, "exp: %d", pl_data.exp);
|
|
}
|
|
void updateMeta() {
|
|
setTextt(text_metabits, "METABITs: %d", pl_data.active_metabits);
|
|
}
|
|
|
|
void addDamage(int amount) {
|
|
pl_data.cyb_hp -= amount;
|
|
if (pl_data.cyb_hp <= 0) {
|
|
handleDeath();
|
|
}
|
|
updateHp();
|
|
}
|
|
|
|
int handleDeath() {
|
|
// show fade or something here...
|
|
pl_data.exp = start_exp;
|
|
pl_data.level = start_level;
|
|
pl_data.metabits = start_metabits;
|
|
pl_data.cyb_hp = start_hp;
|
|
popState(g_state_manager);
|
|
return 0;
|
|
}
|
|
|
|
void handlePlayerTriggers() {
|
|
int trigger_i = -1;
|
|
while ((trigger_i = iterObjecti(live_map->triggers)) != -1) {
|
|
struct Trigger *trigger = live_map->triggers->object[trigger_i];
|
|
if (trigger->activator != T_ACT_PLAYER) continue;
|
|
if (trigger->type != T_TYPE_COLLIDE) continue;
|
|
if (collPhysBox(&player->phys, &trigger->box)) {
|
|
activateTrigger(trigger_i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void handleMetaBitTriggers(struct MetaBit *metabit) {
|
|
int trigger_i;
|
|
while ((trigger_i = iterObjecti(live_map->triggers)) != -1) {
|
|
struct Trigger *trigger = live_map->triggers->object[trigger_i];
|
|
if (trigger->activator != T_ACT_METABIT) continue;
|
|
if (trigger->type != T_TYPE_COLLIDE) continue;
|
|
if (collPhysBox(&metabit->phys, &trigger->box)) {
|
|
activateTrigger(trigger_i);
|
|
}
|
|
}
|
|
}
|
|
void setMetaBitTarget(struct Vector *vector) {
|
|
meta_bit->target = vector;
|
|
}
|
|
|
|
void setMetaBitsTarget(struct Vector *vector) {
|
|
struct MetaBit *bit;
|
|
while((bit = iterObject(metabits)) != NULL) {
|
|
bit->target = vector;
|
|
}
|
|
}
|
|
|
|
void activateTrigger(int id) {
|
|
if (live_map->triggers->object[id] == NULL) return;
|
|
int i;
|
|
struct Trigger *trigger = live_map->triggers->object[id];
|
|
switch (trigger->behavior) {
|
|
case T_BEHAVE_NORM: // normal behavior is to run events consecutively once, then die
|
|
remObject(live_map->triggers, id);
|
|
addObject(live_map->active_triggers, trigger);
|
|
break;
|
|
case T_BEHAVE_ALL: // all is to call all events, then die
|
|
for (i = 0; i < trigger->event_count; i++) {
|
|
activateEvent(trigger->events[i]);
|
|
}
|
|
// delete trigger
|
|
freeTrigger(trigger);
|
|
delObject(live_map->triggers, id);
|
|
break;
|
|
case T_BEHAVE_LOOP: // akin to NORM, but in a loop until iter
|
|
remObject(live_map->triggers, id);
|
|
addObject(live_map->active_triggers, trigger);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void activateEvent(int id) {
|
|
if (id < 0) return;
|
|
if (live_map->events->object[id] == NULL) return;
|
|
struct Event *event = live_map->events->object[id];
|
|
switch(event->type) {
|
|
case E_TYPE_MSG:
|
|
if (event->param_count < 3) return;
|
|
eventMessage(event);
|
|
break;
|
|
case E_TYPE_HINT:
|
|
if (event->param_count < 2) return;
|
|
eventHint(event);
|
|
break;
|
|
case E_TYPE_GO:
|
|
if (event->param_count < 1) return;
|
|
eventGo(event);
|
|
break;
|
|
case E_TYPE_DECORSET:
|
|
if (event->param_count < 3) return;
|
|
eventDecorSet(event);
|
|
break;
|
|
case E_TYPE_DECORDEL:
|
|
if (event->param_count < 2) return;
|
|
eventDecorDelete(event);
|
|
break;
|
|
case E_TYPE_CELL:
|
|
if (event->param_count < 1) return;
|
|
eventCellSet(event);
|
|
break;
|
|
case E_TYPE_MUSIC_SET:
|
|
if (event->param_count < 1) return;
|
|
eventMusicSet(event);
|
|
break;
|
|
case E_TYPE_MUSIC_START:
|
|
eventMusicStart(event);
|
|
break;
|
|
case E_TYPE_MUSIC_STOP:
|
|
eventMusicStop(event);
|
|
break;
|
|
case E_TYPE_SPAWN_METABIT:
|
|
if (event->param_count < 1) return;
|
|
eventMetaBitSpawn(event);
|
|
break;
|
|
case E_TYPE_END:
|
|
eventEndGame(event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void eventMessage(struct Event *event) {
|
|
if (event->param_type[0] != T_STRING) return;
|
|
if (event->param_type[1] != T_STRING) return;
|
|
if (event->param_type[2] != T_INT) return;
|
|
|
|
struct Box box = { 0, g_v_height-150, g_v_width, 150 };
|
|
struct Message *message = newMessage(box, &g_large_glyphs, ui_colors, *(int*)event->params[2], (char*)event->params[1]);
|
|
// set up profile pic
|
|
struct AnimData *anim = getResource(live_animations, event->params[0]);
|
|
if (anim != NULL) {
|
|
message->animation.anim = anim;
|
|
setAnimSet(&message->animation, "profile");
|
|
setAnimFace(&message->animation, "s");
|
|
setAnimFrame(&message->animation, 0);
|
|
}
|
|
addObject(&message_queue, message);
|
|
}
|
|
void eventHint(struct Event *event) {
|
|
if (event->param_type[0] != T_STRING) return;
|
|
if (event->param_type[1] != T_INT) return;
|
|
struct Box box = { -250 + (g_v_width/4), g_v_height-g_v_height/3, g_v_width-32, 64 };
|
|
struct Message *hint = newMessage(box, &g_medium_glyphs, hint_colors, *(int*)event->params[1], (char*)event->params[0]);
|
|
if (current_hint != NULL) {
|
|
current_hint->elapsed = current_hint->time;
|
|
}
|
|
addObject(&hint_queue, hint);
|
|
}
|
|
void eventGo(struct Event *event) {
|
|
if (event->param_type[0] != T_STRING) return;
|
|
travel_map = copyString(travel_map, event->params[0]);
|
|
printf("traveling to %s\n", travel_map);
|
|
popState(g_state_manager);
|
|
}
|
|
void eventDecorSet(struct Event *event) {
|
|
if (event->param_type[0] != T_STRING) return;
|
|
if (event->param_type[1] != T_INT) return;
|
|
if (event->param_type[2] != T_INT) return;
|
|
int i = 0;
|
|
int cell_x = event->box.x / g_tile_w;
|
|
int cell_y = event->box.y / g_tile_h;
|
|
if (cell_x < 0 || cell_x >= live_map->width || cell_y < 0 || cell_y >= live_map->height) return;
|
|
int d_count = live_map->cells[cell_x][cell_y].decor_count;
|
|
if (d_count <= 0) return;
|
|
// okay, now we can set
|
|
int start = *(int*)event->params[1];
|
|
int count = *(int*)event->params[2];
|
|
for (i = start; i < start+count; i++) {
|
|
if (i > d_count) break;
|
|
struct Decor *decor = &live_map->cells[cell_x][cell_y].decor[i];
|
|
if (decor == NULL) continue;
|
|
printf("changing %d, from %d+%d\n", i, start, count);
|
|
printf("it is %s\n", decor->animation.anim->name);
|
|
setAnimSet(&decor->animation, event->params[0]);
|
|
setAnimFace(&decor->animation, decor->animation.face->name);
|
|
}
|
|
// TODO: check for all decors colliding in bounding box
|
|
}
|
|
void eventDecorDelete(struct Event *event) {
|
|
if (event->param_type[0] != T_INT) return;
|
|
if (event->param_type[1] != T_INT) return;
|
|
int i = 0;
|
|
int cell_x = event->box.x / g_tile_w;
|
|
int cell_y = event->box.y / g_tile_h;
|
|
if (cell_x < 0 || cell_x >= live_map->width || cell_y < 0 || cell_y >= live_map->height) return;
|
|
int d_count = live_map->cells[cell_x][cell_y].decor_count;
|
|
if (d_count <= 0) return;
|
|
// okay, now we can del
|
|
int start = *(int*)event->params[0];
|
|
int count = *(int*)event->params[1];
|
|
i = 0;
|
|
while (i < count) {
|
|
delDecor(&live_map->cells[cell_x][cell_y], start);
|
|
i++;
|
|
}
|
|
}
|
|
void eventCellSet(struct Event *event) {
|
|
if (event->param_type[0] != T_INT) return;
|
|
int x = 0, y = 0;
|
|
int start_x = event->box.x/g_tile_w;
|
|
int start_y = event->box.y/g_tile_h;
|
|
int end_x = (event->box.x+event->box.w)/g_tile_w;
|
|
int end_y = (event->box.y+event->box.h)/g_tile_h;
|
|
printf("%dx%d to %dx%d\n", event->box.x, event->box.y, event->box.w, event->box.h);
|
|
printf("checking %dx%d to %dx%d for %d\n", start_x, start_y, end_x, end_y, *(int*)event->params[0]);
|
|
for (x = start_x; x <= end_x; x++) {
|
|
for (y = start_y; y <= end_y; y++) {
|
|
if (x < 0 || x >= live_map->width) continue;
|
|
if (y < 0 || y >= live_map->height) continue;
|
|
live_map->cells[x][y].flags = *(int*)event->params[0];
|
|
}
|
|
}
|
|
// TODO: set all cells colliding to passed int
|
|
}
|
|
|
|
void eventMusicSet(struct Event *event) {
|
|
if (event->param_type[0] != T_STRING) return;
|
|
loadResource(g_music, event->params[0]);
|
|
}
|
|
void eventMusicStart(struct Event *event) {
|
|
if (event->param_type[0] != T_STRING) return;
|
|
playMusic(getResource(g_music, event->params[0]));
|
|
}
|
|
void eventMusicStop(struct Event *event) {
|
|
stopMusic();
|
|
}
|
|
|
|
void eventEndGame(struct Event *event) {
|
|
popState(g_state_manager);
|
|
is_end_game = 1;
|
|
}
|
|
|
|
void eventMetaBitSpawn(struct Event *event) {
|
|
if (event->param_type[0] != T_INT) return;
|
|
int count = *(int*)event->params[0];
|
|
int i;
|
|
for (i = 0; i < count; i++) {
|
|
spawnMetaBit(event->box.x+event->box.w/2, event->box.y+event->box.h/2);
|
|
pl_data.metabits++;
|
|
}
|
|
}
|
|
|
|
void spawnMetaBit(int x, int y) {
|
|
// if player has no metabits yet, spawn the initial
|
|
struct Phys ph;
|
|
cleanPhys(&ph);
|
|
if (pl_data.active_metabits == 0) {
|
|
ph.direction.y = 1;
|
|
ph.mass = 2.0f;
|
|
ph.radius = 64.0f;
|
|
meta_bit = newMetaBit(TYPE_GENERIC, OWNER_PLAYER, &player->phys.position, ph);
|
|
meta_bit->turn_rate = 2.0f;
|
|
meta_bit->speed = 2.5f;
|
|
setMetaBitAnim(meta_bit, getResource(live_animations, "cyb_metabit"));
|
|
meta_bit->home = &player->phys.position;
|
|
meta_bit->home_offset.y -= player->height - (player->height/6);
|
|
meta_bit->phys.position.x = x;
|
|
meta_bit->phys.position.y = y;
|
|
pl_data.active_metabits++;
|
|
} else {
|
|
ph.direction.y = 1;
|
|
ph.mass = 2.0f;
|
|
ph.radius = 8.0f;
|
|
//ph.position.x = (-60 + (rand()%120));
|
|
//ph.position.y = (-60 + (rand()%120));
|
|
ph.position.x = x;
|
|
ph.position.y = y;
|
|
//struct MetaBit *new_bit = newMetaBit(TYPE_GENERIC, OWNER_PLAYER, &player->phys.position, ph);
|
|
struct MetaBit *new_bit = newMetaBit(TYPE_GENERIC, OWNER_PLAYER, &meta_bit->phys.position, ph);
|
|
setMetaBitAnim(new_bit, getResource(live_animations, "cyb_metabit"));
|
|
switch(pl_data.active_metabits % 3) {
|
|
case 0:
|
|
new_bit->r = 0.75f;
|
|
new_bit->g = 0.50f;
|
|
new_bit->b = 0.50f;
|
|
new_bit->a = 0.5f;
|
|
break;
|
|
case 1:
|
|
new_bit->r = 0.25f;
|
|
new_bit->b = 0.25f;
|
|
new_bit->a = 0.5f;
|
|
break;
|
|
case 2:
|
|
new_bit->r = 0.25f;
|
|
new_bit->g = 0.50f;
|
|
new_bit->b = 0.75f;
|
|
new_bit->a = 0.5f;
|
|
break;
|
|
}
|
|
//new_bit->home_offset.y -= player->height - (player->height/3);
|
|
addObject(metabits, new_bit);
|
|
pl_data.active_metabits++;
|
|
}
|
|
updateMeta();
|
|
}
|
|
|
|
// handleEntity - this is quite horrible. Hackish.
|
|
void handleEntity(struct Entity *entity) {
|
|
struct Circle circle;
|
|
circle.x = entity->phys.position.x;
|
|
circle.y = entity->phys.position.y;
|
|
|
|
float test = atan2(entity->phys.direction.y, entity->phys.direction.x);
|
|
int octant = ((int)roundf(8 * test / (2*M_PI) + 8.0))%8;
|
|
setAnimFace(&entity->animation, facing[octant]);
|
|
|
|
// if (fabs(entity->phys.velocity.x)+fabs(entity->phys.velocity.y) <= 1.0) {
|
|
// if (checkAnimTag(&entity->animation, "pass")) {
|
|
// setAnimSet(&entity->animation, "idle");
|
|
// entity->curr_id = getAnimSetId(&entity->animation, "idle");
|
|
// }
|
|
// }
|
|
Vector target;
|
|
if (entity->target != NULL) {
|
|
target = *entity->target;
|
|
} else {
|
|
target = entity->phys.position;
|
|
}
|
|
target.x += (-60 + (rand()%120));
|
|
target.y += (-60 + (rand()%120));
|
|
|
|
switch(entity->mode) {
|
|
case E_FIND_PLAYER:
|
|
circle.radius = entity->sight;
|
|
if (collPhysCircle(&player->phys, &circle)) {
|
|
entity->target = &player->phys.position;
|
|
entity->mode = E_MOVE_PLAYER;
|
|
setAnimSet(&entity->animation, "walk");
|
|
} else {
|
|
if (entity->logic == E_LOGIC_WANDER) {
|
|
setAnimSet(&entity->animation, "walk");
|
|
turnToVector(&entity->phys, target, entity->turn_rate);
|
|
addForce(&entity->phys, entity->speed+(rand()%(int)entity->speed));
|
|
handleEntityCollision(entity);
|
|
}
|
|
}
|
|
break;
|
|
case E_MOVE_PLAYER:
|
|
turnToVector(&entity->phys, target, entity->turn_rate);
|
|
addForce(&entity->phys, entity->speed+(rand()%(int)entity->speed));
|
|
handleEntityCollision(entity);
|
|
|
|
circle.radius = entity->range;
|
|
if (collPhysCircle(&player->phys, &circle)) {
|
|
entity->mode = E_ATTACK_PLAYER;
|
|
setAnimSet(&entity->animation, "attack");
|
|
setAnimFrame(&entity->animation, 0);
|
|
}
|
|
break;
|
|
case E_ATTACK_PLAYER:
|
|
circle.radius = entity->range;
|
|
addDamage(entity->damage);
|
|
if (!collPhysCircle(&player->phys, &circle)) {
|
|
entity->mode = E_MOVE_PLAYER;
|
|
setAnimSet(&entity->animation, "walk");
|
|
}
|
|
break;
|
|
}
|
|
circle.radius = entity->sight*2.0f;
|
|
if (!collPhysCircle(&player->phys, &circle)) {
|
|
entity->target = NULL;
|
|
entity->mode = E_FIND_PLAYER;
|
|
setAnimSet(&entity->animation, "idle");
|
|
}
|
|
}
|
|
|
|
void handleEntityCollision(struct Entity *entity) {
|
|
entity->phys.position.x += entity->phys.velocity.x;
|
|
int test_x, test_y;
|
|
test_x = (int)entity->phys.position.x/g_tile_w;
|
|
test_y = (int)entity->phys.position.y/g_tile_h;
|
|
if (test_x >= 0 && test_x < live_map->width && test_y >= 0 && test_y < live_map->height) {
|
|
struct Circle cell = { test_x*g_tile_w+(g_tile_w/2), test_y*g_tile_h+(g_tile_h/2), (g_tile_w + g_tile_h) };
|
|
if (collPhysCircle(&entity->phys, &cell)) {
|
|
if (live_map->cells[test_x][test_y].flags & CELL_BLOCKS_ENTITY) {
|
|
entity->phys.position.x -= entity->phys.velocity.x;
|
|
}
|
|
}
|
|
}
|
|
entity->phys.position.y += entity->phys.velocity.y;
|
|
test_x = (int)entity->phys.position.x/g_tile_w;
|
|
test_y = (int)entity->phys.position.y/g_tile_h;
|
|
if (test_x >= 0 && test_x < live_map->width && test_y >= 0 && test_y < live_map->height) {
|
|
struct Circle cell = { test_x*g_tile_w+(g_tile_w/2), test_y*g_tile_h+(g_tile_h/2), (g_tile_w + g_tile_h) };
|
|
if (collPhysCircle(&entity->phys, &cell)) {
|
|
if (live_map->cells[test_x][test_y].flags & CELL_BLOCKS_ENTITY) {
|
|
entity->phys.position.y -= entity->phys.velocity.y;
|
|
|
|
}
|
|
}
|
|
}
|
|
entity->phys.acceleration = mulVector(entity->phys.acceleration, 0.5);
|
|
entity->phys.velocity = addVector(entity->phys.velocity, entity->phys.acceleration);
|
|
cleanVector(&entity->phys.acceleration);
|
|
}
|
|
|
|
// initial spawn
|
|
void spawnMetaBits() {
|
|
int i;
|
|
metabit_status = METABIT_STATUS_HOME;
|
|
for (i = 0; i < pl_data.metabits; i++) {
|
|
spawnMetaBit(player->phys.position.x, player->phys.position.y);
|
|
}
|
|
}
|
|
|
|
void processTriggers() {
|
|
int trigger_i = -1;
|
|
while ((trigger_i = iterObjecti(live_map->active_triggers)) != -1) {
|
|
struct Trigger *trigger = live_map->active_triggers->object[trigger_i];
|
|
if (trigger == NULL) continue;
|
|
switch(trigger->behavior) {
|
|
case T_BEHAVE_NORM:
|
|
if (trigger->elapsed >= trigger->time) {
|
|
trigger->elapsed = 0;
|
|
if (trigger->event_runs < trigger->event_count) {
|
|
activateEvent(trigger->events[trigger->event_runs]);
|
|
trigger->event_runs++;
|
|
} else {
|
|
// we're done, let's shed this mortal coil
|
|
freeTrigger(trigger);
|
|
delObject(live_map->active_triggers, trigger_i);
|
|
}
|
|
} else {
|
|
trigger->elapsed += g_tickrate.m;
|
|
}
|
|
break;
|
|
case T_BEHAVE_LOOP:
|
|
if (trigger->elapsed <= trigger->time) {
|
|
// TODO:
|
|
// Do logic here
|
|
if (trigger->iter != 0) trigger->runs++;
|
|
}
|
|
trigger->elapsed -= g_tickrate.m;
|
|
if (trigger->iter != 0) {
|
|
if (trigger->runs >= trigger->iter) {
|
|
remObject(live_map->active_triggers, trigger_i);
|
|
addObject(live_map->triggers, trigger);
|
|
trigger->runs = 0;
|
|
trigger->elapsed = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|