name: "Bee Room Reverse" panel_display_name: "Bee Room" panels { name: "POCKET" path: "Panels/Bee Room/bee_9" clue: "pocket" answer: "rocket" symbols: ZERO } panels { name: "FLUME" path: "Panels/Bee Room/bee_10" clue: "flume" answer: "zoom" symbols: ZERO } panels { name: "HEALED" path: "Panels/Bee Room/bee_11" clue: "healed" answer: "shield" symbols: ZERO } panels { name: "SCRATCH" path: "Panels/Bee Room/bee_12" clue: "scratch" answer: "match" symbols: ZERO } panels { name: "MINORITY" path: "Panels/Bee Room/bee_13" clue: "minority" answer: "authority" symbols: ZERO } panels { name: "STYLINGS" path: "Panels/Bee Room/bee_14" clue: "stylings" answer: "filings" symbols: ZERO } panels { name: "PLANTS" path: "Panels/Bee Room/bee_15" clue: "plants" answer: "nature" symbols: BOXES } panels { name: "COUNCIL" path: "Panels/Bee Room/bee_16" clue: "council" answer: "counsel" symbols: ZERO } paintings { name: "BEE3" path: "Components/Paintings/Group3/bee3" gravity: Y_PLUS } paintings { name: "BUTTERFLY2" path: "Components/Paintings/Group3/butterfly2" gravity: Y_PLUS } '/tanetane/log/src/character_system.cpp'>log blame commit diff stats
path: root/src/character_system.cpp
blob: 368505eda1b61a070e5150d96bcae499a2a1335b (plain) (tree)
1
2
3
4
5
6
7
8
9






                             
                          
 
                                                                   

                                             
                                       
                                 

 


                                                                      
                              
                               
 







                                                                    




                                                            



                                                                                                     
                                                                                                
   





                                                                             


                                                                               
                                                                                                    


                                                
                                 
                                     









                                                                           
                                         









                                            








                                                   
                           


   
                                                                    
                                             
                           
 
                                   

                                  











                                                                                         
 


                                                                  
                                             



                                                                               
     
   
 
 

                                                   
 

                                                         
   
 
 
                                       

                                       



                                                 
 
                                                
                                
 



                                                                 
 
                                                                          
                                         
                                                                                                                    

                     
 
                                                                 

                           
                                                                                                                                                                           
 

                                                      

         

                                                               
 
                                                    
                                                                                                            
           
 

                                                 
 



                                                                          
 


                                                                                                           
           
         
 


                                                                                                        

         



                                                                        



                                                                                                             
                                        





                                                                   

           
                                                                 

                                            












                                                                                                          




                                                                                          



                                                                 

                                    
                                                                                                      


           









                                                         
                                                            

                                                           


             
                                                       






                                                           


                           
 
                                                  
                                                   
                              



                                                 
                                                 
                           
                              
 

                                                                         


                                             
                                   
                                           
                                       

   







                                                           
 











                                                                              





                                                   




























                                                                                
                                                  
                                             



                                   
                                                                               
                               
                             








                                                                                 







                                                                                
                             


            

   



















































                                                                                                              
#include "character_system.h"
#include "consts.h"
#include "mixer.h"
#include "sprite.h"
#include "game.h"
#include "transform_system.h"
#include "animation_system.h"
#include "script_system.h"

void CharacterSystem::initSprite(int spriteId, int movementSpeed) {
  Sprite& sprite = game_.getSprite(spriteId);
  sprite.orientable = true;
  sprite.movementSpeed = movementSpeed;
  sprite.trailsAreHalved = false;
}

void CharacterSystem::addSpriteToParty(int leaderId, int followerId) {
  Sprite& leader = game_.getSprite(leaderId);
  Sprite& follower = game_.getSprite(followerId);
  follower.orientable = false;
  follower.leaderId = leaderId;

  vec2i targetPos = leader.loc;

  if (!leader.followers.empty()) {
    Sprite& backFollower = game_.getSprite(leader.followers.back());
    follower.trail = backFollower.trail;
    targetPos = backFollower.loc;
  }

  Direction toFace = leader.dir;
  if (targetPos != follower.loc) {
    toFace = directionFacingPoint(targetPos - follower.loc);
  }

  int truePartyDelay = PARTY_FRAME_DELAY / leader.movementSpeed;

  for (int i=0; i<truePartyDelay; i++) {
    vec2i tween = ((follower.loc - targetPos) * i) / static_cast<double>(truePartyDelay) + targetPos;
    follower.trail.push_front({.pos = tween, .dir = toFace, .medium = CharacterMedium::Normal});
  }

  leader.followers.push_back(followerId);

  game_.getSystem<AnimationSystem>().setSpriteAnimation(followerId, "still");
}

void CharacterSystem::transplantParty(int leaderId, vec2i pos, Direction dir) {
  Sprite& leader = game_.getSprite(leaderId);
  CharacterState oldState = leader.characterState;
  CharacterMedium newMedium = game_.getSystem<TransformSystem>().getMediumAtPosition(leaderId, pos);

  std::vector<int> followers = leader.followers;
  leader.followers.clear();
  leader.trailsAreHalved = false;
  leader.characterMedium = newMedium;

  game_.getSystem<TransformSystem>().moveSprite(leaderId, pos);
  game_.getSystem<AnimationSystem>().setSpriteDirection(leaderId, dir);

  for (int followerId : followers) {
    Sprite& follower = game_.getSprite(followerId);
    follower.trail.clear();

    game_.getSystem<TransformSystem>().moveSprite(followerId, pos);
    game_.getSystem<AnimationSystem>().setSpriteDirection(followerId, dir);
    follower.characterMedium = newMedium;
    addSpriteToParty(leaderId, followerId);
  }

  if (oldState == CharacterState::Running) {
    startRunning(leaderId);
  } else {
    setPartyState(leaderId, oldState);
  }
}

void CharacterSystem::breakUpParty(int leaderId) {
  Sprite& leader = game_.getSprite(leaderId);
  std::vector<int> followers = leader.followers;
  leader.followers.clear();
  leader.trailsAreHalved = false;

  for (int followerId : followers) {
    Sprite& follower = game_.getSprite(followerId);
    follower.trail.clear();
    follower.leaderId = -1;
  }
}

void CharacterSystem::moveInDirection(int spriteId, Direction dir) {
  Sprite& sprite = game_.getSprite(spriteId);
  sprite.movementDir = dir;

  switch (sprite.characterMedium) {
    case CharacterMedium::Normal:
    case CharacterMedium::Water: {
      game_.getSystem<AnimationSystem>().setSpriteDirection(spriteId, dir);
      break;
    }
    case CharacterMedium::Ladder: {
      if (dirHasDir(dir, Direction::up)) {
        game_.getSystem<AnimationSystem>().setSpriteDirection(spriteId, Direction::up);
      } else if (dirHasDir(dir, Direction::down)) {
        game_.getSystem<AnimationSystem>().setSpriteDirection(spriteId, Direction::down);
      }
      break;
    }
  }

  if (sprite.characterState == CharacterState::Still) {
    setPartyState(spriteId, CharacterState::Walking);
  } else if (sprite.characterState == CharacterState::Crouching) {
    for (int followerId : sprite.followers) {
      Sprite& follower = game_.getSprite(followerId);
      if (follower.characterMedium == CharacterMedium::Normal) {
        game_.getSystem<AnimationSystem>().setSpriteDirection(followerId, dir);
      }
    }
  }
}

void CharacterSystem::stopDirecting(int spriteId) {
  Sprite& sprite = game_.getSprite(spriteId);

  if (sprite.characterState == CharacterState::Walking) {
    setPartyState(spriteId, CharacterState::Still);
  }
}

void CharacterSystem::tick(double dt) {
  if (game_.isGameplayPaused()) return;

  inputTimer_.accumulate(dt);
  while (inputTimer_.step()) {
    for (int spriteId : game_.getSprites()) {
      Sprite& sprite = game_.getSprite(spriteId);

      if (sprite.orientable && !sprite.paused) {
        vec2i pLoc = sprite.loc;

        if (sprite.characterState == CharacterState::Still ||
            sprite.characterState == CharacterState::Crouching) {
          continue;
        }

        // Make sure this matches up with the code in adjustPartyTrails().
        int speed = sprite.movementSpeed;
        if (sprite.characterState == CharacterState::Running && sprite.characterMedium == CharacterMedium::Normal) {
          speed *= 2;
        }

        pLoc += (unitVecInDirection(sprite.movementDir) * speed);

        // Check collision.
        CollisionResult collision = game_.getSystem<TransformSystem>().checkCollision(spriteId, sprite.loc, pLoc, sprite.movementDir, CheckCollisionOptions::AllowSliding);

        if (!(collision.blocked && sprite.clipping)) {
          pLoc = collision.adjustedLoc;
        }

        for (int colliderSpriteId : collision.colliders) {
          Sprite& collider = game_.getSprite(colliderSpriteId);

          if (!collider.walkthroughScript.empty()) {
            game_.getSystem<ScriptSystem>().runScript(game_.getMap().getName(), collider.walkthroughScript);
          }

          if (!sprite.bumpPlayerScript.empty()) {
            bool bumpedPlayer = collider.player;

            if (!bumpedPlayer && collider.leaderId != -1) {
              Sprite& colliderLeader = game_.getSprite(collider.leaderId);
              bumpedPlayer = colliderLeader.player;
            }

            if (bumpedPlayer) {
              game_.getSystem<ScriptSystem>().runScript(game_.getMap().getName(), sprite.bumpPlayerScript);
            }
          }
        }

        if (collision.blocked && sprite.characterState == CharacterState::Running && !sprite.clipping) {
          stopRunning(spriteId);
          game_.getMixer().playSound("../res/sfx/bump.wav");
        }

        // Move everything
        if (pLoc != sprite.loc) {
          game_.getSystem<TransformSystem>().moveSprite(spriteId, pLoc);

          CharacterMedium newMedium = game_.getSystem<TransformSystem>().getMediumAtPosition(spriteId, pLoc);
          if (newMedium != sprite.characterMedium) {
            sprite.characterMedium = newMedium;
            setAnimationFor(spriteId, sprite.characterState);
            adjustPartyTrails(spriteId);

            // Stop running if you go into water.
            if (newMedium == CharacterMedium::Water &&
                sprite.characterState == CharacterState::Running) {
              stopRunning(spriteId);
            }
          }

          if (sprite.characterState == CharacterState::Running) {
            const Map& map = game_.getMap();

            vec2i newMapTileLoc = pLoc / map.getTileSize();
            StepType newTileStep = map.getStepType(newMapTileLoc.x(), newMapTileLoc.y());
            if (sprite.stepType != newTileStep) {
              stopRunningSound(sprite);

              sprite.stepType = newTileStep;

              if (newTileStep != StepType::none) {
                sprite.runningSfxChannel = game_.getMixer().loopSound(runningSfxForStepType(newTileStep));
              }
            }
          }

          for (int followerId : sprite.followers) {
            Sprite& pNext = game_.getSprite(followerId);
            const Movement& posdir = pNext.trail.front();
            game_.getSystem<TransformSystem>().moveSprite(followerId, posdir.pos);
            game_.getSystem<AnimationSystem>().setSpriteDirection(followerId, posdir.dir);
            if (posdir.medium != pNext.characterMedium) {
              pNext.characterMedium = posdir.medium;
              setAnimationFor(followerId, sprite.characterState);
            }

            pNext.trail.pop_front();
            pNext.trail.push_back({.pos = pLoc, .dir = sprite.dir, .medium = sprite.characterMedium});
          }
        }
      }
    }
  }
}

void CharacterSystem::beginCrouch(int spriteId) {
  Sprite& sprite = game_.getSprite(spriteId);

  if (sprite.characterState == CharacterState::Running) {
    stopRunning(spriteId);
  } else {
    if (sprite.characterMedium == CharacterMedium::Ladder ||
        sprite.characterMedium == CharacterMedium::Water ||
        sprite.cantCrouch) {
      return;
    }

    setPartyState(spriteId, CharacterState::Crouching);
  }
}

void CharacterSystem::endCrouch(int spriteId) {
  Sprite& sprite = game_.getSprite(spriteId);

  if (sprite.characterState == CharacterState::Crouching) {
    startRunning(spriteId);
  }
}

void CharacterSystem::startRunning(int spriteId) {
  setPartyState(spriteId, CharacterState::Running);
  adjustPartyTrails(spriteId);
}

void CharacterSystem::stopRunning(int spriteId) {
  Sprite& sprite = game_.getSprite(spriteId);
  setPartyState(spriteId, CharacterState::Still);
  stopRunningSound(sprite);
  adjustPartyTrails(spriteId);
}

void CharacterSystem::setPartyState(int spriteId, CharacterState state) {
  Sprite& sprite = game_.getSprite(spriteId);
  sprite.characterState = state;

  setAnimationFor(spriteId, state);
  for (int followerId : sprite.followers) {
    setAnimationFor(followerId, state);
  }
}

void CharacterSystem::stopRunningSound(Sprite& sprite) {
  sprite.stepType = StepType::none;
  if (sprite.runningSfxChannel != -1) {
    game_.getMixer().stopChannel(sprite.runningSfxChannel);
    sprite.runningSfxChannel = -1;
  }
}

void CharacterSystem::halt(int spriteId) {
  // Because special stuff happens when we stop running, we have to handle the
  // running case here.
  Sprite& sprite = game_.getSprite(spriteId);
  if (sprite.characterState == CharacterState::Running) {
    stopRunning(spriteId);
  } else {
    // Other than that, it is simple to go to Still from Walking or Crouching.
    setPartyState(spriteId, CharacterState::Still);
  }
}

void CharacterSystem::destroySprite(int spriteId) {
  Sprite& sprite = game_.getSprite(spriteId);
  if (sprite.runningSfxChannel != -1) {
    stopRunningSound(sprite);
  }
}

void CharacterSystem::setAnimationFor(int spriteId, CharacterState state) {
  Sprite& sprite = game_.getSprite(spriteId);
  std::string animName;

  switch (sprite.characterMedium) {
    case CharacterMedium::Normal: {
      std::string animName;

      switch (state) {
        case CharacterState::Still: {
          animName = "still";
          break;
        }
        case CharacterState::Walking: {
          animName = "walk";
          break;
        }
        case CharacterState::Crouching: {
          animName = "crouch";
          break;
        }
        case CharacterState::Running: {
          animName = "run";
          break;
        }
      }

      game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, animName);
      sprite.hasShadow = sprite.normallyHasShadow;
      sprite.bobbing = sprite.bobsWhenNormal;

      break;
    }
    case CharacterMedium::Ladder: {
      game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, "climb");
      sprite.hasShadow = false;
      sprite.bobbing = false;

      if (state == CharacterState::Still || state == CharacterState::Crouching) {
        sprite.animPaused = true;
      } else {
        sprite.animPaused = false;
      }

      break;
    }
    case CharacterMedium::Water: {
      std::string animName = "swim_still";
      if (state == CharacterState::Walking) {
        animName = "swim_walk";
      }

      game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, animName);
      sprite.hasShadow = false;
      sprite.bobbing = false;

      break;
    }
  }
}

void CharacterSystem::adjustPartyTrails(int spriteId) {
  Sprite& sprite = game_.getSprite(spriteId);

  // Trails should be halved if the effective moving speed is twice the normal
  // speed, which right now is whenever you are running and not on a ladder.
  bool shouldBeHalved = false;
  if (sprite.characterState == CharacterState::Running && sprite.characterMedium == CharacterMedium::Normal) {
    shouldBeHalved = true;
  }

  if (sprite.trailsAreHalved != shouldBeHalved) {
    sprite.trailsAreHalved = shouldBeHalved;

    if (shouldBeHalved) {
      // Halve the movement buffer for the followers.
      for (int followerId : sprite.followers) {
        Sprite& follower = game_.getSprite(followerId);
        std::deque<Movement> newMove;

        while (!follower.trail.empty()) {
          newMove.push_back(follower.trail.front());
          follower.trail.pop_front();
          follower.trail.pop_front();
        }

        follower.trail = std::move(newMove);
      }
    } else {
      // Double the movement buffer for the followers.
      for (int followerId : sprite.followers) {
        Sprite& follower = game_.getSprite(followerId);

        std::deque<Movement> newMove;
        vec2i lastPos = follower.loc;

        while (!follower.trail.empty()) {
          Movement m1 = follower.trail.front();
          Movement m2 = m1;
          m1.pos = (m1.pos + lastPos) / 2;
          lastPos = m2.pos;

          newMove.push_back(m1);
          newMove.push_back(m2);
          follower.trail.pop_front();
        }

        follower.trail = std::move(newMove);
      }
    }
  }
}