/** * \brief Updates this state. */ void Hero::StairsState::update() { State::update(); if (is_suspended()) { return; } // first time: we play the sound and initialize if (phase == 0) { stairs->play_sound(way); next_phase_date = System::now() + 450; phase++; } // update the carried item if any if (carried_item != nullptr) { carried_item->update(); } Hero& hero = get_entity(); if (stairs->is_inside_floor()) { // inside a single floor: return to normal state as soon as the movement is finished if (hero.get_movement()->is_finished()) { if (way == Stairs::REVERSE_WAY) { get_entities().set_entity_layer(hero, stairs->get_layer()); } hero.clear_movement(); if (carried_item == nullptr) { hero.set_state(new FreeState(hero)); } else { hero.set_state(new CarryingState(hero, carried_item)); } } } else { // stairs between two different floors: more complicated HeroSprites& sprites = get_sprites(); if (hero.get_movement()->is_finished()) { hero.clear_movement(); if (carried_item == nullptr) { hero.set_state(new FreeState(hero)); } else { hero.set_state(new CarryingState(hero, carried_item)); } if (way == Stairs::NORMAL_WAY) { // we are on the old floor: // there must be a teletransporter associated with these stairs, // otherwise the hero would get stuck into the walls Teletransporter* teletransporter = hero.get_delayed_teletransporter(); Debug::check_assertion(teletransporter != nullptr, "Teletransporter expected with the stairs"); teletransporter->transport_hero(hero); } else { // we are on the new floor: everything is finished sprites.set_clipping_rectangle(); } } else { // movement not finished yet uint32_t now = System::now(); if (now >= next_phase_date) { phase++; next_phase_date += 350; // main movement direction corresponding to each animation direction while taking stairs static constexpr int movement_directions[] = { 0, 0, 2, 4, 4, 4, 6, 0 }; int animation_direction = stairs->get_animation_direction(way); if (phase == 2) { // the first phase of the movement is finished if (animation_direction % 2 != 0) { // if the stairs are spiral, take a diagonal direction of animation sprites.set_animation_walking_diagonal(animation_direction); } else { // otherwise, take a usual direction sprites.set_animation_direction(animation_direction / 2); sprites.set_animation_walking_normal(); } } else if (phase == 3) { // the second phase of the movement (possibly diagonal) is finished sprites.set_animation_walking_normal(); if (way == Stairs::NORMAL_WAY) { // on the old floor, take a direction towards the next floor sprites.set_animation_direction(movement_directions[animation_direction] / 2); } else { // on the new floor, take the opposite direction from the stairs sprites.set_animation_direction((stairs->get_direction() + 2) % 4); } } } } } }
/** * \brief Throws the item that is being lifted. * * This function is called when this state is interrupted by a new state, * e.g. when the hero is hurt while lifting an item. */ void Hero::LiftingState::throw_item() { lifted_item->throw_item(get_sprites().get_animation_direction()); get_entities().add_entity(lifted_item); lifted_item = NULL; }
/** * \brief This function is called repeatedly. */ void CarriedItem::update() { // update the sprite and the position Entity::update(); if (is_suspended()) { return; } // when the hero finishes lifting the item, start carrying it if (is_lifting && get_movement()->is_finished()) { is_lifting = false; // make the item follow the hero clear_movement(); set_movement(std::make_shared<RelativeMovement>( std::static_pointer_cast<Hero>(hero.shared_from_this()), 0, -18, true )); } // when the item has finished flying, destroy it else if (can_explode() && !is_breaking) { uint32_t now = System::now(); if (now >= explosion_date) { break_item(); } else if (will_explode_soon()) { std::string animation = get_sprite().get_current_animation(); if (animation == "stopped") { get_sprite().set_current_animation("stopped_explosion_soon"); } else if (animation == "walking") { get_sprite().set_current_animation("walking_explosion_soon"); } } } if (is_throwing) { shadow_sprite->update(); if (is_broken()) { remove_from_map(); } else if (break_one_layer_above) { break_item(); Layer layer = get_layer(); if (layer != LAYER_HIGH) { get_entities().set_entity_layer(*this, Layer(layer + 1)); } break_one_layer_above = false; } else if (get_movement()->is_stopped() || y_increment >= 7) { // Interrupt the movement. break_item_on_ground(); } else { uint32_t now = System::now(); while (now >= next_down_date) { next_down_date += 40; item_height -= y_increment; y_increment++; } } } }
/** * \brief Throws the item carried. * * This function is called when the player presses the action key * or when another state becomes the current state. */ void Hero::CarryingState::throw_item() { carried_item->throw_item(get_sprites().get_animation_direction()); get_entities().add_entity(carried_item); carried_item = NULL; }
/** * \brief Changes the order of an entity in its layer. * \param src_index The current index of the entity to change. * \param dst_order The new order to set. * It must be valid: in particular, tiles must remain before dynamic entities. */ void MapData::set_entity_order(const EntityIndex& src_index, int dst_order) { Layer layer = src_index.layer; int src_order = src_index.order; if (dst_order == src_order) { // No change. return; } EntityData entity = get_entity(src_index); // Make a copy. bool dynamic = entity.is_dynamic(); int min_order = dynamic ? get_num_tiles(layer) : 0; int max_order = dynamic ? (get_num_entities(layer) - 1) : (get_num_tiles(layer) - 1); Debug::check_assertion(dst_order >= min_order, "Entity order out of range (lower than min)"); Debug::check_assertion(dst_order <= max_order, "Entity order out of range (higher than max)"); std::deque<EntityData>& entities = get_entities(layer); // Update entities and named_entities. auto src_it = entities.begin() + src_order; if (entity.has_name()) { named_entities[entity.get_name()] = { layer, dst_order }; } entities.erase(src_it); auto dst_it = entities.begin() + dst_order; entities.insert(dst_it, entity); if (dst_order < src_order) { // Moving downwards. // Indexes between dst_order exclusive and src_order inclusive get incremented. for (int i = dst_order + 1; i <= src_order; ++i ) { const EntityData& current_entity = get_entity({ layer, i }); const std::string& name = current_entity.get_name(); if (!name.empty() && name != entity.get_name()) { EntityIndex& index = named_entities[name]; ++index.order; } } } else { // Moving upwards. // Indexes between src_order inclusive and dst_order exclusive get decremented. for (int i = src_order; i < dst_order; ++i ) { const EntityData& current_entity = get_entity({ layer, i }); const std::string& name = current_entity.get_name(); if (!name.empty() && name != entity.get_name()) { EntityIndex& index = named_entities[name]; --index.order; } } } }
/** * @brief This function is called repeatedly. */ void CarriedItem::update() { // update the sprite and the position MapEntity::update(); if (suspended) { return; } // when the hero finishes lifting the item, start carrying it if (is_lifting && get_movement()->is_finished()) { is_lifting = false; // make the item follow the hero clear_movement(); set_movement(new FollowMovement(&hero, 0, -18, true)); } // when the item has finished flying, destroy it else if (can_explode() && !is_breaking) { uint32_t now = System::now(); if (now >= explosion_date) { break_item(); } else if (will_explode_soon()) { std::string animation = get_sprite().get_current_animation(); if (animation == "stopped") { get_sprite().set_current_animation("stopped_explosion_soon"); } else if (animation == "walking") { get_sprite().set_current_animation("walking_explosion_soon"); } } } if (is_throwing) { shadow_sprite->update(); if (is_broken()) { remove_from_map(); } else if (break_on_intermediate_layer) { break_item(); get_entities().set_entity_layer(*this, LAYER_INTERMEDIATE); break_on_intermediate_layer = false; } else if (get_movement()->is_stopped() || y_increment >= 7) { break_item(); } else { uint32_t now = System::now(); while (now >= next_down_date) { next_down_date += 40; item_height -= y_increment; y_increment++; } } } }
/** * \brief Makes the bomb explode. */ void Bomb::explode() { get_entities().add_entity(new Explosion("", get_layer(), get_center_point(), true)); Sound::play("explosion"); remove_from_map(); }
int main( int argc, char *argv[] ) { int ret; int fd; struct stat stat; void *ptr = MAP_FAILED; Entity::vector faces; DGIFFHeader header = { magic: { 'D', 'G', 'I', 'F', 'F' }, major: 0, minor: 0, flags: DGIFF_FLAG_LITTLE_ENDIAN, num_faces: 0 }; direct_initialize(); direct_debug_config_domain( "mkdgiff", true ); direct_config->debug = true; direct_config->debugmem = true; /* Parse the command line. */ if (!parse_command_line( argc, argv )) return -1; /* Open the file. */ fd = open( filename, O_RDONLY ); if (fd < 0) { ret = errno2result( errno ); D_PERROR( "Font/DGIFF: Failure during open() of '%s'!\n", filename ); return ret; } /* Query file size etc. */ if (fstat( fd, &stat ) < 0) { ret = errno2result( errno ); D_PERROR( "Font/DGIFF: Failure during fstat() of '%s'!\n", filename ); goto out; } /* Memory map the file. */ ptr = mmap( NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0 ); if (ptr == MAP_FAILED) { ret = errno2result( errno ); D_PERROR( "Font/DGIFF: Failure during mmap() of '%s'!\n", filename ); goto out; } get_entities( (const char*) ptr, stat.st_size, faces ); header.num_faces = faces.size(); fwrite( &header, sizeof(header), 1, stdout ); for (Entity::vector::const_iterator iter = faces.begin(); iter != faces.end(); iter++) { const Face *face = dynamic_cast<const Face*>( *iter ); face->Dump(); ret = do_face( face ); if (ret) goto out; } out: if (ptr != MAP_FAILED) munmap( ptr, stat.st_size ); close( fd ); direct_print_memleaks(); direct_shutdown(); return ret; }
StringDictionary EntityBrowser<Assembly>::get_entities(const string& type) const { return get_entities(m_assembly, type); }
DESCRIBE("Entity System") IT("adds entities that have correct components on world update", { // Arrange auto world = std::make_shared<World>(); auto system = std::make_shared<SystemThatWantsComponent1>(); world->attach(system); // Act auto e = world->create_entity(); world->add_component<Component1>(e); world->update(); // Assert S_ASSERT(1 == (int)system->get_entities().size(), "system did not store entity"); // Teardown }); IT("does'nt add entities with incorrect components on world update", { // Arrange auto world = std::make_shared<World>(); auto system = std::make_shared<SystemThatWantsComponent1>(); world->attach(system); // Act world->create_entity(); world->update();
void ib::tulip_fabric_t::populate(const bool populateFields) { tlp::StringProperty * viewLabel = 0; ///Using string for GUID since integer is 32bits (on x86) tlp::StringProperty * ibGuid = 0; tlp::IntegerProperty * ibPortNum = 0; tlp::IntegerProperty * ibLid = 0; tlp::IntegerProperty * ibHca = 0; tlp::StringProperty * ibWidth = 0; tlp::StringProperty * ibSpeed = 0; tlp::StringProperty * ibName = 0; tlp::StringProperty * ibLeaf = 0; tlp::StringProperty * ibSpine = 0; if(populateFields) { viewLabel = graph->getProperty<tlp::StringProperty>("viewLabel"); ibGuid = graph->getProperty<tlp::StringProperty>("ibGuid"); ibWidth = graph->getProperty<tlp::StringProperty>("ibWidth"); ibSpeed = graph->getProperty<tlp::StringProperty>("ibSpeed"); ibName = graph->getProperty<tlp::StringProperty>("ibName"); ibLeaf = graph->getProperty<tlp::StringProperty>("ibLeaf"); ibSpine = graph->getProperty<tlp::StringProperty>("ibSpine"); ibPortNum = graph->getProperty<tlp::IntegerProperty >("ibPortNum"); ibLid = graph->getProperty<tlp::IntegerProperty >("ibLid"); ibHca = graph->getProperty<tlp::IntegerProperty >("ibHca"); } /** * Create the tulip graph by having the following * 1 node = 1 entity * 2 edges = 1 cable (1 edge in each direction) */ /** * reserve 2 edges per cable */ graph->reserveEdges(get_portmap().size() * 2); /** * Walk every entity and create every node */ for( ib::fabric_t::entities_t::const_iterator itr = get_entities().begin(), eitr = get_entities().end(); itr != eitr; ++itr ) { const ib::entity_t &entity = itr->second; if(entity_nodes.find(const_cast<ib::entity_t*>(&entity)) == entity_nodes.end()) { /** * Create node and insert it into map */ tlp::node node = graph->addNode(); assert(node.isValid()); assert(graph->getRoot()->isElement(node)); std::pair<ib::tulip_fabric_t::entity_nodes_t::const_iterator, bool> result = entity_nodes.insert(std::make_pair(const_cast<ib::entity_t*>(&entity), node)); assert(result.second); ///should never fail! assert(result.first->second == node); if(populateFields) { typedef ib::entity_t l; viewLabel->setNodeValue(node, entity.label(l::LABEL_ENTITY_ONLY)); ibName->setNodeValue(node, entity.label(l::LABEL_NAME_ONLY)); ibLeaf->setNodeValue(node, entity.label(l::LABEL_LEAF_ONLY)); ibSpine->setNodeValue(node, entity.label(l::LABEL_SPINE_ONLY)); ibPortNum->setNodeValue(node, entity.ports.size()); ///define list of known port count on this entity ibGuid->setNodeValue(node, regex::string_cast_uint(entity.guid)); ibLid->setNodeValue(node, entity.lid()); ibHca->setNodeValue(node, entity.hca()); } } } /** * Walk every port and create every edge */ for( ib::fabric_t::portmap_guidport_t::const_iterator itr = get_portmap().begin(), eitr = get_portmap().end(); itr != eitr; ++itr ) { ib::port_t const * const port = itr->second; assert(port); if(port->connection) { tlp::node n1 = get_entity_node(port->guid); tlp::node n2 = get_entity_node(port->connection->guid); assert(n1.isValid()); assert(n2.isValid()); tlp::edge edge = graph->addEdge(n1, n2); assert(edge.isValid()); std::pair<ib::tulip_fabric_t::port_edges_t::iterator, bool> result = port_edges.insert(std::make_pair(const_cast<ib::port_t*>(port), edge)); assert(result.second); ///should never fail! assert(result.first->second == edge); if(populateFields) { typedef ib::port_t l; ibName->setEdgeValue(edge, port->label(l::LABEL_FULL)); ///Dump full label for edges with both ports const std::string label = port->label() + " <--> " + port->connection->label(); viewLabel->setEdgeValue(edge, label); ibGuid->setEdgeValue(edge, regex::string_cast_uint(port->guid)); ibWidth->setEdgeValue(edge, port->width); ibSpeed->setEdgeValue(edge, port->speed); ibLeaf->setEdgeValue(edge, regex::string_cast_uint(port->leaf)); ibSpine->setEdgeValue(edge, regex::string_cast_uint(port->spine)); ibPortNum->setEdgeValue(edge, port->port); ibLid->setEdgeValue(edge, port->lid); ibHca->setEdgeValue(edge, port->hca); } } } }
/** * \brief Creates an explosion on the item. */ void Destructible::explode() { get_entities().add_entity(new Explosion("", get_layer(), get_xy(), true)); Sound::play("explosion"); }
/** * \brief Adds to the map the pickable treasure (if any) hidden under this destructible item. */ void Destructible::create_pickable() { get_entities().add_entity(Pickable::create(get_game(), "", get_layer(), get_x(), get_y(), treasure, FALLING_MEDIUM, false)); }
/** * \brief Reacts to the ground of the pickable. * * It is removed it is on water, lava or a hole. * It goes to the lower layer if the ground is empty. */ void Pickable::check_bad_ground() { if (is_being_removed()) { // Be silent if the pickable was already removed by a script. return; } if (get_entity_followed() != nullptr) { // We are attached to a hookshot or boomerang: don't fall. return; } if (get_y() < shadow_xy.y) { // The pickable is above the ground for now, let it fall first. return; } if (get_movement() != nullptr && !get_movement()->is_finished()) { // The falling movement is not finished yet. return; } if (System::now() <= appear_date + 200) { // The pickable appeared very recently, let the user see it for // a short time at least. return; } Ground ground = get_ground_below(); switch (ground) { case Ground::EMPTY: { // Fall to a lower layer. int layer = get_layer(); if (layer > 0) { --layer; get_entities().set_entity_layer(*this, layer); } } break; case Ground::HOLE: { Sound::play("jump"); remove_from_map(); } break; case Ground::DEEP_WATER: case Ground::LAVA: { Sound::play("splash"); remove_from_map(); } break; default: break; } }
/** * \brief Returns the number of entities on a layer of this map. * \param layer A layer. * \return The number of entities on that layer. */ int MapData::get_num_entities(Layer layer) const { return get_entities(layer).size(); }
/** * \brief Updates the enemy. */ void Enemy::update() { MapEntity::update(); if (is_suspended() || !is_enabled()) { return; } uint32_t now = System::now(); if (being_hurt) { // see if we should stop the animation "hurt" if (now >= stop_hurt_date) { being_hurt = false; set_movement_events_enabled(true); if (life <= 0) { kill(); } else if (is_immobilized()) { clear_movement(); set_animation("immobilized"); notify_immobilized(); } else { clear_movement(); restart(); } } } if (life > 0 && invulnerable && now >= vulnerable_again_date && !being_hurt) { invulnerable = false; } if (life > 0 && !can_attack && !is_immobilized() && can_attack_again_date != 0 && now >= can_attack_again_date) { can_attack = true; } if (is_immobilized() && !is_killed() && now >= end_shaking_date && get_sprite().get_current_animation() == "shaking") { restart(); } if (is_immobilized() && !is_killed() && !is_being_hurt() && now >= start_shaking_date && get_sprite().get_current_animation() != "shaking") { end_shaking_date = now + 2000; set_animation("shaking"); } if (exploding) { uint32_t now = System::now(); if (now >= next_explosion_date) { // create an explosion Rectangle xy; xy.set_x(get_top_left_x() + Random::get_number(get_width())); xy.set_y(get_top_left_y() + Random::get_number(get_height())); get_entities().add_entity(new Explosion("", LAYER_HIGH, xy, false)); Sound::play("explosion"); next_explosion_date = now + 200; nb_explosions++; if (nb_explosions >= 15) { exploding = false; } } } if (is_killed() && is_dying_animation_finished()) { // Create the pickable treasure if any. get_entities().add_entity(Pickable::create(get_game(), "", get_layer(), get_x(), get_y(), treasure, FALLING_HIGH, false)); // Remove the enemy. remove_from_map(); // Notify Lua that this enemy is dead. // We need to do this after remove_from_map() so that this enemy is // considered dead in functions like map:has_entities(prefix). notify_dead(); } get_lua_context().enemy_on_update(*this); }
static int do_face( const Face *face ) { int i, ret; int align = DFB_PIXELFORMAT_ALIGNMENT( m_format ); int num_glyphs = 0; int num_rows = 1; int row_index = 0; int row_offset = 0; int next_face = sizeof(DGIFFFaceHeader); int total_height = 0; Entity::vector glyph_vector; unsigned int glyph_count = 0; DGIFFFaceHeader header; DGIFFGlyphInfo *glyphs; DGIFFGlyphRow *rows; void **row_data; DFBSurfaceDescription *descs; D_DEBUG_AT( mkdgiff, "%s( %p )\n", __FUNCTION__, face ); get_entities( face->buf, face->length, glyph_vector ); glyph_count = glyph_vector.size(); /* Clear to not leak any data into file. */ memset( &header, 0, sizeof(header) ); /* Allocate glyph info array. */ glyphs = (DGIFFGlyphInfo*) D_CALLOC( glyph_count, sizeof(DGIFFGlyphInfo) ); rows = (DGIFFGlyphRow*) D_CALLOC( glyph_count, sizeof(DGIFFGlyphRow) ); /* WORST case :) */ row_data = (void**) D_CALLOC( glyph_count, sizeof(void*) ); /* WORST case :) */ descs = (DFBSurfaceDescription*) D_CALLOC( glyph_count, sizeof(DFBSurfaceDescription) ); /* WORST case :) */ for (Entity::vector::const_iterator iter = glyph_vector.begin(); iter != glyph_vector.end(); iter++) { const Glyph *glyph = dynamic_cast<const Glyph*>( *iter ); glyph->Dump(); DGIFFGlyphInfo *info = &glyphs[num_glyphs]; DGIFFGlyphRow *row = &rows[num_rows - 1]; D_DEBUG_AT( mkdgiff, " -> code %3u\n", glyph->unicode ); ret = load_image( glyph->file.c_str(), &descs[num_glyphs] ); if (ret) continue; info->unicode = glyph->unicode; info->width = descs[num_glyphs].width; info->height = descs[num_glyphs].height; info->left = glyph->left; info->top = glyph->top; info->advance = glyph->advance; num_glyphs++; if (row->width > 0 && row->width + info->width > MAX_ROW_WIDTH) { num_rows++; row++; } row->width += (info->width + align) & ~align; if (row->height < info->height) row->height = info->height; } for (i=0; i<num_rows; i++) { DGIFFGlyphRow *row = &rows[i]; D_DEBUG_AT( mkdgiff, " -> row %d, width %d, height %d\n", i, row->width, row->height ); total_height += row->height; row->pitch = (DFB_BYTES_PER_LINE( m_format, row->width ) + 7) & ~7; row_data[i] = D_CALLOC( row->height, row->pitch ); next_face += row->height * row->pitch; } D_DEBUG_AT( mkdgiff, " -> %d glyphs, %d rows, total height %d\n", num_glyphs, num_rows, total_height ); next_face += num_glyphs * sizeof(DGIFFGlyphInfo); next_face += num_rows * sizeof(DGIFFGlyphRow); for (i=0; i<num_glyphs; i++) { DGIFFGlyphInfo *glyph = &glyphs[i]; D_DEBUG_AT( mkdgiff, " -> writing character 0x%x (%d)\n", glyph->unicode, i ); if (row_offset > 0 && row_offset + glyph->width > MAX_ROW_WIDTH) { row_index++; row_offset = 0; } D_DEBUG_AT( mkdgiff, " -> row offset %d\n", row_offset ); write_glyph( glyph, descs[i], (char*) row_data[row_index] + DFB_BYTES_PER_LINE( m_format, row_offset ), rows[row_index].pitch ); glyph->row = row_index; glyph->offset = row_offset; row_offset += (glyph->width + align) & ~align; } D_ASSERT( row_index == num_rows - 1 ); header.next_face = next_face; header.size = face->size; header.ascender = face->ascender; header.descender = face->descender; header.height = face->height; header.max_advance = face->maxadvance; header.pixelformat = m_format; header.num_glyphs = num_glyphs; header.num_rows = num_rows; header.blittingflags = face->blittingflags; D_DEBUG_AT( mkdgiff, " -> ascender %d, descender %d\n", header.ascender, header.descender ); D_DEBUG_AT( mkdgiff, " -> height %d, max advance %d\n", header.height, header.max_advance ); fwrite( &header, sizeof(header), 1, stdout ); fwrite( glyphs, sizeof(*glyphs), num_glyphs, stdout ); for (i=0; i<num_rows; i++) { DGIFFGlyphRow *row = &rows[i]; fwrite( row, sizeof(*row), 1, stdout ); fwrite( row_data[i], row->pitch, row->height, stdout ); } for (i=0; i<num_rows; i++) { if (row_data[i]) D_FREE( row_data[i] ); } D_FREE( row_data ); D_FREE( rows ); D_FREE( glyphs ); return 0; }