3 #include <BLIB/Math.hpp>
4 #include <BLIB/Util/Random.hpp>
18 constexpr
float PlayerPosX = 88.f;
19 constexpr
float PlayerPosY = 265.f;
20 constexpr
float OpponentPosX = 508.f;
21 constexpr
float OpponentPosY = 25.f;
22 static constexpr
float SquareSize = 200.f;
23 const glm::vec2 PlayerPos(PlayerPosX, PlayerPosY);
24 const glm::vec2 OpponentPos(OpponentPosX, OpponentPosY);
25 const glm::vec2 ViewSize(SquareSize, SquareSize);
26 constexpr
float SlideRate = 285.f;
27 constexpr
float ShakesPerSecond = 18.f;
28 constexpr
float ShakeOffMultiple = ShakesPerSecond * 360.f;
29 constexpr
float ShakeXMag = 10.f;
30 constexpr
float ShakeYMag = 5.f;
31 constexpr
float ShakeTime = 0.75f;
32 constexpr
float ExpandRate = 600.f;
33 constexpr
float ContractRate = 325.f;
34 constexpr
float BallFlashRadius = 200.f;
35 constexpr
float ArrowOscillations = 4.f;
36 constexpr
float ArrowShowTime = 2.5f;
37 constexpr
float ArrowShakeAmount = 10.f;
38 constexpr std::uint8_t ScreenFlashAlpha = 100;
39 constexpr
float ThrowStartX = -25.f;
40 constexpr
float ThrowStartY = PlayerPosY + SquareSize * 0.5f;
41 constexpr
float ThrowEndX = OpponentPosX + SquareSize * 0.5f;
42 constexpr
float ThrowEndY = OpponentPosY;
43 constexpr
float ThrowTime = 1.f;
44 constexpr
float ThrowXVel = (ThrowEndX - ThrowStartX) / ThrowTime;
45 constexpr
float ThrowMiddle = ThrowStartX + (ThrowEndX - ThrowStartX) * 0.75f;
46 constexpr
float ThrowSecondHalfWidth = ThrowEndX - ThrowMiddle;
47 constexpr
float ThrowTop = 10.f;
48 constexpr
float ThrowA = (ThrowEndY - ThrowTop) / (ThrowSecondHalfWidth * ThrowSecondHalfWidth);
49 constexpr
float ThrowSpins = 3.f;
50 constexpr
float ThrowSpinRate = ThrowSpins / ThrowTime * 360.f;
51 constexpr
float BallBounceHeight = ThrowEndY + SquareSize * 0.75f - OpponentPosY - 20.f;
52 constexpr
float BallBounceTime = 1.4f;
53 const glm::vec2 BallFinalPos(ThrowEndX, OpponentPosY + BallBounceHeight);
54 constexpr
float BallShakeTime = 0.5f;
55 constexpr
float ShakeRestTime = 0.65f;
56 constexpr
float ShakeXAmount = 10.f;
57 constexpr
float ShakeYAmount = 2.f;
58 constexpr
float ShakeCount = 6.f;
59 constexpr
float ShakeSpeed = 360.f * ShakeCount;
60 constexpr
float GreenRate = 255.f / 0.5f;
61 constexpr glm::vec3 FlashColor(252.f / 256.f, 230.f / 256.f, 30.f / 256.f);
63 constexpr
float PeoplemonFlashOn = 0.08f;
64 constexpr
float PeoplemonFlashOff = 0.05f;
66 using Animation = cmd::Animation;
68 glm::vec4 makeColor(
float alpha) {
return glm::vec4(FlashColor, alpha / 256.f); }
75 vp.width = engine.renderer().getObserver().getRegionSize().x;
76 vp.height = engine.renderer().getObserver().getRegionSize().y;
85 scissor.offset.x = corner.x;
86 scissor.offset.y = corner.y;
87 scissor.extent.width = ViewSize.x;
88 scissor.extent.height = ViewSize.y;
92 glm::vec4 sfcol(
const sf::Color& c) {
93 return glm::vec4(
static_cast<float>(c.r),
94 static_cast<float>(c.g),
95 static_cast<float>(c.b),
96 static_cast<float>(c.a)) /
104 , viewport(makeViewport(engine, pos))
105 , scissor(makeScissor(pos))
106 , renderBall(false) {}
109 if (sparks) { engine.particleSystem().removeRepeatedSystems<
PeoplemonSpark>(); }
113 const auto join = bl::util::FileUtil::joinPath;
116 const glm::vec2 boxCorner = position == Position::Player ? PlayerPos : OpponentPos;
118 peoplemon.create(engine, engine.renderer().texturePool().getBlankTexture());
119 peoplemon.getTransform().setPosition(ViewSize.x * 0.5f, ViewSize.y);
120 peoplemon.addToScene(scene, bl::rc::UpdateSpeed::Static);
122 ballOpenTxtr = engine.renderer().texturePool().getOrLoadTexture(
125 ballTxtr = engine.renderer().texturePool().getOrLoadTexture(
127 ball.create(engine, ballTxtr);
128 setBallTexture(ballTxtr);
129 ball.getTransform().setPosition(ViewSize.x * 0.5f, ViewSize.y * 0.75f);
130 ball.addToScene(scene, bl::rc::UpdateSpeed::Dynamic);
132 ballFlash.create(engine, 100.f);
133 ballFlash.getTransform().setOrigin(100.f, 100.f);
134 ballFlash.getTransform().setPosition(boxCorner + ball.getTransform().getLocalPosition() +
135 glm::vec2(0.f, 15.f));
136 ballFlash.setColorGradient(glm::vec4(FlashColor, 1.f), sfcol(sf::Color::Transparent));
137 ballFlash.addToScene(scene, bl::rc::UpdateSpeed::Static);
139 statTxtr = engine.renderer().texturePool().getOrLoadTexture(
141 statArrow.create(engine, statTxtr);
142 statArrow.getTransform().setOrigin(statTxtr->size().x * 0.5f, statTxtr->size().y * 0.5f);
143 statArrow.getTransform().setPosition(ViewSize.x, ViewSize.y * 0.5f);
144 statArrow.addToScene(scene, bl::rc::UpdateSpeed::Dynamic);
152 frustrationSrc = AnimationManager::load(
163 recreateAilmentAnimation(trappedSrc);
164 ailmentAnim.getTransform().setPosition(ViewSize * 0.5f);
166 throwBallTxtr = engine.renderer().texturePool().getOrLoadTexture(
168 throwBall.create(engine, throwBallTxtr);
169 setThrowBallTxtr(throwBallTxtr);
170 throwBall.addToScene(scene, bl::rc::UpdateSpeed::Dynamic);
172 sparks = &engine.particleSystem().addRepeatedSystem<
PeoplemonSpark>();
176 sparks->addToScene(scene);
178 implosion = &engine.particleSystem().addRepeatedSystem<
PeoplemonSpark>();
182 implosion->addToScene(scene);
185 screenFlash.addToScene(scene, bl::rc::UpdateSpeed::Static);
191 txtr = engine.renderer().texturePool().getOrLoadTexture(path(ppl));
193 const glm::vec2 ts(txtr->size());
194 peoplemon.setTexture(txtr,
true);
195 peoplemon.getTransform().setOrigin(ts.x * 0.5f, ts.y);
196 peoplemon.setColor(sf::Color::White);
197 scale.x = ViewSize.x / ts.x;
198 scale.y = ViewSize.y / ts.y;
199 peoplemon.getTransform().setScale(scale);
200 state = State::Hidden;
204 state = State::Playing;
208 case Animation::Type::ComeBack:
211 glm::vec2(viewport.x, viewport.y) + ViewSize * 0.5f);
212 setBallTexture(ballOpenTxtr);
213 ball.setColor(sf::Color::White);
216 case Animation::Type::SendOut:
218 peoplemon.setColor(sf::Color(255, 255, 255, 0));
219 ball.setColor(sf::Color::White);
220 setBallTexture(ballTxtr);
221 screenFlash.setFillColor(sf::Color::Transparent);
225 case Animation::Type::ShakeAndFlash:
227 peoplemon.flash(0.08f, 0.05f);
230 case Animation::Type::SlideDown:
234 case Animation::Type::SlideOut:
238 case Animation::Type::MultipleStateDecrease:
239 statArrow.setColor(sf::Color(70, 70, 70));
242 case Animation::Type::StatDecrease:
244 statArrow.getTransform().setScale(1.f, 1.f);
247 case Animation::Type::MultipleStateIncrease:
248 statArrow.setColor(sf::Color(70, 70, 70));
251 case Animation::Type::StatIncrease:
253 statArrow.getTransform().setScale(1.f, -1.f);
258 ailmentAnim.getPlayer().play(
true);
261 case Animation::Type::MakeWildVisible:
262 state = State::Static;
265 case Animation::Type::ThrowCloneBall:
266 case Animation::Type::ThrowPeopleball:
268 throwState = BallThrowState::Arcing;
269 setThrowBallTxtr(throwBallTxtr);
270 throwBall.getTransform().setPosition(ThrowStartX, ThrowStartY);
271 throwBall.getTransform().setRotation(0.f);
272 throwBall.setColor(sf::Color::White);
275 if (anim == Animation::Type::ThrowCloneBall) {
276 clone.create(engine, peoplemon.getTexture());
277 clone.getTransform().setPosition(peoplemon.getTransform().getLocalPosition());
278 clone.addToScene(scene, bl::rc::UpdateSpeed::Dynamic);
283 case Animation::Type::PeopleballShake:
287 case Animation::Type::PeopleballCaught:
292 BL_LOG_ERROR <<
"Invalid animation type for peoplemon: " << anim;
293 state = State::Static;
299 switch (anim.getType()) {
300 case Animation::Type::StatDecrease:
301 case Animation::Type::StatIncrease:
302 switch (anim.getStat()) {
304 statArrow.setColor(sf::Color(50, 50, 180));
308 statArrow.setColor(sf::Color(180, 50, 50));
312 statArrow.setColor(sf::Color(50, 180, 50));
315 statArrow.setColor(sf::Color(120, 120, 50));
319 statArrow.setColor(sf::Color(70, 70, 70));
322 BL_LOG_ERROR <<
"Invalid stat for animation: " << anim.getStat();
328 updateAilmentAnimation(anim.getAilment());
332 updateAilmentAnimation(anim.getPassiveAilment());
345 if (state == State::Playing) {
347 case Animation::Type::ComeBack: {
349 if (ball.getTexture().id() == ballOpenTxtr.id()) {
350 alpha -= ContractRate * dt;
353 setBallTexture(ballTxtr);
356 const std::uint8_t a =
static_cast<std::uint8_t
>(
alpha);
357 const float p =
alpha / 255.f;
358 const float ps = std::sqrt(p);
359 peoplemon.setColor(sf::Color(255, 255, 255,
static_cast<std::uint8_t
>(
alpha)));
360 peoplemon.getTransform().setScale(ps * scale.x, ps * scale.y);
361 if (a <= ScreenFlashAlpha) {
362 screenFlash.setFillColor(sf::Color(255, 255, 255, a));
364 else if (a >= 255 - ScreenFlashAlpha) {
365 screenFlash.setFillColor(sf::Color(255, 255, 255, 255 - a));
367 else { screenFlash.setFillColor(sf::Color(255, 255, 255, ScreenFlashAlpha)); }
372 if (
ballTime >= 0.75f) { state = State::Static; }
376 case Animation::Type::SendOut:
378 if (ball.getTexture().id() == ballTxtr.id()) {
381 setBallTexture(ballOpenTxtr);
382 ballFlash.scaleToSize({1.f, 1.f});
383 ballFlash.setColorGradient(makeColor(255.f), {0.f, 0.f, 0.f, 0.f});
390 alpha += ExpandRate * dt;
392 if (
alpha >= 255.f) {
394 if (sparks->getParticleCountLocked() == 0) { state = State::Static; }
396 const float p =
alpha / 255.f;
397 const float ps = std::sqrt(p);
398 const float pss = std::sqrt(ps);
399 const float ns = BallFlashRadius * pss * 2.f;
400 const std::uint8_t a =
static_cast<std::uint8_t
>(
alpha);
401 ballFlash.setColorGradient(makeColor(255.f - 255.f * p), {0.f, 0.f, 0.f, 0.f});
402 ballFlash.scaleToSize({ns, ns});
404 peoplemon.setColor(sf::Color(122, 8, 128, std::max(a, ScreenFlashAlpha)));
406 else { peoplemon.setColor(sf::Color::White); }
407 peoplemon.getTransform().setScale(ps * scale.x, ps * scale.y);
408 ball.setColor(sf::Color(255, 255, 255, 255 - a));
409 if (a <= ScreenFlashAlpha) {
410 screenFlash.setFillColor(sf::Color(255, 255, 255, a));
412 else if (a >= 255 - ScreenFlashAlpha) {
413 screenFlash.setFillColor(sf::Color(255, 255, 255, 255 - a));
415 else { screenFlash.setFillColor(sf::Color(255, 255, 255, ScreenFlashAlpha)); }
419 case Animation::Type::ShakeAndFlash:
422 peoplemon.stopFlashing();
423 state = State::Static;
427 case Animation::Type::SlideOut:
430 state = State::Static;
431 peoplemon.setColor(sf::Color(255, 255, 255, 0));
435 case Animation::Type::SlideDown:
438 state = State::Static;
439 peoplemon.setColor(sf::Color(255, 255, 255, 0));
443 case Animation::Type::MultipleStateDecrease:
444 case Animation::Type::MultipleStateIncrease:
445 case Animation::Type::StatDecrease:
446 case Animation::Type::StatIncrease:
448 arrowOffset = ArrowShakeAmount *
449 bl::math::sin(
arrowTime / ArrowShowTime * 360.f * ArrowOscillations);
452 state = State::Static;
454 statArrow.getTransform().setPosition(statArrow.getTransform().getLocalPosition().x,
455 ViewSize.y * 0.5f + arrowOffset);
460 if (!ailmentAnim.getPlayer().playing()) { state = State::Static; }
463 case Animation::Type::ThrowCloneBall:
464 case Animation::Type::ThrowPeopleball: {
465 switch (throwState) {
466 case BallThrowState::Arcing: {
468 const float xp =
throwX - ThrowMiddle;
469 throwBall.getTransform().setPosition(
throwX, ThrowA * xp * xp + ThrowTop);
470 throwBall.getTransform().rotate(ThrowSpinRate * dt);
471 if (
throwX >= ThrowEndX) {
473 throwBall.getTransform().setPosition(ThrowEndX, ThrowEndY);
474 setThrowBallTxtr(ballOpenTxtr);
475 throwState = BallThrowState::Eating;
476 sparkImplosionEmitter->
setEnabled(
true, {ThrowEndX, ThrowEndY});
478 if (type == Animation::Type::ThrowCloneBall) {
479 peoplemon.getTransform().setPosition(OpponentPosX + SquareSize * 0.5f,
480 OpponentPosY + SquareSize);
481 peoplemon.setColor(sf::Color(255, 255, 255, 128));
486 case BallThrowState::Eating:
487 if (throwBall.getTexture().id() == ballOpenTxtr.id()) {
488 alpha -= ContractRate * dt;
491 setThrowBallTxtr(ballTxtr);
494 const std::uint8_t a =
static_cast<std::uint8_t
>(
alpha);
495 const float p =
alpha / 255.f;
496 const float ps = std::sqrt(p);
497 toEat->setColor(sf::Color(255, 255, 255,
static_cast<std::uint8_t
>(
alpha)));
498 toEat->getTransform().setScale(ps * scale.x, p * scale.y);
499 toEat->getTransform().setPosition(
500 OpponentPosX + SquareSize * 0.5f,
501 OpponentPosY - (BallBounceHeight + 35.f) * (255.f -
alpha) / 255.f +
503 if (a <= ScreenFlashAlpha) {
504 screenFlash.setFillColor(sf::Color(255, 255, 255, a));
506 else if (a >= 255 - ScreenFlashAlpha) {
507 screenFlash.setFillColor(sf::Color(255, 255, 255, 255 - a));
509 else { screenFlash.setFillColor(sf::Color(255, 255, 255, ScreenFlashAlpha)); }
514 peoplemon.getTransform().setPosition(ViewSize.x * 0.5f, ViewSize.y);
515 if (type == Animation::Type::ThrowCloneBall) {
516 throwState = BallThrowState::CloneFading;
518 peoplemon.setColor(sf::Color::White);
521 throwState = BallThrowState::Bouncing;
528 case BallThrowState::Bouncing:
530 throwBall.getTransform().setPosition(
531 throwBall.getTransform().getLocalPosition().x,
532 OpponentPosY + BallBounceHeight -
533 std::abs(bl::math::cos(bl::math::radiansToDegrees(
534 3.5f * bl::math::Pi / BallBounceTime *
bounceTime))) /
535 std::max(4.f *
bounceTime, 1.f) * BallBounceHeight);
537 throwBall.getTransform().setPosition(BallFinalPos);
538 state = State::Static;
542 case BallThrowState::CloneFading:
543 alpha = std::max(
alpha - dt * GreenRate, 0.f);
544 throwBall.setColor(sf::Color(255, 255, 255,
static_cast<int>(
alpha)));
547 state = State::Static;
553 case Animation::Type::PeopleballShake:
556 throwBall.getTransform().setPosition(
558 glm::vec2(ShakeXAmount * bl::math::cos(ShakeSpeed *
shakeTime * 2.f),
559 ShakeYAmount * bl::math::cos(ShakeSpeed *
shakeTime)));
562 throwBall.getTransform().setPosition(BallFinalPos);
563 if (
shakeTime >= BallShakeTime + ShakeRestTime) { state = State::Static; }
567 case Animation::Type::PeopleballCaught:
568 alpha = std::min(
alpha + dt * GreenRate, 255.f);
570 sf::Color(255 -
static_cast<int>(
alpha),
static_cast<int>(
alpha), 0));
571 if (
alpha >= 255.f) { state = State::Static; }
574 case Animation::Type::PlayerFirstSendout:
575 case Animation::Type::OpponentFirstSendout:
576 case Animation::Type::UseMove:
577 case Animation::Type::MakeWildVisible:
578 case Animation::Type::_ERROR:
586 if (state == State::Hidden)
return;
588 const auto setViewport = [
this, &ctx]() {
589 ctx.setViewport(viewport,
false);
590 ctx.setScissor(scissor,
false);
594 if (state == State::Playing) {
596 case Animation::Type::ComeBack:
599 ctx.resetViewportAndScissor();
600 screenFlash.draw(ctx);
601 implosion->draw(ctx);
604 case Animation::Type::SendOut:
607 ctx.resetViewportAndScissor();
608 screenFlash.draw(ctx);
609 if (ball.getTexture().id() == ballOpenTxtr.id()) {
615 case Animation::Type::ShakeAndFlash: {
616 const float t =
shakeTime * ShakeOffMultiple;
617 const float m = bl::math::sin(t / ShakeTime * 180.f);
618 VkViewport vp = viewport;
619 vp.x += m * ShakeXMag * bl::math::sin(t);
620 vp.y += m * ShakeYMag * bl::math::cos(-t);
621 ctx.setViewport(vp,
false);
625 case Animation::Type::SlideDown: {
626 VkViewport vp = viewport;
628 ctx.setViewport(vp,
false);
632 case Animation::Type::SlideOut: {
633 VkViewport vp = viewport;
634 vp.x +=
slideAmount * (position == Position::Player ? -1.f : 1.f);
635 ctx.setViewport(vp,
false);
639 case Animation::Type::MultipleStateDecrease:
640 case Animation::Type::MultipleStateIncrease:
641 case Animation::Type::StatDecrease:
642 case Animation::Type::StatIncrease:
651 ailmentAnim.draw(ctx);
654 case Animation::Type::ThrowCloneBall:
655 case Animation::Type::ThrowPeopleball:
656 switch (throwState) {
657 case BallThrowState::Arcing:
659 ctx.resetViewportAndScissor();
663 case BallThrowState::Eating:
665 ctx.resetViewportAndScissor();
667 if (type == Animation::Type::ThrowCloneBall) { clone.draw(ctx); }
669 screenFlash.draw(ctx);
670 implosion->draw(ctx);
673 case BallThrowState::CloneFading:
677 case BallThrowState::Bouncing:
680 ctx.resetViewportAndScissor();
686 case Animation::Type::PeopleballShake:
688 ctx.resetViewportAndScissor();
692 case Animation::Type::PeopleballCaught:
694 ctx.resetViewportAndScissor();
698 case Animation::Type::PlayerFirstSendout:
699 case Animation::Type::OpponentFirstSendout:
700 case Animation::Type::UseMove:
701 case Animation::Type::MakeWildVisible:
702 case Animation::Type::_ERROR:
707 else if (state == State::Static) {
710 ctx.resetViewportAndScissor();
715 ctx.resetViewportAndScissor();
718 void PeoplemonAnimation::setBallTexture(bl::rc::res::TextureRef& t) {
719 ball.setTexture(t,
true);
720 ball.getTransform().setOrigin(t->size().x / 2.f, t->size().y);
723 void PeoplemonAnimation::setThrowBallTxtr(bl::rc::res::TextureRef& t) {
724 throwBall.setTexture(t,
true);
725 throwBall.getTransform().setOrigin(t->size() * 0.5f);
728 void PeoplemonAnimation::updateAilmentAnimation(
pplmn::Ailment ail) {
731 recreateAilmentAnimation(annoySrc);
734 recreateAilmentAnimation(frozenSrc);
737 recreateAilmentAnimation(frustrationSrc);
740 recreateAilmentAnimation(sleepSrc);
743 recreateAilmentAnimation(stickySrc);
746 BL_LOG_WARN <<
"Invalid ailment: " << ail;
754 recreateAilmentAnimation(confuseSrc);
757 recreateAilmentAnimation(trappedSrc);
760 recreateAilmentAnimation(jumpedSrc);
763 BL_LOG_WARN <<
"Invalid ailment animation: " << ail;
768 void PeoplemonAnimation::recreateAilmentAnimation(
769 bl::resource::Ref<bl::gfx::a2d::AnimationData>& src) {
770 ailmentAnim.createWithUniquePlayer(engine, src);
771 ailmentAnim.addToScene(scene, bl::rc::UpdateSpeed::Static);
Type
The type classification of an item. This is used to determine when an item may be used and how to use...
PassiveAilment
Represents a passive ailment a Peoplemon can have.
Ailment
Represents an ailment that a Peoplemon can have.
Core classes and functionality for both the editor and game.
void init(bl::rc::scene::CodeScene *scene)
Initializes rendering resources.
Position
Which position the peoplemon is in.
~PeoplemonAnimation()
Frees resources.
PeoplemonAnimation(bl::engine::Engine &engine, Position position)
Construct a new Peoplemon Animation utility.
void render(bl::rc::scene::CodeScene::RenderContext &ctx)
Renders the peoplemon animation.
void triggerAnimation(const cmd::Animation &anim)
Begins playing the given animation.
bool completed() const
Returns true if the animation has completed, false if in progress.
void update(float dt)
Updates the playing animation.
void setPeoplemon(pplmn::Id ppl)
Sets the specific peoplemon graphic and resets to hidden state.
Particle system spark for peoplemon animations.
Particle system spark emitter for explosions.
Particle system spark emitter for implosions.
void setEnabled(bool e, const glm::vec2 &origin={})
Particle system spark affector.
Particle system spark sink.
static std::string playerImage(Id id)
Returns the full path to the image to use in battle for the player peoplemon.
static std::string opponentImage(Id id)
Returns the full path to the image to use in battle for the opponent peoplemon.
static const std::string & AnimationPath()
static int WindowHeight()
static const std::string & ImagePath()