Skip to content

Commit

Permalink
Add optional sprite parameters to entity:overlaps()
Browse files Browse the repository at this point in the history
  • Loading branch information
christopho committed Aug 18, 2018
1 parent e81a4ab commit b6e2914
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 33 deletions.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Changes that do not introduce incompatibilities:
* Add methods entity:get_properties() and entity:set_properties() (#1144).
* Add methods entity:get_layer() and entity:set_layer().
* Add a method entity:get_controlling_stream() (#1204).
* Add optional sprite parameters to entity:overlaps() (#1159).
* Add methods door:open(), door:close() and door:set_open() (#1007).
* Add methods stairs:get_direction() and stairs:is_inner() (#1037).
* destructible:on_lifting() now gives a carried object parameter.
Expand Down
12 changes: 9 additions & 3 deletions include/solarus/entities/Entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,15 +295,21 @@ class SOLARUS_API Entity: public ExportableToLua {
void check_collision(Entity& other);
void check_collision(Entity& other, Sprite& other_sprite);
void check_collision(Sprite& this_sprite, Entity& other);
// TODO void check_collision(Sprite& this_sprite, Entity& other, Sprite& other_sprite);
bool test_collision(Entity& entity, CollisionMode collision_mode);
bool test_collision(
Entity& entity,
CollisionMode collision_mode,
const SpritePtr& this_sprite,
const SpritePtr& other_sprite);
bool test_collision_rectangle(const Entity& entity) const;
bool test_collision_inside(const Entity& entity) const;
bool test_collision_origin_point(const Entity& entity) const;
bool test_collision_facing_point(const Entity& entity) const;
bool test_collision_touching(const Entity& entity) const;
bool test_collision_center(const Entity& entity) const;
bool test_collision_sprites(Entity& entity);
bool test_collision_sprites(
Entity& entity,
const SpritePtr& this_sprite,
const SpritePtr& other_sprite);
virtual bool test_collision_custom(Entity& entity);

// Being detected by other entities.
Expand Down
56 changes: 41 additions & 15 deletions src/entities/Entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2066,9 +2066,17 @@ void Entity::check_collision(Sprite& this_sprite, Entity& other) {
* collision test.
* \param entity The entity.
* \param collision_mode The collision test to perform.
* \param this_sprite Sprite of this entity to test (only for sprite collision mode),
* or nullptr to test all of them.
* \param other_sprite Sprite of the other entity to test (only for sprite collision mode),
* or nullptr to test all of them.
* \return \c true if there is a collision.
*/
bool Entity::test_collision(Entity& entity, CollisionMode collision_mode) {
bool Entity::test_collision(
Entity& entity,
CollisionMode collision_mode,
const SpritePtr& this_sprite,
const SpritePtr& other_sprite) {

if (get_layer() != entity.get_layer() && !has_layer_independent_collisions()) {
// Not the same layer: no collision.
Expand Down Expand Up @@ -2102,7 +2110,7 @@ bool Entity::test_collision(Entity& entity, CollisionMode collision_mode) {
return test_collision_custom(entity);

case CollisionMode::COLLISION_SPRITE:
return test_collision_sprites(entity);
return test_collision_sprites(entity, this_sprite, other_sprite);
}

return false;
Expand Down Expand Up @@ -2201,25 +2209,43 @@ bool Entity::test_collision_center(const Entity& entity) const {
* The test is pixel-precise.
*
* \param entity The entity.
* \param this_sprite Sprite of this entity to test, or nullptr to test all of them.
* \param other_sprite Sprite of the other entity to test, or nullptr to test all of them.
* \return \c true if sprites of both entities overlap.
*/
bool Entity::test_collision_sprites(Entity& entity) {
bool Entity::test_collision_sprites(
Entity& entity,
const SpritePtr& this_sprite,
const SpritePtr& other_sprite) {

for (const NamedSprite& this_named_sprite: sprites) {

if (this_named_sprite.removed) {
continue;
// Select the sprites to check depending on paramaters.
std::vector<SpritePtr> this_sprites;
if (this_sprite != nullptr) {
this_sprites.push_back(this_sprite);
} else {
for (const NamedSprite& this_named_sprite: this->sprites) {
if (!this_named_sprite.removed) {
this_sprites.push_back(this_named_sprite.sprite);
}
}
Sprite& this_sprite = *this_named_sprite.sprite;
this_sprite.enable_pixel_collisions();
}
std::vector<SpritePtr> other_sprites;
if (other_sprite != nullptr) {
other_sprites.push_back(other_sprite);
} else {
for (const NamedSprite& other_named_sprite: entity.sprites) {

if (other_named_sprite.removed) {
continue;
if (!other_named_sprite.removed) {
other_sprites.push_back(other_named_sprite.sprite);
}
Sprite& other_sprite = *other_named_sprite.sprite;
other_sprite.enable_pixel_collisions();
if (this_sprite.test_collision(other_sprite, get_x(), get_y(), entity.get_x(), entity.get_y())) {
}
}

// Test the selected sprites.
for (const SpritePtr& this_sprite : this_sprites) {
this_sprite->enable_pixel_collisions();
for (const SpritePtr& other_sprite : other_sprites) {
other_sprite->enable_pixel_collisions();
if (this_sprite->test_collision(*other_sprite, get_x(), get_y(), entity.get_x(), entity.get_y())) {
return true;
}
}
Expand Down
41 changes: 26 additions & 15 deletions src/lua/EntityApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,8 @@ int LuaContext::entity_api_overlaps(lua_State* l) {
if (is_entity(l, 2)) {
Entity& other_entity = *check_entity(l, 2);
std::string collision_mode_name = LuaTools::opt_string(l, 3, "overlapping");
SpritePtr entity_sprite;
SpritePtr other_entity_sprite;

CollisionMode collision_mode = CollisionMode::COLLISION_NONE;
if (collision_mode_name == "overlapping") {
Expand All @@ -1271,22 +1273,31 @@ int LuaContext::entity_api_overlaps(lua_State* l) {
}
else if (collision_mode_name == "sprite") {
collision_mode = CollisionMode::COLLISION_SPRITE;
if (!lua_isnoneornil(l, 4)) {
entity_sprite = check_sprite(l, 4);
}
if (!lua_isnoneornil(l, 5)) {
other_entity_sprite = check_sprite(l, 5);
}
}
else {
LuaTools::arg_error(l, 3,
std::string("Invalid name '") + lua_tostring(l, 2) + "'"
);
}

overlaps = entity.test_collision(other_entity, collision_mode);
overlaps = entity.test_collision(other_entity, collision_mode, entity_sprite, other_entity_sprite);
}
else {
else if (lua_isnumber(l, 2)) {
int x = LuaTools::check_int(l, 2);
int y = LuaTools::check_int(l, 3);
int width = LuaTools::opt_int(l, 4, 1);
int height = LuaTools::opt_int(l, 5, 1);
overlaps = entity.overlaps(Rectangle(x, y, width, height));
}
else {
LuaTools::type_error(l, 2, "entity or integer");
}

lua_pushboolean(l, overlaps);
return 1;
Expand Down Expand Up @@ -1979,7 +1990,7 @@ int LuaContext::entity_api_set_properties(lua_State* l) {
* \brief Returns whether a value is a userdata of type hero.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a hero.
* \return \c true if the value at this index is a hero.
*/
bool LuaContext::is_hero(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::HERO));
Expand Down Expand Up @@ -2853,7 +2864,7 @@ int LuaContext::l_treasure_brandish_finished(lua_State* l) {
* \brief Returns whether a value is a userdata of type camera.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a camera.
* \return \c true if the value at this index is a camera.
*/
bool LuaContext::is_camera(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::CAMERA));
Expand Down Expand Up @@ -3255,7 +3266,7 @@ int LuaContext::teletransporter_api_set_destination_name(lua_State* l) {
* \brief Returns whether a value is a userdata of type NPC.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is an NPC.
* \return \c true if the value at this index is an NPC.
*/
bool LuaContext::is_npc(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::NPC));
Expand Down Expand Up @@ -3320,7 +3331,7 @@ int LuaContext::npc_api_set_traversable(lua_State* l) {
* \brief Returns whether a value is a userdata of type chest.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a chest.
* \return \c true if the value at this index is a chest.
*/
bool LuaContext::is_chest(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::CHEST));
Expand Down Expand Up @@ -3451,7 +3462,7 @@ int LuaContext::chest_api_set_treasure(lua_State* l) {
* \brief Returns whether a value is a userdata of type block.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a block.
* \return \c true if the value at this index is a block.
*/
bool LuaContext::is_block(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::BLOCK));
Expand Down Expand Up @@ -3643,7 +3654,7 @@ int LuaContext::block_api_set_maximum_moves(lua_State* l) {
* \brief Returns whether a value is a userdata of type switch.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a switch.
* \return \c true if the value at this index is a switch.
*/
bool LuaContext::is_switch(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::SWITCH));
Expand Down Expand Up @@ -3754,7 +3765,7 @@ int LuaContext::switch_api_is_walkable(lua_State* l) {
* \brief Returns whether a value is a userdata of type stream.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a stream.
* \return \c true if the value at this index is a stream.
*/
bool LuaContext::is_stream(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::STREAM));
Expand Down Expand Up @@ -3950,7 +3961,7 @@ int LuaContext::stream_api_set_allow_item(lua_State* l) {
* \brief Returns whether a value is a userdata of type door.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a door.
* \return \c true if the value at this index is a door.
*/
bool LuaContext::is_door(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::DOOR));
Expand Down Expand Up @@ -4097,7 +4108,7 @@ int LuaContext::door_api_set_open(lua_State* l) {
* \brief Returns whether a value is a userdata of type stairs.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a stairs.
* \return \c true if the value at this index is a stairs entity.
*/
bool LuaContext::is_stairs(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::STAIRS));
Expand Down Expand Up @@ -4159,7 +4170,7 @@ int LuaContext::stairs_api_is_inner(lua_State* l) {
* \brief Returns whether a value is a userdata of type shop treasure.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a shop treasure.
* \return \c true if the value at this index is a shop treasure.
*/
bool LuaContext::is_shop_treasure(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::SHOP_TREASURE));
Expand Down Expand Up @@ -4310,7 +4321,7 @@ int LuaContext::l_shop_treasure_question_dialog_finished(lua_State* l) {
* \brief Returns whether a value is a userdata of type pickable.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a pickable.
* \return \c true if the value at this index is a pickable.
*/
bool LuaContext::is_pickable(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::PICKABLE));
Expand Down Expand Up @@ -4866,7 +4877,7 @@ int LuaContext::dynamic_tile_api_get_modified_ground(lua_State* l) {
* \brief Returns whether a value is a userdata of type enemy.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is an enemy.
* \return \c true if the value at this index is an enemy.
*/
bool LuaContext::is_enemy(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::ENEMY));
Expand Down Expand Up @@ -5768,7 +5779,7 @@ int LuaContext::enemy_api_create_enemy(lua_State* l) {
* \brief Returns whether a value is a userdata of type custom entity.
* \param l A Lua context.
* \param index An index in the stack.
* \return true if the value at this index is a custom entity.
* \return \c true if the value at this index is a custom entity.
*/
bool LuaContext::is_custom_entity(lua_State* l, int index) {
return is_userdata(l, index, get_entity_internal_type_name(EntityType::CUSTOM));
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ set(lua_test_maps
"bugs/1136_get_path_get_trajectory_missing_element"
"bugs/1139_pickable_on_stream"
"bugs/1158_entity_timer_suspended"
"bugs/1159_entity_overlaps_sprites"
"bugs/1162_custom_entity_collision_wrong_order"
"bugs/1163_circular_movement_get_direction4"
"bugs/1171_sword_knowledge_on_ability_used"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
properties{
x = 0,
y = 0,
width = 320,
height = 240,
min_layer = 0,
max_layer = 2,
tileset = "castle",
}

tile{
layer = 0,
x = 0,
y = 0,
width = 320,
height = 240,
pattern = "3",
}

destination{
name = "destination",
layer = 0,
x = 24,
y = 29,
direction = 0,
}

custom_entity{
name = "custom",
layer = 0,
x = 48,
y = 29,
width = 16,
height = 16,
direction = 0,
sprite = "16x16",
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
local map = ...
local game = map:get_game()

game:set_ability("sword", 1)

function map:on_opening_transition_finished()

-- Give two sprites to the custom entity: a main one and a shadow.
-- The sword sprite of the hero will overlap the main one but not the shadow.

local custom_main_sprite = custom:get_sprite()
assert(custom_main_sprite ~= nil)
local custom_shadow_sprite = custom:create_sprite("entities/shadow")
assert(custom_shadow_sprite ~= nil)
custom_shadow_sprite:set_animation("small")

game:simulate_command_pressed("attack")
sol.timer.start(map, 500, function()
assert(not hero:overlaps(custom))
assert(not hero:overlaps(custom, overlapping))
assert(hero:overlaps(custom, "sprite"))

local tunic_sprite = hero:get_sprite("tunic")
assert(tunic_sprite ~= nil)
local sword_sprite = hero:get_sprite("sword")
assert(sword_sprite ~= nil)

assert(not hero:overlaps(custom, "sprite", tunic_sprite, custom_main_sprite))
assert(not hero:overlaps(custom, "sprite", tunic_sprite, custom_shadow_sprite))
assert(hero:overlaps(custom, "sprite", sword_sprite, custom_main_sprite))
assert(not hero:overlaps(custom, "sprite", sword_sprite, custom_shadow_sprite))

assert(not hero:overlaps(custom, "sprite", tunic_sprite, nil))
assert(not hero:overlaps(custom, "sprite", tunic_sprite))
assert(hero:overlaps(custom, "sprite", sword_sprite, nil))
assert(hero:overlaps(custom, "sprite", sword_sprite))
assert(hero:overlaps(custom, "sprite", nil, custom_main_sprite))
assert(not hero:overlaps(custom, "sprite", nil, custom_shadow_sprite))

sol.main.exit()
end)
end
1 change: 1 addition & 0 deletions tests/testing_quest/data/project_db.dat
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ map{ id = "bugs/1105_custom_entity_repeat", description = "#1105: Allow to repea
map{ id = "bugs/1136_get_path_get_trajectory_missing_element", description = "#1136: Missing element in pixel_movement:get_trajectory() and path_movement:get_path()" }
map{ id = "bugs/1139_pickable_on_stream", description = "#1139: Allow pickables to follow streams" }
map{ id = "bugs/1158_entity_timer_suspended", description = "#1158: timer:set_suspended_with_map(false) not working with entity timers" }
map{ id = "bugs/1159_entity_overlaps_sprites", description = "#1159: Add sprites parameters to entity:overlaps()" }
map{ id = "bugs/1162_custom_entity_collision_wrong_order", description = "#1162: Wrong sprite order in custom entity collision callback" }
map{ id = "bugs/1163_circular_movement_get_direction4", description = "#1163: circular_movement:get_direction4() not working" }
map{ id = "bugs/1171_sword_knowledge_on_ability_used", description = "#1171: Fix item:on_ability_used() not called with sword knowledge" }
Expand Down

0 comments on commit b6e2914

Please sign in to comment.