#include "game/GameState.h" #include "game/Level.h" #include #include #include GameState * GameState_create(void (* levelCompleteCallback)(void), void (* diedCallback)(void)) { GameState * self; self = malloc(sizeof(GameState)); GameState_init(self, levelCompleteCallback, diedCallback); return self; } void GameState_init(GameState * self, void (* levelCompleteCallback)(void), void (* diedCallback)(void)) { self->space = NULL; self->staticBody = NULL; self->wallSegmentCount = 0; self->wallSegments = NULL; self->player = NULL; self->playerShape = NULL; self->stuck = false; self->anchorCount = 0; self->anchors = NULL; self->levelCompleteCallback = levelCompleteCallback; self->diedCallback = diedCallback; } void GameState_disposeSpace(GameState * self) { unsigned int segmentIndex; for (segmentIndex = 0; segmentIndex < self->wallSegmentCount; segmentIndex++) { cpShapeFree(self->wallSegments[segmentIndex]); } self->wallSegmentCount = 0; free(self->wallSegments); if (self->stuck) { self->stuck = false; } self->anchorCount = 0; free(self->anchors); self->anchors = NULL; cpBodyFree(self->player); cpShapeFree(self->playerShape); cpBodyFree(self->staticBody); cpSpaceFree(self->space); } void GameState_dispose(GameState * self) { GameState_disposeSpace(self); free(self); } int stickyWallCollisionCallback(cpShape * a, cpShape * b, cpContact * contacts, int numContacts, cpFloat normal_coef, void * data) { GameState * self; self = data; if (!self->stuck) { self->stuck = true; self->anchors = realloc(self->anchors, sizeof(cpVect) * self->anchorCount + 1); self->anchors[self->anchorCount++] = contacts[0].p; self->player->v = cpvzero; } return 1; } int exitCollisionCallback(cpShape * a, cpShape * b, cpContact * contacts, int numContacts, cpFloat normal_coef, void * data) { GameState * self; self = data; if (self->levelCompleteCallback != NULL) { self->levelCompleteCallback(); } return 1; } int deadlyWallCollisionCallback(cpShape * a, cpShape * b, cpContact * contacts, int numContacts, cpFloat normal_coef, void * data) { GameState * self; self = data; if (self->diedCallback != NULL) { self->diedCallback(); } return 1; } #define PLAYER_COLLISION_TYPE 9999 void GameState_loadLevel(GameState * self, Level * level) { unsigned int wallIndex; unsigned int vertexIndex; unsigned int segmentIndex; Vector2 vertex1, vertex2; if (self->space != NULL) { GameState_disposeSpace(self); } self->wallSegments = NULL; self->space = cpSpaceNew(); self->space->gravity = cpv(0.0f, -15.0f); self->space->elasticIterations = 10; self->staticBody = cpBodyNew(INFINITY, INFINITY); for (wallIndex = 0; wallIndex < level->wallCount; wallIndex++) { self->wallSegmentCount += level->walls[wallIndex].vertexCount - 1; } self->wallSegments = malloc(sizeof(cpShape *) * self->wallSegmentCount); segmentIndex = 0; for (wallIndex = 0; wallIndex < level->wallCount; wallIndex++) { for (vertexIndex = 0; vertexIndex < level->walls[wallIndex].vertexCount - 1; vertexIndex++) { vertex1 = level->walls[wallIndex].vertices[vertexIndex]; vertex2 = level->walls[wallIndex].vertices[vertexIndex + 1]; self->wallSegments[segmentIndex] = cpSegmentShapeNew(self->staticBody, cpv(vertex1.x, vertex1.y), cpv(vertex2.x, vertex2.y), 0.0f); self->wallSegments[segmentIndex]->u = 10.0f; self->wallSegments[segmentIndex]->collision_type = level->walls[wallIndex].type; cpSpaceAddStaticShape(self->space, self->wallSegments[segmentIndex]); switch (level->walls[wallIndex].type) { case WALL_TYPE_STICKY: cpSpaceAddCollisionPairFunc(self->space, PLAYER_COLLISION_TYPE, WALL_TYPE_STICKY, stickyWallCollisionCallback, self); break; case WALL_TYPE_EXIT: cpSpaceAddCollisionPairFunc(self->space, PLAYER_COLLISION_TYPE, WALL_TYPE_EXIT, exitCollisionCallback, self); break; case WALL_TYPE_BOUNCY: self->wallSegments[segmentIndex]->e = 0.99f; break; case WALL_TYPE_DEADLY: cpSpaceAddCollisionPairFunc(self->space, PLAYER_COLLISION_TYPE, WALL_TYPE_DEADLY, deadlyWallCollisionCallback, self); break; } segmentIndex++; } } self->playerRadius = level->radius; self->player = cpBodyNew(1.0f, cpMomentForCircle(1.0f, self->playerRadius, self->playerRadius, cpvzero)); self->player->p = cpv(level->startPosition.x, level->startPosition.y); cpSpaceAddBody(self->space, self->player); self->playerShape = cpCircleShapeNew(self->player, self->playerRadius, cpvzero); self->playerShape->u = 150.0f; self->playerShape->e = 0.99f; self->playerShape->collision_type = PLAYER_COLLISION_TYPE; cpSpaceAddShape(self->space, self->playerShape); } void GameState_launchPlayer(GameState * self, float forceX, float forceY) { if (self->stuck) { self->stuck = false; self->player->v.x = forceX; self->player->v.y = forceY; } } void GameState_step(GameState * self, float interval) { unsigned int anchorIndex; cpBodyResetForces(self->player); if (!self->stuck) { float anchorStrength; float distance; for (anchorIndex = 0; anchorIndex < self->anchorCount; anchorIndex++) { distance = sqrt(((self->player->p.x - self->anchors[anchorIndex].x) * (self->player->p.x - self->anchors[anchorIndex].x)) + ((self->player->p.y - self->anchors[anchorIndex].y) * (self->player->p.y - self->anchors[anchorIndex].y))); if (distance < 1.0f) { distance = 1.0f; } else if (distance > 10.0f) { unsigned int anchorIndex2; self->anchorCount = 0; for (anchorIndex2 = anchorIndex; anchorIndex2 < self->anchorCount; anchorIndex2++) { self->anchors[anchorIndex2] = self->anchors[anchorIndex2 + 1]; } anchorIndex--; continue; } anchorStrength = 25 / distance; cpDampedSpring(self->staticBody, self->player, self->anchors[anchorIndex], cpvzero, 0.0f, anchorStrength, 1, interval); } } else { self->player->v = cpvzero; } cpSpaceStep(self->space, interval); }