Add item collision map

This commit is contained in:
Miloslav Číž 2020-02-22 23:36:15 +01:00
parent e0922011bb
commit e6367982b1
3 changed files with 151 additions and 95 deletions

View File

@ -1,9 +1,5 @@
general: general:
- make enemies not move through items:
create a 2D bit array saying at which squares there are colliding items, then
check collisions for both player and monsters against this array (elevate
these squares for collisions only) -- will also be faster
- try to remove the debug flag (-g1) from compiler and see if it decreases size - try to remove the debug flag (-g1) from compiler and see if it decreases size
- port to GB Meta - port to GB Meta
- more level prop items - more level prop items
@ -107,4 +103,8 @@ done:
- check if monsters are hit by bullets from completely up close - check if monsters are hit by bullets from completely up close
- menu - menu
- GUI - GUI
- make enemies not move through items:
create a 2D bit array saying at which squares there are colliding items, then
check collisions for both player and monsters against this array (elevate
these squares for collisions only) -- will also be faster

View File

@ -303,7 +303,7 @@ SFG_PROGRAM_MEMORY SFG_Level SFG_levels[SFG_NUMBER_OF_LEVELS] =
{SFG_LEVEL_ELEMENT_TELEPORT, {11, 12}}, {SFG_LEVEL_ELEMENT_TELEPORT, {17, 20}}, {SFG_LEVEL_ELEMENT_TELEPORT, {11, 12}}, {SFG_LEVEL_ELEMENT_TELEPORT, {17, 20}},
{SFG_LEVEL_ELEMENT_TELEPORT, {20, 40}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_TELEPORT, {20, 40}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}},
{SFG_LEVEL_ELEMENT_TERMINAL, {11, 25}}, {SFG_LEVEL_ELEMENT_TERMINAL, {11, 26}}, {SFG_LEVEL_ELEMENT_TERMINAL, {11, 25}}, {SFG_LEVEL_ELEMENT_TERMINAL, {11, 26}},
{SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_MONSTER_WARRIOR, {10, 45}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}},
{SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}},
{SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}},
{SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}}, {SFG_LEVEL_ELEMENT_NONE, {0, 0}},

236
main.c
View File

@ -338,8 +338,41 @@ struct
uint16_t mapRevealMask; /**< Bits say which parts of the map have been uint16_t mapRevealMask; /**< Bits say which parts of the map have been
revealed. */ revealed. */
uint8_t itemCollisionMap[(SFG_MAP_SIZE * SFG_MAP_SIZE) / 8];
/**< Bit array, for each map square says whether there
is a colliding item or not. */
} SFG_currentLevel; } SFG_currentLevel;
void SFG_getItemCollisionMapIndex(
uint8_t x, uint8_t y, uint16_t *byte, uint8_t *bit)
{
uint16_t index = y * SFG_MAP_SIZE + x;
*byte = index / 8;
*bit = index % 8;
}
void SFG_setItemCollisionMapBit(uint8_t x, uint8_t y, uint8_t value)
{
uint16_t byte;
uint8_t bit;
SFG_getItemCollisionMapIndex(x,y,&byte,&bit);
SFG_currentLevel.itemCollisionMap[byte] &= ~(0x01 << bit);
SFG_currentLevel.itemCollisionMap[byte] |= (value & 0x01) << bit;
}
uint8_t SFG_getItemCollisionMapBit(uint8_t x, uint8_t y)
{
uint16_t byte;
uint8_t bit;
SFG_getItemCollisionMapIndex(x,y,&byte,&bit);
return (SFG_currentLevel.itemCollisionMap[byte] >> bit) & 0x01;
}
#if SFG_DITHERED_SHADOW #if SFG_DITHERED_SHADOW
SFG_PROGRAM_MEMORY uint8_t SFG_ditheringPatterns[] = SFG_PROGRAM_MEMORY uint8_t SFG_ditheringPatterns[] =
{ {
@ -994,6 +1027,12 @@ RCL_Unit SFG_floorHeightAt(int16_t x, int16_t y)
doorHeight * SFG_DOOR_HEIGHT_STEP; doorHeight * SFG_DOOR_HEIGHT_STEP;
} }
RCL_Unit SFG_floorCollisionHeightAt(int16_t x, int16_t y)
{
return SFG_floorHeightAt(x,y) +
SFG_getItemCollisionMapBit(x,y) * RCL_UNITS_PER_SQUARE;
}
void SFG_initPlayer() void SFG_initPlayer()
{ {
RCL_initCamera(&SFG_player.camera); RCL_initCamera(&SFG_player.camera);
@ -1063,6 +1102,46 @@ RCL_Unit SFG_ceilingHeightAt(int16_t x, int16_t y)
SFG_game.frameTime - SFG_currentLevel.timeStart); SFG_game.frameTime - SFG_currentLevel.timeStart);
} }
void SFG_getLevelElementSprite( // TODO: this is just for items -- rename?
uint8_t elementType, uint8_t *spriteIndex, uint8_t *spriteSize)
{
*spriteSize = 0;
*spriteIndex = elementType - 1;
switch (elementType)
{
case SFG_LEVEL_ELEMENT_TREE:
*spriteSize = 2;
break;
case SFG_LEVEL_ELEMENT_TERMINAL:
*spriteSize = 1;
break;
case SFG_LEVEL_ELEMENT_TELEPORT:
case SFG_LEVEL_ELEMENT_FINISH:
*spriteSize = 3;
break;
case SFG_LEVEL_ELEMENT_CARD0:
case SFG_LEVEL_ELEMENT_CARD1:
case SFG_LEVEL_ELEMENT_CARD2:
*spriteIndex = SFG_LEVEL_ELEMENT_CARD0 - 1;
break;
default:
break;
}
}
uint8_t SFG_levelElementCollides(uint8_t elementType) // TODO: better name?
{
return
elementType == SFG_LEVEL_ELEMENT_BARREL ||
elementType == SFG_LEVEL_ELEMENT_TREE ||
elementType == SFG_LEVEL_ELEMENT_TERMINAL;
}
void SFG_setAndInitLevel(const SFG_Level *level) void SFG_setAndInitLevel(const SFG_Level *level)
{ {
SFG_LOG("setting and initializing level"); SFG_LOG("setting and initializing level");
@ -1129,6 +1208,9 @@ void SFG_setAndInitLevel(const SFG_Level *level)
SFG_MonsterRecord *monster; SFG_MonsterRecord *monster;
for (uint16_t i = 0; i < ((SFG_MAP_SIZE * SFG_MAP_SIZE) / 8); ++i)
SFG_currentLevel.itemCollisionMap[i] = 0;
for (uint8_t i = 0; i < SFG_MAX_LEVEL_ELEMENTS; ++i) for (uint8_t i = 0; i < SFG_MAX_LEVEL_ELEMENTS; ++i)
{ {
const SFG_LevelElement *e = &(SFG_currentLevel.levelPointer->elements[i]); const SFG_LevelElement *e = &(SFG_currentLevel.levelPointer->elements[i]);
@ -1160,6 +1242,9 @@ void SFG_setAndInitLevel(const SFG_Level *level)
if (e->type == SFG_LEVEL_ELEMENT_TELEPORT) if (e->type == SFG_LEVEL_ELEMENT_TELEPORT)
SFG_currentLevel.teleportCount++; SFG_currentLevel.teleportCount++;
if (SFG_levelElementCollides(e->type))
SFG_setItemCollisionMapBit(e->coords[0],e->coords[1],1);
} }
else else
{ {
@ -1376,7 +1461,7 @@ uint8_t SFG_pushAway(
c.position.y = pos[1]; c.position.y = pos[1];
c.height = pos[2]; c.height = pos[2];
RCL_moveCameraWithCollision(&c,offset,0,SFG_floorHeightAt, RCL_moveCameraWithCollision(&c,offset,0,SFG_floorCollisionHeightAt,
SFG_ceilingHeightAt,1,1); SFG_ceilingHeightAt,1,1);
pos[0] = c.position.x; pos[0] = c.position.x;
@ -1474,10 +1559,35 @@ void SFG_removeItem(uint8_t index)
SFG_currentLevel.itemRecordCount--; SFG_currentLevel.itemRecordCount--;
} }
/**
Helper function, returns a pointer to level element representing item with
given index, but only if the item is active (otherwise 0 is returned).
*/
static inline const SFG_LevelElement *SFG_getActiveItemElement(uint8_t index)
{
SFG_ItemRecord item = SFG_currentLevel.itemRecords[index];
if ((item & SFG_ITEM_RECORD_ACTIVE_MASK) == 0)
return 0;
return &(SFG_currentLevel.levelPointer->elements[item &
~SFG_ITEM_RECORD_ACTIVE_MASK]);
}
static inline const SFG_LevelElement *SFG_getLevelElement(uint8_t index)
{
SFG_ItemRecord item = SFG_currentLevel.itemRecords[index];
return &(SFG_currentLevel.levelPointer->elements[item &
~SFG_ITEM_RECORD_ACTIVE_MASK]);
}
void SFG_createExplosion(RCL_Unit, RCL_Unit, RCL_Unit); // forward decl void SFG_createExplosion(RCL_Unit, RCL_Unit, RCL_Unit); // forward decl
void SFG_explodeBarrel(uint8_t itemIndex, RCL_Unit x, RCL_Unit y, RCL_Unit z) void SFG_explodeBarrel(uint8_t itemIndex, RCL_Unit x, RCL_Unit y, RCL_Unit z)
{ {
const SFG_LevelElement *e = SFG_getLevelElement(itemIndex);
SFG_setItemCollisionMapBit(e->coords[0],e->coords[1],0);
SFG_removeItem(itemIndex); SFG_removeItem(itemIndex);
SFG_createExplosion(x,y,z); SFG_createExplosion(x,y,z);
} }
@ -1852,10 +1962,10 @@ void SFG_monsterPerformAI(SFG_MonsterRecord *monster)
else else
{ {
RCL_Unit currentHeight = RCL_Unit currentHeight =
SFG_floorHeightAt(monster->coords[0] / 4,monster->coords[1] / 4); SFG_floorCollisionHeightAt(monster->coords[0] / 4,monster->coords[1] / 4);
RCL_Unit newHeight = RCL_Unit newHeight =
SFG_floorHeightAt(newPos[0] / 4,newPos[1] / 4); SFG_floorCollisionHeightAt(newPos[0] / 4,newPos[1] / 4);
collision = collision =
RCL_absVal(currentHeight - newHeight) > RCL_CAMERA_COLL_STEP_HEIGHT; RCL_absVal(currentHeight - newHeight) > RCL_CAMERA_COLL_STEP_HEIGHT;
@ -1875,21 +1985,6 @@ void SFG_monsterPerformAI(SFG_MonsterRecord *monster)
monster->coords[1] = newPos[1];; monster->coords[1] = newPos[1];;
} }
/**
Helper function, returns a pointer to level element representing item with
given index, but only if the item is active (otherwise 0 is returned).
*/
static inline const SFG_LevelElement *SFG_getActiveItemElement(uint8_t index)
{
SFG_ItemRecord item = SFG_currentLevel.itemRecords[index];
if ((item & SFG_ITEM_RECORD_ACTIVE_MASK) == 0)
return 0;
return &(SFG_currentLevel.levelPointer->elements[item &
~SFG_ITEM_RECORD_ACTIVE_MASK]);
}
static inline uint8_t SFG_elementCollides( static inline uint8_t SFG_elementCollides(
RCL_Unit pointX, RCL_Unit pointX,
RCL_Unit pointY, RCL_Unit pointY,
@ -1933,38 +2028,6 @@ uint8_t SFG_projectileCollides(SFG_ProjectileRecord *projectile,
return RCL_vectorsAngleCos(projDir,toElement) >= 0; return RCL_vectorsAngleCos(projDir,toElement) >= 0;
} }
void SFG_getLevelElementSprite(
uint8_t elementType, uint8_t *spriteIndex, uint8_t *spriteSize)
{
*spriteSize = 0;
*spriteIndex = elementType - 1;
switch (elementType)
{
case SFG_LEVEL_ELEMENT_TREE:
*spriteSize = 2;
break;
case SFG_LEVEL_ELEMENT_TERMINAL:
*spriteSize = 1;
break;
case SFG_LEVEL_ELEMENT_TELEPORT:
case SFG_LEVEL_ELEMENT_FINISH:
*spriteSize = 3;
break;
case SFG_LEVEL_ELEMENT_CARD0:
case SFG_LEVEL_ELEMENT_CARD1:
case SFG_LEVEL_ELEMENT_CARD2:
*spriteIndex = SFG_LEVEL_ELEMENT_CARD0 - 1;
break;
default:
break;
}
}
/** /**
Updates a frame of the currently loaded level, i.e. enemies, projectiles, Updates a frame of the currently loaded level, i.e. enemies, projectiles,
aimations etc., with the exception of player. aimations etc., with the exception of player.
@ -2059,7 +2122,7 @@ void SFG_updateLevel()
} }
} }
if (!eliminate) // items if (!eliminate) // items (can't check itemCollisionMap because of barrels)
for (uint16_t j = 0; j < SFG_currentLevel.itemRecordCount; ++j) for (uint16_t j = 0; j < SFG_currentLevel.itemRecordCount; ++j)
{ {
const SFG_LevelElement *e = SFG_getActiveItemElement(j); const SFG_LevelElement *e = SFG_getActiveItemElement(j);
@ -2753,54 +2816,47 @@ void SFG_gameStepPlaying()
SFG_playSoundSafe(3,255); SFG_playSoundSafe(3,255);
#endif #endif
} }
else else if (
e->type == SFG_LEVEL_ELEMENT_TELEPORT &&
SFG_currentLevel.teleportCount > 1 &&
!SFG_player.justTeleported)
{ {
if (e->type != SFG_LEVEL_ELEMENT_TELEPORT) // teleport to random destination teleport
{
// collide
moveOffset = SFG_resolveCollisionWithElement(
SFG_player.camera.position,moveOffset,ePos);
}
else if ((SFG_currentLevel.teleportCount > 1) &&
!SFG_player.justTeleported)
{
// teleport to random destination teleport
uint8_t teleportNumber = uint8_t teleportNumber =
SFG_random() % (SFG_currentLevel.teleportCount - 1) + 1; SFG_random() % (SFG_currentLevel.teleportCount - 1) + 1;
for (uint16_t j = 0; j < SFG_currentLevel.itemRecordCount; ++j) for (uint16_t j = 0; j < SFG_currentLevel.itemRecordCount; ++j)
{
SFG_LevelElement e2 =
SFG_currentLevel.levelPointer->elements
[SFG_currentLevel.itemRecords[j] &
~SFG_ITEM_RECORD_ACTIVE_MASK];
if ((e2.type == SFG_LEVEL_ELEMENT_TELEPORT) && (j != i))
teleportNumber--;
if (teleportNumber == 0)
{ {
SFG_LevelElement e2 = SFG_player.camera.position.x =
SFG_currentLevel.levelPointer->elements SFG_ELEMENT_COORD_TO_RCL_UNITS(e2.coords[0]);
[SFG_currentLevel.itemRecords[j] &
~SFG_ITEM_RECORD_ACTIVE_MASK];
if ((e2.type == SFG_LEVEL_ELEMENT_TELEPORT) && (j != i)) SFG_player.camera.position.y =
teleportNumber--; SFG_ELEMENT_COORD_TO_RCL_UNITS(e2.coords[1]);
if (teleportNumber == 0) SFG_player.camera.height =
{ SFG_floorHeightAt(e2.coords[0],e2.coords[1]) +
SFG_player.camera.position.x = RCL_CAMERA_COLL_HEIGHT_BELOW;
SFG_ELEMENT_COORD_TO_RCL_UNITS(e2.coords[0]);
SFG_player.camera.position.y = SFG_currentLevel.itemRecords[j] |= SFG_ITEM_RECORD_ACTIVE_MASK;
SFG_ELEMENT_COORD_TO_RCL_UNITS(e2.coords[1]); /* ^ we have to make the new teleport immediately active so
that it will immediately collide */
SFG_player.camera.height = SFG_player.justTeleported = 1;
SFG_floorHeightAt(e2.coords[0],e2.coords[1]) +
RCL_CAMERA_COLL_HEIGHT_BELOW;
SFG_currentLevel.itemRecords[j] |= SFG_ITEM_RECORD_ACTIVE_MASK; SFG_playSoundSafe(4,255);
/* ^ we have to make the new teleport immediately active so
that it will immediately collide */
SFG_player.justTeleported = 1; break;
SFG_playSoundSafe(4,255);
break;
}
} }
} }
} }
@ -2822,7 +2878,7 @@ void SFG_gameStepPlaying()
SFG_PREVIEW_MODE_SPEED_MULTIPLIER * SFG_player.verticalSpeed; SFG_PREVIEW_MODE_SPEED_MULTIPLIER * SFG_player.verticalSpeed;
#else #else
RCL_moveCameraWithCollision(&(SFG_player.camera),moveOffset, RCL_moveCameraWithCollision(&(SFG_player.camera),moveOffset,
verticalOffset,SFG_floorHeightAt,SFG_ceilingHeightAt,1,1); verticalOffset,SFG_floorCollisionHeightAt,SFG_ceilingHeightAt,1,1);
SFG_player.previousVerticalSpeed = SFG_player.verticalSpeed; SFG_player.previousVerticalSpeed = SFG_player.verticalSpeed;