/*************************************************************************** * Sheep : Those cute little white guys that you have to round up. ***************************************************************************/ class Sheep { /*************************************************************************** * Variables ***************************************************************************/ // List of states private static final int STATE_RANDOM_WALK = 0; private static final int STATE_DIE = 1; private static final int STATE_RUN_FROM_WOLF = 2; private static final int STATE_DEAD = 3; private static final int STATE_START_HERDING = 4; private static final int STATE_CIRCLING = 5; private static final int STATE_BARK_CIRCLING = 6; private static final int STATE_BRING_BACK = 7; private static final int STATE_GETTING_DONE = 8; private static final int STATE_DONE = 9; // Info about icon private PImage m_img; private PImage m_deadImg; private float m_scale; // Location and movement private int m_x, m_y, m_direction; private float m_speed, m_maxSpeed; // List of all the sheep and other important things private ArrayList m_sheepList; private Dog m_dog; private Wolf m_wolf; // Current state private int m_currentState; // Time before dying or being saved private int m_timeBeforeDead; private int m_timeBeforeDone; // Herding and circling variables private int m_herdTo_x, m_herdTo_y; private int m_circlingX, m_circlingY, m_circlingR; private int m_timeEscapingWolf; private boolean m_escapingWolf; private static final int MAX_TIME_ESCAPING_WOLF = 25; /*************************************************************************** * Constructor ***************************************************************************/ public Sheep(String imgLoc, String deadImgLoc, float scale, int x, int y, ArrayList sheepList, Dog dog) { // Load and save the images m_img = loadImage(imgLoc); m_deadImg = loadImage(deadImgLoc); m_scale = scale; // Starting position m_x = x; m_y = y; m_direction = 0; m_speed = 0; m_sheepList = sheepList; m_dog = dog; m_wolf = null; m_currentState = STATE_RANDOM_WALK; m_timeBeforeDead = -1; m_timeBeforeDone = -1; m_herdTo_x = m_herdTo_y = -1; m_circlingX = m_circlingY = m_circlingR = -1; m_timeEscapingWolf = 0; m_escapingWolf = false; move(); } /*************************************************************************** * Getters/setters ***************************************************************************/ public void setMaxSpeed(float s) { m_maxSpeed = s; } public void setDirection(int d) { m_direction = d; } public int getSize() { return (int)(max(m_img.width, m_img.height) * m_scale); } public int getX() { return m_x; } public int getY() { return m_y; } public int getHerdToX() { return m_herdTo_x; } public int getHerdToY() { return m_herdTo_y; } /*************************************************************************** * Drawing function ***************************************************************************/ public void draw() { if (m_currentState == STATE_DIE) { imageMode(CORNER); tint(255, max(m_timeBeforeDead,0)); image(m_deadImg, m_x, m_y, (m_deadImg.width*m_scale), (m_deadImg.height*m_scale)); tint(255,255); m_timeBeforeDead -= 10; if (m_timeBeforeDead <= 0) { m_currentState = STATE_DEAD; } } else if (m_currentState == STATE_GETTING_DONE) { imageMode(CORNER); tint(255, m_timeBeforeDone); image(m_img, m_x, m_y, (m_img.width*m_scale), (m_img.height*m_scale)); tint(255,255); m_timeBeforeDone -= 10; if (m_timeBeforeDone <= 0) { m_currentState = STATE_DONE; } } else if (m_currentState != STATE_DONE && m_currentState != STATE_DEAD) { imageMode(CORNER); image(m_img, m_x, m_y, (m_img.width*m_scale), (m_img.height*m_scale)); } } /*************************************************************************** * Drawing function that includes a letter on top of the sheep ***************************************************************************/ public void draw(char letter) { draw(); if (!isDeadOrDying() && !isDoneOrGettingDone()) { fill(180,100,37); textSize(24); text(letter, m_x - 5, m_y - 5); } } /*************************************************************************** * Check the state of the sheep and move it accordingly. ***************************************************************************/ public void move() { if (m_currentState == STATE_RANDOM_WALK) { moveRandom(); } else if (m_currentState == STATE_RUN_FROM_WOLF) { moveAwayFromWolf(); } else if (m_currentState == STATE_START_HERDING) { moveToHerdPosition(); } else if (m_currentState == STATE_CIRCLING) { moveCircling(); } else if (m_currentState == STATE_BARK_CIRCLING) { moveBarking(); } else if (m_currentState == STATE_BRING_BACK) { moveBringBack(); } else if (m_currentState == STATE_GETTING_DONE) { moveGettingDone(); } } /*************************************************************************** * Move the sheep in its current direction at its speed. Check for hitting * boundaries, and change direction randomly. ***************************************************************************/ private void moveRandom() { float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); m_x += (int)changeInX; m_y += (int)changeInY; // Adjust direction for any boundary hitting if (!checkBoundaries()) { // Otherwise, we will want to randomly decide to change direction some small random amount. if (random(1.0) < 0.05) { m_direction = (int)(m_direction + random(30,50)) % 360; } } // Randomly adjust the speed every once in a while if (random(1.0) < 0.05) { m_speed = random(0, m_maxSpeed); } } /*************************************************************************** * Try to move away from the wolf in general ***************************************************************************/ private void moveAwayFromWolf() { float distance = m_wolf.getDistanceFromCenter(m_x + (int)(m_img.width*m_scale/2), m_y + (int)(m_img.height*m_scale/2)); float angle = m_wolf.getAngleFromCenter(m_x + (int)(m_img.width*m_scale/2), m_y + (int)(m_img.height*m_scale/2)); // Currently escaping wolf if (m_escapingWolf) { m_timeEscapingWolf++; float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); m_x += (int)changeInX; m_y += (int)changeInY; if (checkBoundaries() || m_timeEscapingWolf > MAX_TIME_ESCAPING_WOLF) { m_escapingWolf = false; m_timeEscapingWolf = 0; } } // Close if (distance < 150 && !m_escapingWolf) { m_escapingWolf = true; m_speed = m_maxSpeed; float changeInX = 0; float changeInY = 0; int numTries = 0; int maxNumTries = 50; do { numTries++; m_direction = (int)random(angle-45, angle+45) % 360; changeInX = m_speed * cos(radians(m_direction)); changeInY = m_speed * sin(radians(m_direction)); } while (checkBoundaries((int)changeInX, (int)changeInY) && numTries < maxNumTries); // If can't get out, probably stuck on a wall/corner if (numTries >= maxNumTries) { // Left hand side if (abs(m_x - sheepBoundaryX1) < 10) { // Top corner if (abs(m_y - sheepBoundaryY1) < 10) { m_direction = 0; } // Bottom Corner else if (abs(sheepBoundaryY2 - getSize() - m_y) < 10) { m_direction = 0; } // Along wall else { m_direction = random(1) < 0.5 ? 90 : 270; } } // Right hand side else if (abs(m_x + getSize() - sheepBoundaryX1) < 10) { // Top corner if (abs(m_y - sheepBoundaryY1) < 10) { m_direction = 90; } // Bottom corner else if (abs(m_y + getSize() - sheepBoundaryY2) < 10) { m_direction = 180; } // Along wall else { m_direction = random(1) < 0.5 ? 90 : 270; } } // top/bottom walls else { m_direction = random(1) < 0.5 ? 0 : 180; } changeInX = m_speed * cos(radians(m_direction)); changeInY = m_speed * sin(radians(m_direction)); } m_x += (int)changeInX; m_y += (int)changeInY; checkBoundaries(); } // Not close else { moveRandom(); } } /*************************************************************************** * Move the sheep towards its herding position. ***************************************************************************/ private void moveToHerdPosition() { m_direction = (int)(degrees(atan2(m_herdTo_y - m_y, m_herdTo_x - m_x))); float distance = dist(m_herdTo_x, m_herdTo_y, m_x, m_y); if (distance <= m_circlingR /*- getSize()*/) { startCircling(); return; } // Make the x/y changes needed float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); m_x += (int)changeInX; m_y += (int)changeInY; } /*************************************************************************** * As they are circling, the sheep should move around a bit randomly. Also, * the circle may move. ***************************************************************************/ private void moveCircling() { float distance = dist(m_herdTo_x, m_herdTo_y, m_x, m_y); if (distance > m_circlingR/2) { // Head back m_direction = (int)(degrees(atan2(m_herdTo_y - m_y, m_herdTo_x - m_x))); } else if (random(1.0) < 0.08) { m_direction = (int)(m_direction + random(-10,10)) % 360; } // Randomly adjust the speed every once in a while if (random(1.0) < 0.05) { m_speed = random(0, m_maxSpeed); } // Make the x/y changes needed float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); m_x += (int)changeInX; m_y += (int)changeInY; checkBoundaries(); } /*************************************************************************** * In this state, it's just a slide in the right direction. No random * movements. ***************************************************************************/ private void moveBarking() { float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); m_x += (int)changeInX; m_y += (int)changeInY; // Are we near the opening of the pen? If so, we should be herded if (m_x-getSize()/3 >= wallLengthBottom && m_x + getSize() + getSize()/3 <= width - wallLengthBottom) { if ((height - spaceBelowSheep - wallWidth) - (m_y + getSize()) <= 30) { doneBringingBack(); return; } } checkBoundaries(); } /*************************************************************************** * Dog is bringing back the sheep, so just follow it ***************************************************************************/ private void moveBringBack() { m_x = m_dog.getX(); m_y = m_dog.getY(); } /*************************************************************************** * Last bit of fading animation to show sheep being saved ***************************************************************************/ private void moveGettingDone() { m_direction = 90; float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); m_x += (int)changeInX; m_y += (int)changeInY; } /*************************************************************************** * Just check whether the given x,y coords hit a boundary ***************************************************************************/ private boolean checkBoundaries(int dx, int dy) { boolean hitBoundary = false; // Left boundary if (m_x + dx < sheepBoundaryX1) { hitBoundary = true; } // Right boundary if (m_x + dx + m_scale*m_img.width > sheepBoundaryX2) { hitBoundary = true; } // Top boundary if (m_y + dy < sheepBoundaryY1) { hitBoundary = true; } // Bottom boundary if (m_y + dy + m_scale*m_img.height > sheepBoundaryY2) { hitBoundary = true; } return hitBoundary; } /*************************************************************************** * Check the boundaries, and adjust the direction accordingly ***************************************************************************/ private boolean checkBoundaries() { boolean hitBoundary = false; // Left boundary if (m_x < sheepBoundaryX1) { m_x = sheepBoundaryX1; hitBoundary = true; } // Right boundary if (m_x + m_scale*m_img.width > sheepBoundaryX2) { m_x = sheepBoundaryX2 - (int)(m_scale*m_img.width); hitBoundary = true; } // Top boundary if (m_y < sheepBoundaryY1) { m_y = sheepBoundaryY1; hitBoundary = true; } // Bottom boundary if (m_y + m_scale*m_img.height > sheepBoundaryY2) { m_y = sheepBoundaryY2 - (int)(m_scale*m_img.height); hitBoundary = true; } // Change direction if the sheep hit the boundary if (hitBoundary) { m_direction += 45; // Add some noise m_direction += (int)(random(10)); // Keep in range m_direction = m_direction % 360; } return hitBoundary; } /*************************************************************************** * Does the sheep collide with a given set of boundaries? ***************************************************************************/ public boolean collides(int x1, int x2, int y1, int y2) { // int x1Me = m_x; // int x2Me = m_x + (int)(m_img.width * m_scale); // // int y1Me = m_y; // int y2Me = m_y + (int)(m_img.height * m_scale); // // if ((x1Me >= x1 && x1Me <= x2 && y1Me >= y1 && y1Me <= y2) || // (x2Me >= x1 && x2Me <= x2 && y2Me >= y1 && y2Me <= y2)) // { // return true; // } // else // { // return false; // } int xMe = m_x + (int)(0.5 * m_img.width * m_scale); int yMe = m_y + (int)(0.5 * m_img.height * m_scale); return (xMe > x1 && xMe < x2 && yMe > y1 && yMe < y2); } /*************************************************************************** * Get the angle from the location to the sheep ***************************************************************************/ public int getAngle(int x, int y) { float rads = atan2(m_y - y, m_x - x); return (int)(degrees(rads)); } /*************************************************************************** * A wolf has entered the scene! ***************************************************************************/ public void wolfArrived(Wolf wolf) { if (!isDeadOrDying() && !isDoneOrGettingDone()) { if (m_currentState == STATE_RANDOM_WALK) { m_currentState = STATE_RUN_FROM_WOLF; } m_timeEscapingWolf = 0; m_escapingWolf = false; m_wolf = wolf; } } /*************************************************************************** * Wolf is gone. ***************************************************************************/ public void wolfLeft(Wolf wolf) { if (m_currentState == STATE_RUN_FROM_WOLF) { m_currentState = STATE_RANDOM_WALK; } m_wolf = null; } /*************************************************************************** * Time to die ***************************************************************************/ public void die() { // Can't die when with the dog, or if already dead ;) if (m_currentState != STATE_DIE && m_currentState != STATE_DEAD && m_currentState != STATE_BRING_BACK && m_currentState != STATE_GETTING_DONE && m_currentState != STATE_DONE) { // Go into die state m_currentState = STATE_DIE; m_timeBeforeDead = 270; draw(); } } /*************************************************************************** * Check if dead ***************************************************************************/ public boolean isDead() { return (m_currentState == STATE_DEAD); } public boolean isDeadOrDying() { return (m_currentState == STATE_DEAD || m_currentState == STATE_DIE); } /*************************************************************************** * Start the herding behaviour by heading to (x,y). Inform the dog when done. ***************************************************************************/ public void startHerding(int x, int y, int r) { if (!isDeadOrDying() && !isDoneOrGettingDone()) { m_currentState = STATE_START_HERDING; setHerdToLocation(x,y,r); m_speed = m_maxSpeed + 1.0; } } /*************************************************************************** * Stop herding. ***************************************************************************/ public void stopHerding() { if (!isDeadOrDying() && !isDoneOrGettingDone()) { if (m_wolf != null) m_currentState = STATE_RUN_FROM_WOLF; else m_currentState = STATE_RANDOM_WALK; m_timeEscapingWolf = 0; m_escapingWolf = false; } } /*************************************************************************** * Start the circling behaviour after herding is done. ***************************************************************************/ public void startCircling() { if (!isDeadOrDying() && !isDoneOrGettingDone()) { m_currentState = STATE_CIRCLING; m_dog.sheepReadyToCircle(this); } } /*************************************************************************** * No longer circling. Go back to random walk (unless there's a wolf?) ***************************************************************************/ public void stopCircling() { if (!isDeadOrDying() && !isDoneOrGettingDone()) { if (m_wolf != null) m_currentState = STATE_RUN_FROM_WOLF; else m_currentState = STATE_RANDOM_WALK; m_timeEscapingWolf = 0; m_escapingWolf = false; } } /*************************************************************************** * Find good herd to locations. ***************************************************************************/ public void setHerdToLocation(int circleX, int circleY, int circleR) { if (isDeadOrDying() || isDoneOrGettingDone()) { return; } int newX, newY; boolean foundGoodCoords; m_circlingX = circleX; m_circlingY = circleY; m_circlingR = circleR; int bounds_x1 = circleX - circleR; int bounds_x2 = circleX + circleR - getSize(); int bounds_y1 = circleY - circleR; int bounds_y2 = circleY + circleR - getSize(); do { newX = (int)random(bounds_x1, bounds_x2); newY = (int)random(bounds_y1, bounds_y2); foundGoodCoords = false; // Check if in the circle if (dist(newX + getSize()/2, newY + getSize()/2, circleX, circleY) <= circleR - getSize()/2) { foundGoodCoords = true; } // Check if too close to other sheep if (foundGoodCoords) { for (int ss=0; ss < m_sheepList.size(); ss++) { Sheep innerSheep = (Sheep)m_sheepList.get(ss); if (!innerSheep.isDeadOrDying() && !innerSheep.isDoneOrGettingDone()) { if (innerSheep.getHerdToX() >= 0 && innerSheep.getHerdToY() >= 0 && dist(newX, newY, innerSheep.getHerdToX(), innerSheep.getHerdToY()) < getSize()/2) { foundGoodCoords = false; break; } } } } } while(!foundGoodCoords); m_herdTo_x = newX; m_herdTo_y = newY; // Adjust the direction to suit the new go-to location m_direction = (int)(degrees(atan2(m_herdTo_y - m_y, m_herdTo_x - m_x))); } /*************************************************************************** * When the dog barks, the sheep will just do a slide movement in the given * direction. ***************************************************************************/ public void startBark(int dir, float speed) { if (!isDeadOrDying() && !isDoneOrGettingDone()) { m_direction = dir; m_speed = speed; m_currentState = STATE_BARK_CIRCLING; } } /*************************************************************************** * The dog's done barking, so reset the circling behaviour ***************************************************************************/ public void stopBark(int circleX, int circleY, int circleR) { if (!isDeadOrDying() && !isDoneOrGettingDone()) { setHerdToLocation(circleX, circleY, circleR); m_currentState = STATE_CIRCLING; m_timeEscapingWolf = 0; m_escapingWolf = false; } } /*************************************************************************** * The dog is bringing back the sheep ***************************************************************************/ public void startBringingBack() { if (isDeadOrDying() || isDoneOrGettingDone()) { return; } m_currentState = STATE_BRING_BACK; } /*************************************************************************** * No longer bringing back the sheep - back to roaming or wolf running ***************************************************************************/ public void cancelBringingBack() { if (isDeadOrDying() || isDoneOrGettingDone()) { return; } if (m_wolf == null) m_currentState = STATE_RANDOM_WALK; else m_currentState = STATE_RUN_FROM_WOLF; } /*************************************************************************** * The dog's done bringing the sheep back. This sheep is now done and * will no longer be affected by the goings on of the game. ***************************************************************************/ public void doneBringingBack() { if (isDeadOrDying() || isDoneOrGettingDone()) { return; } //if (m_currentState != STATE_GETTING_DONE) { m_timeBeforeDone= 330; draw(); } m_currentState = STATE_GETTING_DONE; } /*************************************************************************** * Is the sheep done because it was brought back? ***************************************************************************/ public boolean isDone() { return m_currentState == STATE_DONE; } public boolean isDoneOrGettingDone() { return m_currentState == STATE_DONE || m_currentState == STATE_GETTING_DONE; } }// end class Sheep