/*************************************************************************** * Dog: The dude that can herd the sheep. You give commands to him. ***************************************************************************/ /*************************************************************************** * Dog: That bad guy that chases after and eats sheep. ***************************************************************************/ class Dog { /*************************************************************************** * Variables ***************************************************************************/ // List of states private static final int STATE_WAITING = 0; private static final int STATE_START_HERDING = 1; private static final int STATE_CIRCLING = 2; private static final int STATE_BARK_CIRCLING = 3; private static final int STATE_BACK_TO_WAITING = 4; private static final int STATE_GO_GET_SHEEP = 5; private static final int STATE_BRING_BACK_SHEEP = 6; // Info about icon private PImage m_img, m_barkImg; private float m_scale; // Location and movement private int m_x, m_y, m_direction; private int m_rotation; private float m_speed, m_maxSpeed; // List of all the sheep private ArrayList m_sheepList; int m_numActiveSheep; // Herding variables private int m_numSheepReadyToCircle; private int m_circleStart_x, m_circleStart_y; private int m_circleStart_radius; // Circling variables private int m_circlingPos; private int m_timeSpentBarking; private float m_dogSpace; private int m_timeSpentCircling; private static final int TIME_ALLOWED_CIRCLING = 240; private static final int MAX_TIME_SPENT_BARKING = 10; private Wolf m_wolf; // Bringing back individual sheep private Sheep m_bringBackSheep; // Current state private int m_currentState; /*************************************************************************** * Constructor ***************************************************************************/ public Dog(String imgLoc, String imgBarkLoc, float scale, int x, int y, ArrayList sheepList, int numSheep) { // Load and save the image m_img = loadImage(imgLoc); m_barkImg = loadImage(imgBarkLoc); m_scale = scale; // Starting position m_x = x; m_y = y; m_direction = 0; m_rotation = 0; m_speed = 0; // List of all the sheep m_sheepList = sheepList; m_numActiveSheep = numSheep; m_numSheepReadyToCircle = 0; m_circleStart_x = m_circleStart_y = m_circleStart_radius = -1; m_circlingPos = -1; m_timeSpentBarking = -1; m_timeSpentCircling = -1; m_wolf = null; m_dogSpace = m_img.height*m_scale/ 1; //1.5; m_bringBackSheep = null; // state m_currentState = STATE_WAITING; } /*************************************************************************** * Getters/setters ***************************************************************************/ public void setMaxSpeed(float s) { m_maxSpeed = s; } public void setDirection(int d) { m_direction = d; } public int getX() { return m_x; } public int getY() { return m_y; } public boolean isWaiting() { return m_currentState == STATE_WAITING; } public boolean isCircling() { return m_currentState == STATE_CIRCLING || m_currentState == STATE_BARK_CIRCLING; } public void oneLessActiveSheep() { m_numActiveSheep--; } /*************************************************************************** * Drawing function ***************************************************************************/ public void draw() { // Special drawing when circling if (m_currentState == STATE_CIRCLING || m_currentState == STATE_BARK_CIRCLING) { PImage img = m_currentState == STATE_CIRCLING ? m_img : m_barkImg; imageMode(CENTER); // Rotate the image according to the current position on circle float rotation = radians(m_circlingPos - 270); translate(m_x, m_y); rotate(rotation); image(img, 0, 0, (m_img.width*m_scale), (m_img.height*m_scale)); rotate(-rotation); translate(-m_x, -m_y); } else { imageMode(CORNER); image(m_img, m_x, m_y, (m_img.width*m_scale), (m_img.height*m_scale)); } //TEMP if (false) { noFill(); stroke(255,0,0); ellipse(m_circleStart_x, m_circleStart_y, 2*m_circleStart_radius, 2*m_circleStart_radius); } } /*************************************************************************** * Moving function ***************************************************************************/ public void move() { //println("Current state: " + m_currentState); if (m_currentState == STATE_WAITING) { m_x = windowW/2 - (int)(m_img.width * m_scale/2); m_y = windowH - (spaceBelowSheep/2); } else if (m_currentState == STATE_BACK_TO_WAITING) { moveBackToWaiting(); } else if (m_currentState == STATE_START_HERDING) { moveToHerdingStart(); } else if (m_currentState == STATE_CIRCLING) { moveCircling(); } else if (m_currentState == STATE_BARK_CIRCLING) { moveBarking(); } else if (m_currentState == STATE_GO_GET_SHEEP) { moveToBringBackSheep(); } else if (m_currentState == STATE_BRING_BACK_SHEEP) { moveBringBackSheep(); } } /*************************************************************************** * Head back to waiting position ***************************************************************************/ private void moveBackToWaiting() { int finalX = windowW/2 - (int)(m_img.width * m_scale/2); int finalY = windowH - (spaceBelowSheep/2); float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); int direction = (int)(degrees(atan2(finalY - m_y, finalX - m_x))); // If within a certain distance of the final destination, just go // straight for it float distance = dist(finalX, finalY, m_x, m_y); if ((direction - m_direction > 45) || distance <= 5) { m_x = finalX; m_y = finalY; startWaiting(); return; } // Make the x/y changes needed m_x += (int)changeInX; m_y += (int)changeInY; m_direction = direction; } /*************************************************************************** * Head toward to the top of the herding circle ***************************************************************************/ private void moveToHerdingStart() { int finalX = m_circleStart_x - (int)(m_img.width/2 * m_scale); int finalY = m_circleStart_y-m_circleStart_radius - (int)(m_img.height/2 * m_scale); float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); float direction = (int)(degrees(atan2(finalY - m_y, finalX - m_x))); // If within a certain distance of the final destination, just go // straight for it float distance = dist(finalX, finalY, m_x, m_y); if ((direction - m_direction > 45) || distance <= 5) { m_x = finalX + (int)(m_img.width/2*m_scale); m_y = finalY + (int)(m_img.height/2*m_scale); startCircling(); return; } // Make the x/y changes needed m_x += (int)changeInX; m_y += (int)changeInY; } /*************************************************************************** * Run around the circle ***************************************************************************/ private void moveCircling() { if (m_numSheepReadyToCircle >= m_numActiveSheep) m_timeSpentCircling++; if (m_timeSpentCircling > TIME_ALLOWED_CIRCLING) { callDogBack(); return; } // Go around the circle some more m_circlingPos = (int)(m_circlingPos - m_speed/2) % 360; // Figure out the x/y coordinates and the orientation at this position m_x = (int)(cos(radians(m_circlingPos)) * m_circleStart_radius + m_circleStart_x); m_y = (int)(sin(radians(m_circlingPos)) * m_circleStart_radius + m_circleStart_y); // Randomly adjust the speed every once in a while if (random(1.0) < 0.05) { m_speed = random(m_maxSpeed-0.5, m_maxSpeed); } } /*************************************************************************** * Push the circle in bark mode ***************************************************************************/ private void moveBarking() { if (m_numSheepReadyToCircle >= m_numActiveSheep) m_timeSpentCircling++; if (m_timeSpentCircling > TIME_ALLOWED_CIRCLING) { callDogBack(); return; } m_timeSpentBarking++; if (m_timeSpentBarking >= MAX_TIME_SPENT_BARKING) { stopBarking(); return; } float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); // Check if the move would put the circle past one of the boundaries float x1 = m_circleStart_x + changeInX - (m_circleStart_radius-m_dogSpace); float x2 = m_circleStart_x + changeInX + (m_circleStart_radius-m_dogSpace); float y1 = m_circleStart_y + changeInY - (m_circleStart_radius-m_dogSpace); float y2 = m_circleStart_y + changeInY + (m_circleStart_radius-m_dogSpace); if (x1 >= sheepBoundaryX1 && x2 <= sheepBoundaryX2 && y1 >= sheepBoundaryY1 && y2 <= sheepBoundaryY2) { m_x += changeInX; m_y += changeInY; m_circleStart_x += changeInX; m_circleStart_y += changeInY; } else { stopBarking(); return; } } /*************************************************************************** * Head toward the sheep that we need to retrieve ***************************************************************************/ private void moveToBringBackSheep() { if (m_bringBackSheep.isDeadOrDying()) { callDogBack(); return; } int finalX = m_bringBackSheep.getX(); int finalY = m_bringBackSheep.getY(); m_direction = (int)(degrees(atan2(finalY - m_y, finalX - m_x))); float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); // If within a certain distance of the final destination, just go // straight for it float distance = dist(finalX, finalY, m_x, m_y); if (distance <= 5) { m_x = finalX; m_y = finalY; doneGettingSheep(); return; } // Make the x/y changes needed m_x += (int)changeInX; m_y += (int)changeInY; } /*************************************************************************** * Head toward the sheep that we need to retrieve ***************************************************************************/ private void moveBringBackSheep() { int w = (int)(m_img.width * m_scale); int h = (int)(m_img.height * m_scale); int finalX = width/2 - m_bringBackSheep.getSize()/2; int finalY = height - spaceBelowSheep - wallWidth; m_direction = (int)(degrees(atan2(finalY - m_y, finalX - m_x))); float changeInX = m_speed * cos(radians(m_direction)); float changeInY = m_speed * sin(radians(m_direction)); // If within a certain distance of the final destination, just go // straight for it float distance = dist(finalX, finalY, m_x, m_y); if (distance <= m_speed) { m_x = finalX; m_y = finalY; doneBringingBackSheep(); return; } // Check that we don't walk over the walls with the sheep if (abs(m_y + changeInY + m_bringBackSheep.getSize() - (height - spaceBelowSheep - wallWidth)) < 5) { if (m_x < wallLengthBottom || m_x + m_bringBackSheep.getSize() > width-wallLengthBottom) { m_direction = (m_x > width/2) ? 180 : 0; changeInX = m_speed * cos(radians(m_direction)); changeInY = m_speed * sin(radians(m_direction)); } } // Make the x/y changes needed m_x += (int)changeInX; m_y += (int)changeInY; } /*************************************************************************** * Start waiting for commands. ***************************************************************************/ public void startWaiting() { if (m_currentState != STATE_BACK_TO_WAITING) { println("Can't go straight to waiting - go through BACK_TO_WAITING first."); return; } m_currentState = STATE_WAITING; } /*************************************************************************** * Start herding sheep. ***************************************************************************/ public void startHerding() { if (m_currentState != STATE_WAITING) return; // Tell each sheep to get into start herding mode. This will get them // to bunch together into a circle. They will tell the dog when they // are ready for circling. m_speed = m_maxSpeed * 3; m_numSheepReadyToCircle = 0; m_currentState = STATE_START_HERDING; m_timeSpentCircling = -1; // The center of the herding circle can be chosen randomly, and the radius // depends on how many sheep there are int sheepSize = ((Sheep)(m_sheepList.get(0))).getSize(); int sheepArea = sheepSize * sheepSize; m_circleStart_radius = (int)(sqrt(sheepArea * m_sheepList.size() / PI) + m_dogSpace); m_circleStart_x = (int)random(sheepBoundaryX1+m_circleStart_radius, sheepBoundaryX2-m_circleStart_radius); m_circleStart_y = (int)random(sheepBoundaryY1+m_circleStart_radius, sheepBoundaryY2-m_circleStart_radius); int finalX = m_circleStart_x - (int)(m_img.width/2 * m_scale); int finalY = m_circleStart_y-m_circleStart_radius - (int)(m_img.height/2 * m_scale); m_direction = (int)(degrees(atan2(finalY - m_y, finalX - m_x))); for (int s=0; s < m_sheepList.size(); s++) { Sheep sheep = (Sheep)m_sheepList.get(s); if (!sheep.isDeadOrDying() && !sheep.isDoneOrGettingDone()) { sheep.startHerding(m_circleStart_x, m_circleStart_y, m_circleStart_radius - (int)(m_dogSpace)); } } } /*************************************************************************** * A new sheep is ready to circle. ***************************************************************************/ public void sheepReadyToCircle(Sheep sheep) { m_numSheepReadyToCircle++; if (m_numSheepReadyToCircle >= m_numActiveSheep) m_timeSpentCircling = 0; } /*************************************************************************** * Start circling. ***************************************************************************/ private void startCircling() { m_currentState = STATE_CIRCLING; m_circlingPos = 270; m_speed = m_maxSpeed; if (m_wolf != null) { m_wolf.startHerding(); } } /*************************************************************************** * How much time left for circling? ***************************************************************************/ public int timeLeftForCircling() { if ((m_currentState == STATE_CIRCLING || m_currentState == STATE_BARK_CIRCLING) && m_numSheepReadyToCircle >= m_numActiveSheep) { return (TIME_ALLOWED_CIRCLING - m_timeSpentCircling) / frameRate; } else { return -1; } } /*************************************************************************** * Is the given location within the bounds of the dog's circling radius? ***************************************************************************/ public boolean withinCirclingRadius(int x, int y) { return (dist(x,y, m_circleStart_x, m_circleStart_y) < m_circleStart_radius + 10); } /*************************************************************************** * Does the given line cross the circling radius? ***************************************************************************/ public boolean crossingCirclingRadius(int x1, int y1, int x2, int y2) { return false; } /*************************************************************************** * Bark button pressed ***************************************************************************/ public void barkButtonPressed() { if (m_currentState == STATE_CIRCLING) { startBarking(); } } /*************************************************************************** * Start bark ***************************************************************************/ private void startBarking() { if (m_currentState != STATE_CIRCLING || m_numSheepReadyToCircle < m_numActiveSheep) { return; } m_currentState = STATE_BARK_CIRCLING; m_timeSpentBarking = 0; // Move the circle in the direction between the dog's current location // and the circle's current center m_direction = (int)(degrees(atan2(m_circleStart_y - m_y, m_circleStart_x - m_x))); // Tell the sheep we started barking for (int s=0; s < m_sheepList.size(); s++) { Sheep sheep = (Sheep)m_sheepList.get(s); sheep.startBark(m_direction, m_speed); } } /*************************************************************************** * Stop bark ***************************************************************************/ private void stopBarking() { m_currentState = STATE_CIRCLING; // Tell the sheep we stopped barking for (int s=0; s < m_sheepList.size(); s++) { Sheep sheep = (Sheep)m_sheepList.get(s); sheep.stopBark(m_circleStart_x, m_circleStart_y, m_circleStart_radius - (int)m_dogSpace); } } /*************************************************************************** * Stop dog from going whatever he is doing and bring him back to waiting ***************************************************************************/ public void callDogBack() { if (m_currentState == STATE_START_HERDING) { for (int s=0; s < m_sheepList.size(); s++) { Sheep sheep = (Sheep)m_sheepList.get(s); sheep.stopHerding(); } if (m_wolf != null) { m_wolf.stopHerding(); } } else if (m_currentState == STATE_CIRCLING) { for (int s=0; s < m_sheepList.size(); s++) { Sheep sheep = (Sheep)m_sheepList.get(s); sheep.stopCircling(); } if (m_wolf != null) { m_wolf.stopHerding(); } } else if (m_currentState == STATE_BARK_CIRCLING) { for (int s=0; s < m_sheepList.size(); s++) { Sheep sheep = (Sheep)m_sheepList.get(s); sheep.stopCircling(); } } else if (m_currentState == STATE_BRING_BACK_SHEEP && m_bringBackSheep != null) { // This means the dog was called back in the middle of bringing back the sheep m_bringBackSheep.cancelBringingBack(); m_bringBackSheep = null; } m_speed = m_maxSpeed * 2; m_timeSpentCircling = 0; int finalX = windowW/2 - (int)(m_img.width * m_scale/2); int finalY = windowH - (spaceBelowSheep/2); m_direction = (int)(degrees(atan2(finalY - m_y, finalX - m_x))); m_currentState = STATE_BACK_TO_WAITING; } /*************************************************************************** * A wolf has arrived! ***************************************************************************/ public void wolfArrived(Wolf theWolf) { m_wolf = theWolf; } /*************************************************************************** * Wolf is gone again. ***************************************************************************/ public void wolfLeft() { m_wolf = null; } /*************************************************************************** * Retrieve the specified sheep ***************************************************************************/ public void goGetSheep(Sheep s) { if (m_currentState != STATE_WAITING) return; m_currentState = STATE_GO_GET_SHEEP; m_speed = m_maxSpeed; m_bringBackSheep = s; } /*************************************************************************** * Done getting the sheep - now what? ***************************************************************************/ private void doneGettingSheep() { bringBackSheep(); } /*************************************************************************** * Bring back the sheep ***************************************************************************/ private void bringBackSheep() { m_currentState = STATE_BRING_BACK_SHEEP; m_speed = m_maxSpeed; m_bringBackSheep.startBringingBack(); } /*************************************************************************** * Done getting the sheep - now what? ***************************************************************************/ private void doneBringingBackSheep() { m_bringBackSheep.doneBringingBack(); m_bringBackSheep = null; callDogBack(); } }// end class Dog