EventDataPtr EventData::clone() const { // not implemented, but do not make it pure virtual to avoid // breaking compatibility with existing code. abort(); return EventDataPtr(); }
void Hero::DamagedMobilityState::enter() { hero.isStunned = true; hero.invincibilityTimer = 1.4667f; hero.stopShooting(); hero.chooseAnimation(); // If we're facing left, move right, if right, move left. if(hero.getDirection() == Directions::Left) { horizontalVelocity.setX(NESNumber(0x00, 0x80).toFloat()); } else { horizontalVelocity.setX(-(NESNumber(0x00, 0x80).toFloat())); } // Special case for sliding and climbing: // * If damaged mid-slide there is no knockback // * If damaged while climbing there is no knockback if(hero.isSliding || hero.isClimbing) { horizontalVelocity.setX(0.0f); } // Ensure we get knocked back hero.body.setGravitated(true); hero.body.setApplyHorizontalVelocity(true); // Make sure we start falling immediately hero.setVelocityY(0.0f); // Make sure animations will play (like when you're on a ladder) hero.getAnimatedSprite()->unpause(); if(auto events = hero.getEventBus().lock()) { events->triggerEvent(EventDataPtr(new EntityDamageEventData(hero.getId(), 0.0f))); } }
void Enemy::update(float dt) { Entity::update(dt); if(getHitPoints() <= 0.0f) { if(auto eventManagetPtr = getEventBus().lock()) { eventManagetPtr->queueEvent(EventDataPtr(new EntityDeathEventData(getId(), EntityDeathEventData::Enemy))); } } if(brain) { brain->update(dt); } if(damageTickCounter > 0) { // setVulnerable(false); damageTickCounter++; damageTickCounter %= 2; } else { // setVulnerable(true); } }
EventDataPtr EventData::clone() const { abort(); // not implemented return EventDataPtr(); }
EventDataPtr CreateEvent(EventType eventType) { return EventDataPtr( g_eventFactory.create(eventType) ); }
void BlockSequence::update(float dt) { if(isActive()) { timer += dt; const auto & timing = descriptor.getTiming(); // First we need to hide any entities that are older than they should be. for(auto it = std::begin(blockEntities), end = std::end(blockEntities); it != end; it++) { auto & entity = *it; if(entity && entity->isActive()) { if(entity->getAge() >= descriptor.getMaximumBlockAge()) { entity->reset(); entity->setActive(false); world.queueObjectRemoval(entity); } } } if(timer >= descriptor.getSpawnInterval()) { timer = 0.0f; const BlockTiming & timingStep = timing.at(step); for(auto it = std::begin(timingStep.getBlockIndicies()), end = std::end(timingStep.getBlockIndicies()); it != end; it++) { int id = *it; auto & entity = blockEntities[id]; blockRects[id].setFillColor({ 32, 255, 32, 196 }); if(entity) { entity->setActive(true); entity->setAgeless(false); world.queueObjectAddition(blockEntities[id]); } } if(timingStep.getBlockIndicies().size()) { if(auto events = eventBus.lock()) { events->triggerEvent( EventDataPtr( new AudioEventData( AudioEventData::ACTION_PLAY_SAMPLE, descriptor.getSoundName() ) ) ); } } step++; } if(step >= timing.size()) { reset(); } // // Check if we want to advance along in the sequence... // if(timer >= timingStep.getAtTime()) { // // Process activiations // for(auto it = std::begin(timingStep.getActivations()), end = std::end(timingStep.getActivations()); it != end; it++) { // int id = *it; // auto & entity = blockEntities[id]; // blockRects[id].setFillColor({ 32, 255, 32, 196 }); // if(entity) { // entity->setActive(true); // world.queueObjectAddition(blockEntities[id]); // } // } // for(auto it = std::begin(timingStep.getDeactivations()), end = std::end(timingStep.getDeactivations()); it != end; it++) { // int id = *it; // auto & entity = blockEntities[id]; // blockRects[id].setFillColor({ 255, 32, 32, 128 }); // if(entity) { // entity->reset(); // entity->setActive(false); // world.queueObjectRemoval(entity); // } // } // if(timingStep.getActivations().size()) { // if(auto events = eventBus.lock()) { // events->triggerEvent( // EventDataPtr( // new AudioEventData( // AudioEventData::ACTION_PLAY_SAMPLE, // descriptor.getSoundName() // ) // ) // ); // } // } // // Advance the step counter // step++; // } // // We got to the end so reset. // if(step >= timing.size()) { // reset(); // } } }
EventDataPtr ObjectRemovedEventData::copy() const { return EventDataPtr(new ObjectRemovedEventData(getObjectId())); }
Hero::Hero(int id, std::shared_ptr<Room> room) : Entity(id, room) , isDecelerating(false) , isStanding(false) , isWalking(false) , isSliding(false) , isInTunnel(false) , isJumping(false) , isFalling(false) , isAirborn(false) , isClimbing(false) , isTouchingLadderTop(false) , isFullyAccelerated(false) , isShooting(false) , isTeleporting(false) , isMorphing(false) , isStunned(false) , isInvincible(false) , isUnderWater(false) , wasUnderWaterLastFrame(false) , climbableRegion(0, 0, 0, 0) , actionController(nullptr) , mobilityState(nullptr) , nextMobilityState(nullptr) , temporaryMobilityState(nullptr) , shootingState(nullptr) , nextShootingState(nullptr) { setDeathType(EntityDeathType::Hero); body.setGravitated(true); body.setHasWorldCollision(true); body.setCollisionCallback(std::bind(&Entity::handleCollision, this, std::placeholders::_1, std::placeholders::_2)); body.setLandingCallback([this](Movable& movable, CollisionInfo& collisionInfo) { if(actionController->shouldMoveRight() || actionController->shouldMoveLeft()) { this->isFullyAccelerated = true; } this->isJumping = false; this->isFalling = false; this->isAirborn = false; if(auto events = this->getEventBus().lock()) { events->triggerEvent(EventDataPtr(new EntityStateChangeEventData(getId(), "landed"))); } #ifdef HIKARI_DEBUG_HERO_PHYSICS this->countAscendingFrames = 0; this->countDecendingFrames = 0; #endif // HIKARI_DEBUG_HERO_PHYSICS }); invincibilityTimer = 0; blinkTimer = 0; isBlinking = false; isVisible = true; walkVelocity = Vector2<float>(NESNumber(0x01, 0x4C).toFloat(), 0.0f); climbVelocity = Vector2<float>(walkVelocity.getY(), walkVelocity.getX()); // Climbs at same speed as he walks jumpVelocity = Vector2<float>(0.0f, -(NESNumber(0x04, 0xA5) + NESNumber(0, 0x40) + NESNumber(0, 0x40)).toFloat()); suddenFallVelocity = Vector2<float>(0.0f, NESNumber(0, 0x80).toFloat()); slideVelocity = Vector2<float>(NESNumber(0x02, 0x80).toFloat(), 0.0f); hurtVelocity = Vector2<float>(); accelerationDelay = 0; accelerationDelayThreshold = 6; #ifdef HIKARI_DEBUG_HERO_PHYSICS this->countAscendingFrames = 0; this->countDecendingFrames = 0; #endif // HIKARI_DEBUG_HERO_PHYSICS isFullyAccelerated = false; setFaction(Factions::Hero); changeMobilityState(std::unique_ptr<MobilityState>(new IdleMobilityState(*this))); changeShootingState(std::unique_ptr<ShootingState>(new NotShootingState(*this))); getAnimatedSprite()->setUsePalette(true); getAnimatedSprite()->setUseSharedPalette(true); }
void Hero::update(float dt) { if(wasUnderWaterLastFrame != isUnderWater) { if(wasUnderWaterLastFrame) { HIKARI_LOG(debug4) << "I'm not in water anymore!"; body.setGravityApplicationThreshold(1); } else { HIKARI_LOG(debug4) << "I'm jumping in the water NOW!"; body.setGravityApplicationThreshold(3); // Only emit event when plunging in to a body of water. if(auto events = this->getEventBus().lock()) { events->triggerEvent(EventDataPtr(new EntityStateChangeEventData(getId(), "water"))); } } } wasUnderWaterLastFrame = isUnderWater; if(const auto & room = getRoom()) { const int gridSize = room->getGridSize(); if(invincibilityTimer > 0.0f) { invincibilityTimer -= dt; isInvincible = true; } else { isInvincible = false; isBlinking = false; isVisible = true; } if(isBlinking) { blinkTimer -= dt; if(blinkTimer <= 0.0f) { blinkTimer = 0.0667f; isVisible = !isVisible; } } // // Check if we're in a tunnel // if(isSliding) { const auto & bbox = body.getBoundingBox(); const int startingX = static_cast<int>(bbox.getLeft()); const int endingX = static_cast<int>(bbox.getRight()); const int y = static_cast<int>(bbox.getTop()) - 1; // Subtract 1 to test the tile directly *above* Rockman isInTunnel = false; int topLeftTile = room->getAttributeAt(startingX / gridSize, y / gridSize); int topRightTile = room->getAttributeAt((endingX - 1) / gridSize, y / gridSize); // Only check the top left and top right points if(((topLeftTile != Room::NO_TILE) && TileAttribute::hasAttribute(topLeftTile, TileAttribute::SOLID)) || ((topRightTile != Room::NO_TILE) && TileAttribute::hasAttribute(topRightTile, TileAttribute::SOLID))) { isInTunnel = true; } } // Check if we're under water or starting to enter water { int bodyPositionTile = room->getAttributeAt( static_cast<int>(getPosition().getX()) / gridSize, static_cast<int>(getPosition().getY()) / gridSize); if((bodyPositionTile != Room::NO_TILE) && TileAttribute::hasAttribute(bodyPositionTile, TileAttribute::WATER)) { isUnderWater = true; } else { isUnderWater = false; } } // // State machine updates // if(temporaryMobilityState) { MobilityState::StateChangeAction action = temporaryMobilityState->update(dt); if(MobilityState::NEXT == action) { popTemporaryMobilityState(); } } else { if(shootingState) { // Handle state change request actions... ShootingState::StateChangeAction action = shootingState->update(dt); if(ShootingState::NEXT == action) { if(nextShootingState) { changeShootingState(std::move(nextShootingState)); } } } if(mobilityState) { // Handle state change request actions... MobilityState::StateChangeAction action = mobilityState->update(dt); if(MobilityState::NEXT == action) { if(nextMobilityState) { changeMobilityState(std::move(nextMobilityState)); } } } } } Entity::update(dt); }