mirror of
https://gitlab.com/drummyfish/anarch.git
synced 2024-08-13 15:53:48 -04:00
Continue game states
This commit is contained in:
parent
b4f7efacfa
commit
ad60208c3f
34
main.c
34
main.c
@ -37,8 +37,9 @@
|
||||
#define SFG_KEY_TOGGLE_FREELOOK 11
|
||||
#define SFG_KEY_NEXT_WEAPON 12
|
||||
#define SFG_KEY_PREVIOUS_WEAPON 13
|
||||
#define SFG_KEY_MENU 14
|
||||
|
||||
#define SFG_KEY_COUNT 14 ///< Number of keys.
|
||||
#define SFG_KEY_COUNT 15 ///< Number of keys.
|
||||
|
||||
/* ============================= PORTING =================================== */
|
||||
|
||||
@ -223,6 +224,7 @@ typedef struct
|
||||
#define SFG_GAME_STATE_LOSE 3
|
||||
#define SFG_GAME_STATE_INTRO 4
|
||||
#define SFG_GAME_STATE_OUTRO 5
|
||||
#define SFG_GAME_STATE_MAP 6
|
||||
|
||||
#define SFG_MENU_ITEM_CONTINUE 0
|
||||
#define SFG_MENU_ITEM_MAP 1
|
||||
@ -2283,6 +2285,14 @@ void SFG_updateLevel()
|
||||
*/
|
||||
void SFG_gameStepPlaying()
|
||||
{
|
||||
if (
|
||||
(SFG_keyIsDown(SFG_KEY_C) && SFG_keyIsDown(SFG_KEY_DOWN)) ||
|
||||
SFG_keyIsDown(SFG_KEY_MENU))
|
||||
{
|
||||
SFG_setGameState(SFG_GAME_STATE_MENU);
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t recomputeDirection = 0;
|
||||
|
||||
RCL_Vector2D moveOffset;
|
||||
@ -2825,8 +2835,10 @@ void SFG_gameStepPlaying()
|
||||
|
||||
uint8_t SFG_getMenuItem(uint8_t index)
|
||||
{
|
||||
if (index <= SFG_MENU_ITEM_EXIT)
|
||||
return index;
|
||||
uint8_t start = (SFG_currentLevel.levelPointer == 0) ? 2 : 0;
|
||||
|
||||
if (index <= (SFG_MENU_ITEM_EXIT - start))
|
||||
return start + index;
|
||||
|
||||
return SFG_MENU_ITEM_NONE;
|
||||
}
|
||||
@ -2860,6 +2872,14 @@ void SFG_gameStepMenu()
|
||||
SFG_setGameState(SFG_GAME_STATE_PLAYING);
|
||||
break;
|
||||
|
||||
case SFG_MENU_ITEM_CONTINUE:
|
||||
SFG_setGameState(SFG_GAME_STATE_PLAYING);
|
||||
break;
|
||||
|
||||
case SFG_MENU_ITEM_MAP:
|
||||
SFG_setGameState(SFG_GAME_STATE_MAP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2931,6 +2951,12 @@ void SFG_gameStep()
|
||||
break;
|
||||
}
|
||||
|
||||
case SFG_GAME_STATE_MAP:
|
||||
if (SFG_keyIsDown(SFG_KEY_B))
|
||||
SFG_setGameState(SFG_GAME_STATE_MENU);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -3292,7 +3318,7 @@ void SFG_draw()
|
||||
return;
|
||||
}
|
||||
|
||||
if (SFG_keyPressed(SFG_KEY_MAP))
|
||||
if (SFG_keyPressed(SFG_KEY_MAP) || (SFG_game.state == SFG_GAME_STATE_MAP))
|
||||
{
|
||||
SFG_drawMap();
|
||||
}
|
||||
|
@ -145,6 +145,10 @@ int8_t SFG_keyPressed(uint8_t key)
|
||||
return sdlKeyboardState[SDL_SCANCODE_N];
|
||||
break;
|
||||
|
||||
case SFG_KEY_MENU:
|
||||
return sdlKeyboardState[SDL_SCANCODE_X];
|
||||
break;
|
||||
|
||||
default: return 0; break;
|
||||
}
|
||||
}
|
||||
|
737
pokitto/main.cpp
737
pokitto/main.cpp
@ -37,8 +37,9 @@
|
||||
#define SFG_KEY_TOGGLE_FREELOOK 11
|
||||
#define SFG_KEY_NEXT_WEAPON 12
|
||||
#define SFG_KEY_PREVIOUS_WEAPON 13
|
||||
#define SFG_KEY_MENU 14
|
||||
|
||||
#define SFG_KEY_COUNT 14 ///< Number of keys.
|
||||
#define SFG_KEY_COUNT 15 ///< Number of keys.
|
||||
|
||||
/* ============================= PORTING =================================== */
|
||||
|
||||
@ -223,6 +224,7 @@ typedef struct
|
||||
#define SFG_GAME_STATE_LOSE 3
|
||||
#define SFG_GAME_STATE_INTRO 4
|
||||
#define SFG_GAME_STATE_OUTRO 5
|
||||
#define SFG_GAME_STATE_MAP 6
|
||||
|
||||
#define SFG_MENU_ITEM_CONTINUE 0
|
||||
#define SFG_MENU_ITEM_MAP 1
|
||||
@ -621,7 +623,8 @@ void SFG_pixelFunc(RCL_PixelInfo *pixel)
|
||||
uint8_t zValue = pixel->isWall ? SFG_RCLUnitToZBuffer(pixel->depth) : 255;
|
||||
|
||||
for (uint8_t i = 0; i < SFG_RAYCASTING_SUBSAMPLE; ++i)
|
||||
SFG_game.zBuffer[pixel->position.x * SFG_RAYCASTING_SUBSAMPLE + i] = zValue;
|
||||
SFG_game.zBuffer[pixel->position.x * SFG_RAYCASTING_SUBSAMPLE + i] =
|
||||
zValue;
|
||||
}
|
||||
|
||||
if (pixel->isHorizon && pixel->depth > RCL_UNITS_PER_SQUARE * 16)
|
||||
@ -1205,8 +1208,6 @@ void SFG_init()
|
||||
{
|
||||
SFG_LOG("initializing game")
|
||||
|
||||
SFG_setGameState(SFG_GAME_STATE_MENU);
|
||||
|
||||
SFG_game.frame = 0;
|
||||
SFG_game.currentRandom = 0;
|
||||
|
||||
@ -1227,6 +1228,13 @@ void SFG_init()
|
||||
SFG_game.selectedMenuItem = 0;
|
||||
SFG_game.selectedLevel = 0;
|
||||
SFG_player.freeLook = 0;
|
||||
|
||||
#if SFG_START_LEVEL == 0
|
||||
SFG_setGameState(SFG_GAME_STATE_MENU);
|
||||
#else
|
||||
SFG_setAndInitLevel(&SFG_levels[SFG_START_LEVEL - 1]);
|
||||
SFG_setGameState(SFG_GAME_STATE_PLAYING);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SFG_getPlayerWeaponInfo(
|
||||
@ -1952,11 +1960,339 @@ void SFG_getLevelElementSprite(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Updates a frame of the currently loaded level, i.e. enemies, projectiles,
|
||||
aimations etc., with the exception of player.
|
||||
*/
|
||||
void SFG_updateLevel()
|
||||
{
|
||||
// update projectiles:
|
||||
|
||||
uint8_t substractFrames =
|
||||
(SFG_game.frame - SFG_currentLevel.frameStart) & 0x01 ? 1 : 0;
|
||||
// ^ only substract frames to live every other frame
|
||||
|
||||
for (int8_t i = 0; i < SFG_currentLevel.projectileRecordCount; ++i)
|
||||
{ // ^ has to be signed
|
||||
SFG_ProjectileRecord *p = &(SFG_currentLevel.projectileRecords[i]);
|
||||
|
||||
uint8_t attackType = 255;
|
||||
|
||||
if (p->type == SFG_PROJECTILE_BULLET)
|
||||
attackType = SFG_WEAPON_FIRE_TYPE_BULLET;
|
||||
else if (p->type == SFG_PROJECTILE_PLASMA)
|
||||
attackType = SFG_WEAPON_FIRE_TYPE_PLASMA;
|
||||
|
||||
RCL_Unit pos[3]; // we have to convert from uint16_t because under/overflows
|
||||
|
||||
uint8_t eliminate = 0;
|
||||
|
||||
for (uint8_t j = 0; j < 3; ++j)
|
||||
{
|
||||
pos[j] = p->position[j];
|
||||
pos[j] += p->direction[j];
|
||||
|
||||
if ( // projectile outside map?
|
||||
(pos[j] < 0) ||
|
||||
(pos[j] >= (SFG_MAP_SIZE * RCL_UNITS_PER_SQUARE)))
|
||||
{
|
||||
eliminate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->doubleFramesToLive == 0) // no more time to live?
|
||||
{
|
||||
eliminate = 1;
|
||||
}
|
||||
else if (
|
||||
(p->type != SFG_PROJECTILE_EXPLOSION) &&
|
||||
(p->type != SFG_PROJECTILE_DUST))
|
||||
{
|
||||
if (SFG_projectileCollides( // collides with player?
|
||||
p,
|
||||
SFG_player.camera.position.x,
|
||||
SFG_player.camera.position.y,
|
||||
SFG_player.camera.height))
|
||||
{
|
||||
eliminate = 1;
|
||||
SFG_playerChangeHealth(-1 * SFG_getDamageValue(attackType));
|
||||
}
|
||||
|
||||
// check collision with the map
|
||||
|
||||
if (!eliminate &&
|
||||
((SFG_floorHeightAt(pos[0] / RCL_UNITS_PER_SQUARE,pos[1] /
|
||||
RCL_UNITS_PER_SQUARE) >= pos[2])
|
||||
||
|
||||
(SFG_ceilingHeightAt(pos[0] / RCL_UNITS_PER_SQUARE,pos[1] /
|
||||
RCL_UNITS_PER_SQUARE) <= pos[2]))
|
||||
)
|
||||
eliminate = 1;
|
||||
|
||||
// check collision with active level elements
|
||||
|
||||
if (!eliminate) // monsters
|
||||
for (uint16_t j = 0; j < SFG_currentLevel.monsterRecordCount; ++j)
|
||||
{
|
||||
SFG_MonsterRecord *m = &(SFG_currentLevel.monsterRecords[j]);
|
||||
|
||||
if (SFG_MR_STATE(*m) != SFG_MONSTER_STATE_INACTIVE)
|
||||
{
|
||||
if (SFG_projectileCollides(p,
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(m->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(m->coords[1]),
|
||||
SFG_floorHeightAt(
|
||||
SFG_MONSTER_COORD_TO_SQUARES(m->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_SQUARES(m->coords[1]))
|
||||
))
|
||||
{
|
||||
eliminate = 1;
|
||||
SFG_monsterChangeHealth(m,-1 * SFG_getDamageValue(attackType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!eliminate) // items
|
||||
for (uint16_t j = 0; j < SFG_currentLevel.itemRecordCount; ++j)
|
||||
{
|
||||
const SFG_LevelElement *e = SFG_getActiveItemElement(j);
|
||||
|
||||
if (e != 0)
|
||||
{
|
||||
RCL_Unit x = SFG_ELEMENT_COORD_TO_RCL_UNITS(e->coords[0]);
|
||||
RCL_Unit y = SFG_ELEMENT_COORD_TO_RCL_UNITS(e->coords[1]);
|
||||
RCL_Unit z = SFG_floorHeightAt(e->coords[0],e->coords[1]);
|
||||
|
||||
if (SFG_projectileCollides(p,x,y,z))
|
||||
{
|
||||
if (
|
||||
(e->type == SFG_LEVEL_ELEMENT_BARREL) &&
|
||||
(SFG_getDamageValue(attackType) >=
|
||||
SFG_BARREL_EXPLOSION_DAMAGE_THRESHOLD)
|
||||
)
|
||||
{
|
||||
SFG_explodeBarrel(j,x,y,z);
|
||||
}
|
||||
|
||||
eliminate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eliminate)
|
||||
{
|
||||
if (p->type == SFG_PROJECTILE_FIREBALL)
|
||||
SFG_createExplosion(p->position[0],p->position[1],p->position[2]);
|
||||
else if (p->type == SFG_PROJECTILE_BULLET)
|
||||
SFG_createDust(p->position[0],p->position[1],p->position[2]);
|
||||
else if (p->type == SFG_PROJECTILE_PLASMA)
|
||||
SFG_playSoundSafe(4,SFG_distantSoundVolume(pos[0],pos[1],pos[2]));
|
||||
|
||||
// remove the projectile
|
||||
|
||||
for (uint8_t j = i; j < SFG_currentLevel.projectileRecordCount - 1; ++j)
|
||||
SFG_currentLevel.projectileRecords[j] =
|
||||
SFG_currentLevel.projectileRecords[j + 1];
|
||||
|
||||
SFG_currentLevel.projectileRecordCount--;
|
||||
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->position[0] = pos[0];
|
||||
p->position[1] = pos[1];
|
||||
p->position[2] = pos[2];
|
||||
}
|
||||
|
||||
p->doubleFramesToLive -= substractFrames;
|
||||
}
|
||||
|
||||
// handle door:
|
||||
if (SFG_currentLevel.doorRecordCount > 0) // has to be here
|
||||
{
|
||||
/* Check one door on whether a player is standing nearby. For performance
|
||||
reasons we only check a few doors and move to others in the next
|
||||
frame. */
|
||||
|
||||
for (uint16_t i = 0;
|
||||
i < RCL_min(SFG_ELEMENT_DISTANCES_CHECKED_PER_FRAME,
|
||||
SFG_currentLevel.doorRecordCount);
|
||||
++i)
|
||||
{
|
||||
SFG_DoorRecord *door =
|
||||
&(SFG_currentLevel.doorRecords[SFG_currentLevel.checkedDoorIndex]);
|
||||
|
||||
uint8_t upDownState = door->state & SFG_DOOR_UP_DOWN_MASK;
|
||||
|
||||
uint8_t lock = SFG_DOOR_LOCK(door->state);
|
||||
|
||||
uint8_t newUpDownState =
|
||||
(
|
||||
((lock == 0) || (SFG_player.cards & (1 << (lock - 1)))) &&
|
||||
(door->coords[0] >= (SFG_player.squarePosition[0] - 1)) &&
|
||||
(door->coords[0] <= (SFG_player.squarePosition[0] + 1)) &&
|
||||
(door->coords[1] >= (SFG_player.squarePosition[1] - 1)) &&
|
||||
(door->coords[1] <= (SFG_player.squarePosition[1] + 1))
|
||||
) ? SFG_DOOR_UP_DOWN_MASK : 0x00;
|
||||
|
||||
if (upDownState != newUpDownState)
|
||||
SFG_playSoundSafe(1,255);
|
||||
|
||||
door->state = (door->state & ~SFG_DOOR_UP_DOWN_MASK) | newUpDownState;
|
||||
|
||||
SFG_currentLevel.checkedDoorIndex++;
|
||||
|
||||
if (SFG_currentLevel.checkedDoorIndex >= SFG_currentLevel.doorRecordCount)
|
||||
SFG_currentLevel.checkedDoorIndex = 0;
|
||||
}
|
||||
|
||||
// move door up/down:
|
||||
for (uint32_t i = 0; i < SFG_currentLevel.doorRecordCount; ++i)
|
||||
{
|
||||
SFG_DoorRecord *door = &(SFG_currentLevel.doorRecords[i]);
|
||||
|
||||
int8_t height = door->state & SFG_DOOR_VERTICAL_POSITION_MASK;
|
||||
|
||||
height = (door->state & SFG_DOOR_UP_DOWN_MASK) ?
|
||||
RCL_min(0x1f,height + SFG_DOOR_INCREMENT_PER_FRAME) :
|
||||
RCL_max(0x00,height - SFG_DOOR_INCREMENT_PER_FRAME);
|
||||
|
||||
door->state = (door->state & ~SFG_DOOR_VERTICAL_POSITION_MASK) | height;
|
||||
}
|
||||
}
|
||||
|
||||
// handle items, in a similar manner to door:
|
||||
if (SFG_currentLevel.itemRecordCount > 0) // has to be here
|
||||
{
|
||||
// check item distances:
|
||||
|
||||
for (uint16_t i = 0;
|
||||
i < RCL_min(SFG_ELEMENT_DISTANCES_CHECKED_PER_FRAME,
|
||||
SFG_currentLevel.itemRecordCount);
|
||||
++i)
|
||||
{
|
||||
SFG_ItemRecord item =
|
||||
SFG_currentLevel.itemRecords[SFG_currentLevel.checkedItemIndex];
|
||||
|
||||
item &= ~SFG_ITEM_RECORD_ACTIVE_MASK;
|
||||
|
||||
SFG_LevelElement e =
|
||||
SFG_currentLevel.levelPointer->elements[item];
|
||||
|
||||
if (
|
||||
SFG_isInActiveDistanceFromPlayer(
|
||||
e.coords[0] * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2,
|
||||
e.coords[1] * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2,
|
||||
SFG_floorHeightAt(e.coords[0],e.coords[1]) + RCL_UNITS_PER_SQUARE / 2)
|
||||
)
|
||||
item |= SFG_ITEM_RECORD_ACTIVE_MASK;
|
||||
|
||||
SFG_currentLevel.itemRecords[SFG_currentLevel.checkedItemIndex] = item;
|
||||
|
||||
SFG_currentLevel.checkedItemIndex++;
|
||||
|
||||
if (SFG_currentLevel.checkedItemIndex >= SFG_currentLevel.itemRecordCount)
|
||||
SFG_currentLevel.checkedItemIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// similarly handle monsters:
|
||||
if (SFG_currentLevel.monsterRecordCount > 0) // has to be here
|
||||
{
|
||||
// check monster distances:
|
||||
|
||||
for (uint16_t i = 0;
|
||||
i < RCL_min(SFG_ELEMENT_DISTANCES_CHECKED_PER_FRAME,
|
||||
SFG_currentLevel.monsterRecordCount);
|
||||
++i)
|
||||
{
|
||||
SFG_MonsterRecord *monster =
|
||||
&(SFG_currentLevel.monsterRecords[SFG_currentLevel.checkedMonsterIndex]);
|
||||
|
||||
if ( // far away from the player?
|
||||
!SFG_isInActiveDistanceFromPlayer(
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[1]),
|
||||
SFG_floorHeightAt(
|
||||
SFG_MONSTER_COORD_TO_SQUARES(monster->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_SQUARES(monster->coords[1]))
|
||||
+ RCL_UNITS_PER_SQUARE / 2
|
||||
)
|
||||
)
|
||||
{
|
||||
monster->stateType =
|
||||
(monster->stateType & SFG_MONSTER_MASK_TYPE) |
|
||||
SFG_MONSTER_STATE_INACTIVE;
|
||||
}
|
||||
else if (SFG_MR_STATE(*monster) == SFG_MONSTER_STATE_INACTIVE)
|
||||
{
|
||||
monster->stateType =
|
||||
(monster->stateType & SFG_MONSTER_MASK_TYPE) |
|
||||
SFG_MONSTER_STATE_IDLE;
|
||||
}
|
||||
|
||||
SFG_currentLevel.checkedMonsterIndex++;
|
||||
|
||||
if (SFG_currentLevel.checkedMonsterIndex >=
|
||||
SFG_currentLevel.monsterRecordCount)
|
||||
SFG_currentLevel.checkedMonsterIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// update AI and handle dead monsters:
|
||||
if ((SFG_game.frame - SFG_currentLevel.frameStart) %
|
||||
SFG_AI_UPDATE_FRAME_INTERVAL == 0)
|
||||
{
|
||||
for (uint16_t i = 0; i < SFG_currentLevel.monsterRecordCount; ++i)
|
||||
{
|
||||
SFG_MonsterRecord *monster = &(SFG_currentLevel.monsterRecords[i]);
|
||||
uint8_t state = SFG_MR_STATE(*monster);
|
||||
|
||||
if (state == SFG_MONSTER_STATE_DYING)
|
||||
{
|
||||
// remove dead
|
||||
|
||||
for (uint16_t j = i; j < SFG_currentLevel.monsterRecordCount - 1; ++j)
|
||||
SFG_currentLevel.monsterRecords[j] =
|
||||
SFG_currentLevel.monsterRecords[j + 1];
|
||||
|
||||
SFG_currentLevel.monsterRecordCount -= 1;
|
||||
|
||||
i--;
|
||||
}
|
||||
else if (monster->health == 0)
|
||||
{
|
||||
monster->stateType = SFG_MR_TYPE(*monster) | SFG_MONSTER_STATE_DYING;
|
||||
SFG_playSoundSafe(2,255);
|
||||
}
|
||||
else if (state != SFG_MONSTER_STATE_INACTIVE)
|
||||
{
|
||||
#if SFG_PREVIEW_MODE == 0
|
||||
SFG_monsterPerformAI(monster);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Part of SFG_gameStep() for SFG_GAME_STATE_PLAYING.
|
||||
*/
|
||||
void SFG_gameStepPlaying()
|
||||
{
|
||||
if (
|
||||
(SFG_keyIsDown(SFG_KEY_C) && SFG_keyIsDown(SFG_KEY_DOWN)) ||
|
||||
SFG_keyIsDown(SFG_KEY_MENU))
|
||||
{
|
||||
SFG_setGameState(SFG_GAME_STATE_MENU);
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t recomputeDirection = 0;
|
||||
|
||||
RCL_Vector2D moveOffset;
|
||||
@ -2486,324 +2822,23 @@ void SFG_gameStepPlaying()
|
||||
SFG_player.squarePosition[1] =
|
||||
SFG_player.camera.position.y / RCL_UNITS_PER_SQUARE;
|
||||
|
||||
// update projectiles:
|
||||
SFG_updateLevel();
|
||||
|
||||
uint8_t substractFrames =
|
||||
(SFG_game.frame - SFG_currentLevel.frameStart) & 0x01 ? 1 : 0;
|
||||
// ^ only substract frames to live every other frame
|
||||
|
||||
for (int8_t i = 0; i < SFG_currentLevel.projectileRecordCount; ++i)
|
||||
{ // ^ has to be signed
|
||||
SFG_ProjectileRecord *p = &(SFG_currentLevel.projectileRecords[i]);
|
||||
|
||||
uint8_t attackType = 255;
|
||||
|
||||
if (p->type == SFG_PROJECTILE_BULLET)
|
||||
attackType = SFG_WEAPON_FIRE_TYPE_BULLET;
|
||||
else if (p->type == SFG_PROJECTILE_PLASMA)
|
||||
attackType = SFG_WEAPON_FIRE_TYPE_PLASMA;
|
||||
|
||||
RCL_Unit pos[3]; // we have to convert from uint16_t because under/overflows
|
||||
|
||||
uint8_t eliminate = 0;
|
||||
|
||||
for (uint8_t j = 0; j < 3; ++j)
|
||||
{
|
||||
pos[j] = p->position[j];
|
||||
pos[j] += p->direction[j];
|
||||
|
||||
if ( // projectile outside map?
|
||||
(pos[j] < 0) ||
|
||||
(pos[j] >= (SFG_MAP_SIZE * RCL_UNITS_PER_SQUARE)))
|
||||
{
|
||||
eliminate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->doubleFramesToLive == 0) // no more time to live?
|
||||
{
|
||||
eliminate = 1;
|
||||
}
|
||||
else if (
|
||||
(p->type != SFG_PROJECTILE_EXPLOSION) &&
|
||||
(p->type != SFG_PROJECTILE_DUST))
|
||||
{
|
||||
if (SFG_projectileCollides( // collides with player?
|
||||
p,
|
||||
SFG_player.camera.position.x,
|
||||
SFG_player.camera.position.y,
|
||||
SFG_player.camera.height))
|
||||
{
|
||||
eliminate = 1;
|
||||
SFG_playerChangeHealth(-1 * SFG_getDamageValue(attackType));
|
||||
}
|
||||
|
||||
// check collision with the map
|
||||
|
||||
if (!eliminate &&
|
||||
((SFG_floorHeightAt(pos[0] / RCL_UNITS_PER_SQUARE,pos[1] /
|
||||
RCL_UNITS_PER_SQUARE) >= pos[2])
|
||||
||
|
||||
(SFG_ceilingHeightAt(pos[0] / RCL_UNITS_PER_SQUARE,pos[1] /
|
||||
RCL_UNITS_PER_SQUARE) <= pos[2]))
|
||||
)
|
||||
eliminate = 1;
|
||||
|
||||
// check collision with active level elements
|
||||
|
||||
if (!eliminate) // monsters
|
||||
for (uint16_t j = 0; j < SFG_currentLevel.monsterRecordCount; ++j)
|
||||
{
|
||||
SFG_MonsterRecord *m = &(SFG_currentLevel.monsterRecords[j]);
|
||||
|
||||
if (SFG_MR_STATE(*m) != SFG_MONSTER_STATE_INACTIVE)
|
||||
{
|
||||
if (SFG_projectileCollides(p,
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(m->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(m->coords[1]),
|
||||
SFG_floorHeightAt(
|
||||
SFG_MONSTER_COORD_TO_SQUARES(m->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_SQUARES(m->coords[1]))
|
||||
))
|
||||
{
|
||||
eliminate = 1;
|
||||
SFG_monsterChangeHealth(m,-1 * SFG_getDamageValue(attackType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!eliminate) // items
|
||||
for (uint16_t j = 0; j < SFG_currentLevel.itemRecordCount; ++j)
|
||||
{
|
||||
const SFG_LevelElement *e = SFG_getActiveItemElement(j);
|
||||
|
||||
if (e != 0)
|
||||
{
|
||||
RCL_Unit x = SFG_ELEMENT_COORD_TO_RCL_UNITS(e->coords[0]);
|
||||
RCL_Unit y = SFG_ELEMENT_COORD_TO_RCL_UNITS(e->coords[1]);
|
||||
RCL_Unit z = SFG_floorHeightAt(e->coords[0],e->coords[1]);
|
||||
|
||||
if (SFG_projectileCollides(p,x,y,z))
|
||||
{
|
||||
if (
|
||||
(e->type == SFG_LEVEL_ELEMENT_BARREL) &&
|
||||
(SFG_getDamageValue(attackType) >=
|
||||
SFG_BARREL_EXPLOSION_DAMAGE_THRESHOLD)
|
||||
)
|
||||
{
|
||||
SFG_explodeBarrel(j,x,y,z);
|
||||
}
|
||||
|
||||
eliminate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eliminate)
|
||||
{
|
||||
if (p->type == SFG_PROJECTILE_FIREBALL)
|
||||
SFG_createExplosion(p->position[0],p->position[1],p->position[2]);
|
||||
else if (p->type == SFG_PROJECTILE_BULLET)
|
||||
SFG_createDust(p->position[0],p->position[1],p->position[2]);
|
||||
else if (p->type == SFG_PROJECTILE_PLASMA)
|
||||
SFG_playSoundSafe(4,SFG_distantSoundVolume(pos[0],pos[1],pos[2]));
|
||||
|
||||
// remove the projectile
|
||||
|
||||
for (uint8_t j = i; j < SFG_currentLevel.projectileRecordCount - 1; ++j)
|
||||
SFG_currentLevel.projectileRecords[j] =
|
||||
SFG_currentLevel.projectileRecords[j + 1];
|
||||
|
||||
SFG_currentLevel.projectileRecordCount--;
|
||||
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->position[0] = pos[0];
|
||||
p->position[1] = pos[1];
|
||||
p->position[2] = pos[2];
|
||||
}
|
||||
|
||||
p->doubleFramesToLive -= substractFrames;
|
||||
}
|
||||
|
||||
// handle door:
|
||||
if (SFG_currentLevel.doorRecordCount > 0) // has to be here
|
||||
#if SFG_IMMORTAL == 0
|
||||
if (SFG_player.health == 0)
|
||||
{
|
||||
/* Check one door on whether a player is standing nearby. For performance
|
||||
reasons we only check a few doors and move to others in the next
|
||||
frame. */
|
||||
|
||||
for (uint16_t i = 0;
|
||||
i < RCL_min(SFG_ELEMENT_DISTANCES_CHECKED_PER_FRAME,
|
||||
SFG_currentLevel.doorRecordCount);
|
||||
++i)
|
||||
{
|
||||
SFG_DoorRecord *door =
|
||||
&(SFG_currentLevel.doorRecords[SFG_currentLevel.checkedDoorIndex]);
|
||||
|
||||
uint8_t upDownState = door->state & SFG_DOOR_UP_DOWN_MASK;
|
||||
|
||||
uint8_t lock = SFG_DOOR_LOCK(door->state);
|
||||
|
||||
uint8_t newUpDownState =
|
||||
(
|
||||
((lock == 0) || (SFG_player.cards & (1 << (lock - 1)))) &&
|
||||
(door->coords[0] >= (SFG_player.squarePosition[0] - 1)) &&
|
||||
(door->coords[0] <= (SFG_player.squarePosition[0] + 1)) &&
|
||||
(door->coords[1] >= (SFG_player.squarePosition[1] - 1)) &&
|
||||
(door->coords[1] <= (SFG_player.squarePosition[1] + 1))
|
||||
) ? SFG_DOOR_UP_DOWN_MASK : 0x00;
|
||||
|
||||
if (upDownState != newUpDownState)
|
||||
SFG_playSoundSafe(1,255);
|
||||
|
||||
door->state = (door->state & ~SFG_DOOR_UP_DOWN_MASK) | newUpDownState;
|
||||
|
||||
SFG_currentLevel.checkedDoorIndex++;
|
||||
|
||||
if (SFG_currentLevel.checkedDoorIndex >= SFG_currentLevel.doorRecordCount)
|
||||
SFG_currentLevel.checkedDoorIndex = 0;
|
||||
}
|
||||
|
||||
// move door up/down:
|
||||
for (uint32_t i = 0; i < SFG_currentLevel.doorRecordCount; ++i)
|
||||
{
|
||||
SFG_DoorRecord *door = &(SFG_currentLevel.doorRecords[i]);
|
||||
|
||||
int8_t height = door->state & SFG_DOOR_VERTICAL_POSITION_MASK;
|
||||
|
||||
height = (door->state & SFG_DOOR_UP_DOWN_MASK) ?
|
||||
RCL_min(0x1f,height + SFG_DOOR_INCREMENT_PER_FRAME) :
|
||||
RCL_max(0x00,height - SFG_DOOR_INCREMENT_PER_FRAME);
|
||||
|
||||
door->state = (door->state & ~SFG_DOOR_VERTICAL_POSITION_MASK) | height;
|
||||
}
|
||||
SFG_LOG("player dies");
|
||||
SFG_setGameState(SFG_GAME_STATE_LOSE);
|
||||
}
|
||||
|
||||
// handle items, in a similar manner to door:
|
||||
if (SFG_currentLevel.itemRecordCount > 0) // has to be here
|
||||
{
|
||||
// check item distances:
|
||||
|
||||
for (uint16_t i = 0;
|
||||
i < RCL_min(SFG_ELEMENT_DISTANCES_CHECKED_PER_FRAME,
|
||||
SFG_currentLevel.itemRecordCount);
|
||||
++i)
|
||||
{
|
||||
SFG_ItemRecord item =
|
||||
SFG_currentLevel.itemRecords[SFG_currentLevel.checkedItemIndex];
|
||||
|
||||
item &= ~SFG_ITEM_RECORD_ACTIVE_MASK;
|
||||
|
||||
SFG_LevelElement e =
|
||||
SFG_currentLevel.levelPointer->elements[item];
|
||||
|
||||
if (
|
||||
SFG_isInActiveDistanceFromPlayer(
|
||||
e.coords[0] * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2,
|
||||
e.coords[1] * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2,
|
||||
SFG_floorHeightAt(e.coords[0],e.coords[1]) + RCL_UNITS_PER_SQUARE / 2)
|
||||
)
|
||||
item |= SFG_ITEM_RECORD_ACTIVE_MASK;
|
||||
|
||||
SFG_currentLevel.itemRecords[SFG_currentLevel.checkedItemIndex] = item;
|
||||
|
||||
SFG_currentLevel.checkedItemIndex++;
|
||||
|
||||
if (SFG_currentLevel.checkedItemIndex >= SFG_currentLevel.itemRecordCount)
|
||||
SFG_currentLevel.checkedItemIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// similarly handle monsters:
|
||||
if (SFG_currentLevel.monsterRecordCount > 0) // has to be here
|
||||
{
|
||||
// check monster distances:
|
||||
|
||||
for (uint16_t i = 0;
|
||||
i < RCL_min(SFG_ELEMENT_DISTANCES_CHECKED_PER_FRAME,
|
||||
SFG_currentLevel.monsterRecordCount);
|
||||
++i)
|
||||
{
|
||||
SFG_MonsterRecord *monster =
|
||||
&(SFG_currentLevel.monsterRecords[SFG_currentLevel.checkedMonsterIndex]);
|
||||
|
||||
if ( // far away from the player?
|
||||
!SFG_isInActiveDistanceFromPlayer(
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[1]),
|
||||
SFG_floorHeightAt(
|
||||
SFG_MONSTER_COORD_TO_SQUARES(monster->coords[0]),
|
||||
SFG_MONSTER_COORD_TO_SQUARES(monster->coords[1]))
|
||||
+ RCL_UNITS_PER_SQUARE / 2
|
||||
)
|
||||
)
|
||||
{
|
||||
monster->stateType =
|
||||
(monster->stateType & SFG_MONSTER_MASK_TYPE) |
|
||||
SFG_MONSTER_STATE_INACTIVE;
|
||||
}
|
||||
else if (SFG_MR_STATE(*monster) == SFG_MONSTER_STATE_INACTIVE)
|
||||
{
|
||||
monster->stateType =
|
||||
(monster->stateType & SFG_MONSTER_MASK_TYPE) |
|
||||
SFG_MONSTER_STATE_IDLE;
|
||||
}
|
||||
|
||||
SFG_currentLevel.checkedMonsterIndex++;
|
||||
|
||||
if (SFG_currentLevel.checkedMonsterIndex >=
|
||||
SFG_currentLevel.monsterRecordCount)
|
||||
SFG_currentLevel.checkedMonsterIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// update AI and handle dead monsters:
|
||||
if ((SFG_game.frame - SFG_currentLevel.frameStart) %
|
||||
SFG_AI_UPDATE_FRAME_INTERVAL == 0)
|
||||
{
|
||||
for (uint16_t i = 0; i < SFG_currentLevel.monsterRecordCount; ++i)
|
||||
{
|
||||
SFG_MonsterRecord *monster = &(SFG_currentLevel.monsterRecords[i]);
|
||||
uint8_t state = SFG_MR_STATE(*monster);
|
||||
|
||||
if (state == SFG_MONSTER_STATE_DYING)
|
||||
{
|
||||
// remove dead
|
||||
|
||||
for (uint16_t j = i; j < SFG_currentLevel.monsterRecordCount - 1; ++j)
|
||||
SFG_currentLevel.monsterRecords[j] =
|
||||
SFG_currentLevel.monsterRecords[j + 1];
|
||||
|
||||
SFG_currentLevel.monsterRecordCount -= 1;
|
||||
|
||||
i--;
|
||||
}
|
||||
else if (monster->health == 0)
|
||||
{
|
||||
monster->stateType = SFG_MR_TYPE(*monster) | SFG_MONSTER_STATE_DYING;
|
||||
SFG_playSoundSafe(2,255);
|
||||
}
|
||||
else if (state != SFG_MONSTER_STATE_INACTIVE)
|
||||
{
|
||||
#if SFG_PREVIEW_MODE == 0
|
||||
SFG_monsterPerformAI(monster);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t SFG_getMenuItem(uint8_t index)
|
||||
{
|
||||
if (index <= SFG_MENU_ITEM_EXIT)
|
||||
return index;
|
||||
uint8_t start = (SFG_currentLevel.levelPointer == 0) ? 2 : 0;
|
||||
|
||||
if (index <= (SFG_MENU_ITEM_EXIT - start))
|
||||
return start + index;
|
||||
|
||||
return SFG_MENU_ITEM_NONE;
|
||||
}
|
||||
@ -2837,6 +2872,14 @@ void SFG_gameStepMenu()
|
||||
SFG_setGameState(SFG_GAME_STATE_PLAYING);
|
||||
break;
|
||||
|
||||
case SFG_MENU_ITEM_CONTINUE:
|
||||
SFG_setGameState(SFG_GAME_STATE_PLAYING);
|
||||
break;
|
||||
|
||||
case SFG_MENU_ITEM_MAP:
|
||||
SFG_setGameState(SFG_GAME_STATE_MAP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2885,6 +2928,35 @@ void SFG_gameStep()
|
||||
SFG_gameStepMenu();
|
||||
break;
|
||||
|
||||
case SFG_GAME_STATE_LOSE:
|
||||
{
|
||||
// player die animation
|
||||
|
||||
SFG_updateLevel();
|
||||
|
||||
int32_t t = SFG_game.frameTime - SFG_game.stateChangeTime;
|
||||
|
||||
RCL_Unit h = SFG_floorHeightAt(
|
||||
SFG_player.squarePosition[0],
|
||||
SFG_player.squarePosition[1]);
|
||||
|
||||
SFG_player.camera.height =
|
||||
RCL_max(h,h + ((SFG_LOSE_ANIMATION_DURATION - t) *
|
||||
RCL_CAMERA_COLL_HEIGHT_BELOW) / SFG_LOSE_ANIMATION_DURATION);
|
||||
|
||||
SFG_player.camera.shear =
|
||||
RCL_min(SFG_CAMERA_MAX_SHEAR_PIXELS / 4,
|
||||
(t * (SFG_CAMERA_MAX_SHEAR_PIXELS / 4)) / SFG_LOSE_ANIMATION_DURATION);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SFG_GAME_STATE_MAP:
|
||||
if (SFG_keyIsDown(SFG_KEY_B))
|
||||
SFG_setGameState(SFG_GAME_STATE_MENU);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -3246,7 +3318,7 @@ void SFG_draw()
|
||||
return;
|
||||
}
|
||||
|
||||
if (SFG_keyPressed(SFG_KEY_MAP))
|
||||
if (SFG_keyPressed(SFG_KEY_MAP) || (SFG_game.state == SFG_GAME_STATE_MAP))
|
||||
{
|
||||
SFG_drawMap();
|
||||
}
|
||||
@ -3255,19 +3327,33 @@ void SFG_draw()
|
||||
for (uint16_t i = 0; i < SFG_Z_BUFFER_SIZE; ++i)
|
||||
SFG_game.zBuffer[i] = 255;
|
||||
|
||||
int16_t weaponBobOffset;
|
||||
int16_t weaponBobOffset = 0;
|
||||
|
||||
#if SFG_HEADBOB_ENABLED
|
||||
RCL_Unit bobSin = RCL_sinInt(SFG_player.headBobFrame);
|
||||
RCL_Unit headBobOffset = 0;
|
||||
|
||||
RCL_Unit headBobOffset =
|
||||
(bobSin * SFG_HEADBOB_OFFSET) / RCL_UNITS_PER_SQUARE;
|
||||
if (SFG_game.state != SFG_GAME_STATE_LOSE)
|
||||
{
|
||||
RCL_Unit bobSin = RCL_sinInt(SFG_player.headBobFrame);
|
||||
|
||||
weaponBobOffset =
|
||||
(bobSin * SFG_WEAPONBOB_OFFSET_PIXELS) / (RCL_UNITS_PER_SQUARE) +
|
||||
SFG_WEAPONBOB_OFFSET_PIXELS;
|
||||
headBobOffset = (bobSin * SFG_HEADBOB_OFFSET) / RCL_UNITS_PER_SQUARE;
|
||||
|
||||
weaponBobOffset =
|
||||
(bobSin * SFG_WEAPONBOB_OFFSET_PIXELS) / (RCL_UNITS_PER_SQUARE) +
|
||||
SFG_WEAPONBOB_OFFSET_PIXELS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// player die animation
|
||||
|
||||
int32_t t = SFG_game.frameTime - SFG_game.stateChangeTime;
|
||||
|
||||
weaponBobOffset = (SFG_WEAPON_IMAGE_SCALE * SFG_TEXTURE_SIZE * t) /
|
||||
SFG_LOSE_ANIMATION_DURATION;
|
||||
}
|
||||
|
||||
// add head bob just for the rendering
|
||||
|
||||
SFG_player.camera.height += headBobOffset;
|
||||
#endif
|
||||
|
||||
@ -3409,7 +3495,7 @@ void SFG_draw()
|
||||
}
|
||||
|
||||
#if SFG_HEADBOB_ENABLED
|
||||
// substract head bob after rendering
|
||||
// after rendering sprites substract back the head bob offset
|
||||
SFG_player.camera.height -= headBobOffset;
|
||||
#endif
|
||||
|
||||
@ -3460,8 +3546,9 @@ void SFG_draw()
|
||||
|
||||
// border indicator
|
||||
|
||||
if (SFG_game.frame - SFG_player.lastHurtFrame
|
||||
<= SFG_HUD_BORDER_INDICATOR_DURATION_FRAMES)
|
||||
if ((SFG_game.frame - SFG_player.lastHurtFrame
|
||||
<= SFG_HUD_BORDER_INDICATOR_DURATION_FRAMES) ||
|
||||
(SFG_game.state == SFG_GAME_STATE_LOSE))
|
||||
SFG_drawIndicationBorder(SFG_HUD_BORDER_INDICATOR_WIDTH_PIXELS,
|
||||
SFG_HUD_HURT_INDICATION_COLOR);
|
||||
else if (SFG_game.frame - SFG_player.lastItemTakenFrame
|
||||
|
@ -208,6 +208,6 @@
|
||||
Skips menu and starts given level immediatelly, for development. 0 means this
|
||||
options is ignored, 1 means load level 1 etc.
|
||||
*/
|
||||
#define SFG_START_LEVEL 1
|
||||
#define SFG_START_LEVEL 0
|
||||
|
||||
#endif // guard
|
||||
|
Loading…
Reference in New Issue
Block a user