Final Project

sketch
/* Evan Stuhfire
 * estuhlfi@andrew.cmu.edu section B
 * Project-14: Municiple Water Management Game
*/

// map repair state variables
var rsGreen = 0;
var rsYellow = 1;
var rsOrange = 2;
var rsRed = 3;

// variable to track dam cracks
var dYellow = false;
var dOrange = false;
var dRed = false;

// map facility icons to text
var fDam = "Dam";
var fFiltration = "Filter";
var fPumping = "Pump";
var fHydro = "Hydro\nPower";

// Array to hold facility icons
var iconArray = [];

// Array to hold house objects
var houseArray = [];
var numHouses = 5;
// arrays to store house coordinates
var houseX = [35, 80, 140, 210, 190];
var houseY = [315, 335, 310, 285, 330];

// global worker object
var workerObj;

// player stats, number of fixes
var statsObj;
// control for directions page
var gameStart = true;

function drawIcon() {
    fill(this.repairColor);
    // Draw both the tray and the facility icons
    drawIconHelper(this.trayx, this.trayy, 
        this.traySize, this.facilityIcon, 13);
    drawIconHelper(this.facilityx, this.facilityy,
        this.facilitySize, this.facilityIcon, 10);
}

function drawIconHelper(x, y, size, label, txs) {
    push();
    translate(x, y);
    ellipse(0, 0, size);

    strokeWeight(1);
    fill(10);
    textAlign(CENTER);
    textSize(txs);
    text(label, 0, -3);
    pop();
}

function drawWorker() {
    fill(255);
    // draw circle to contain the worker
    ellipse(this.wx, this.wy, this.size);

    // draw stickman worker
    drawStickFigure(this.wx, this.wy, this.headSize);
    textAlign(CENTER);
    textSize(12);
    fill(255);
    strokeWeight(1);
    text("1. Click the worker", this.wx, this.wy + 50);
}

function drawStickFigure(x, y, h) {
    fill(255);
    strokeWeight(1.5);
    // body
    line(x, y - 15, x, y + 20);
    // legs
    line(x, y + 20, x - 10, y + 30);
    line(x, y + 20, x + 10, y + 30);
    // arms
    line(x, y + 10, x -10, y + 15);
    line(x, y + 10, x + 10, y + 15);

    // head
    circle(x, y - 15, h);

    // hat
    fill(255, 255, 0);
    arc(x, y - 20, 30, 33, PI, 2 * PI, OPEN);
    arc(x, y -25, 30, 15, 0, PI, CHORD);

    // eyes
    line(x - 5, y - 15, x - 5, y - 12);
    line(x + 5, y - 15, x + 5, y - 12);
    // mouth
    line(x - 5, y - 5, x + 5, y - 5);
}

function drawHouse() {
    // draw a house 
    // brick red
    fill(153, 0, 0);
    rect(this.hx, this.hy, this.hw, this.hh);

    // draw roof
    fill(50); // grey for roof
    triangle(this.hx, this.hy, this.hx + this.hw/2, this.hy - 15, 
        this.hx + this.hw, this.hy);

    // draw windows
    if(this.power == true) {
        fill(255, 255, 153); // yellow for lit windows 
    } else {
        fill(5); // black for unlit windows, no power
    }
   
    rect(this.hx + 5, this.hy + 5, 10, 12);
    rect(this.hx + 25, this.hy + 5, 10, 12);

    // draw door
    fill(153, 102, 51); // brown for door
    rect(this.hx + 15, this.hy + 15, 10, 15);
}

function houseState() {
    // change power to house based on probability of 
    // an outage, chances increae with higher repair state
    var chanceOfOutage = 0;
    var highState = 0;

    // find highest repair state of hydro power
    for(i = 0; i < iconArray.length; i++) {
        var currentIcon = iconArray[i];
        if(currentIcon.facilityIcon.slice(0, 1) == "H" &
         currentIcon.repairState > highState) {
            highState = currentIcon.repairState;
        }
    }

    if(highState >= rsOrange & this.power == false){
        return;
    }

    // green repair state all houses have power
    switch (highState) {
    case rsGreen:
        chanceOfOutage = 0;
        break;
    case rsYellow:
        // yellow repair state, probability of outage = low
        chanceOfOutage = .005;
        break;
    case rsOrange:
        // orange repair state, probability of outage = medium
        chanceOfOutage = .01;
        break;
    case rsRed:
        // red repair state, probability of outage = high
        chanceOfOutage = .5
        break;
    }
    if(random(0, 1) < chanceOfOutage) {
        this.power = false; // outage
    } else {
        this.power = true; // no outage
    }
}

function changeState() {
    if(gameStart == true) {
        return;
    }

    repairFacility(this);

    // generate a problem based on likelyhood
    if(random(0, 1) < this.problemLikely) {
        this.ageOfState++;
        // increase likelihood of problems over time
        this.problemLikely += .002;
    }


    if(this.ageOfState > 15 & this.repairState == rsYellow) {
        // set state and color to orange
        // reset ageOfState and increase likelyhood of problem

        this.repairState = rsOrange; 
        this.repairColor = color(255, 102, 0);
        this.ageOfState = 0; 
        this.problemLikely = .015;        
    } else if(this.ageOfState > 10 & this.repairState == rsOrange) {
        // set state and color to red
        // reset ageOfState and increase likelyhood of problem

        this.repairState = rsRed;
        this.repairColor = color(204, 0, 0);
        this.ageOfState = 0;
        this.problemLikely = .02; 

        if(this.facilityIcon.slice(0, 1) == "D" ||
            this.facilityIcon.slice(0, 1) == "P") {
            statsObj.floods++;
        } else if(this.facilityIcon.slice(0, 1) =="F"){
            statsObj.filterFail++;
        } else {
            statsObj.powerLoss++;
        }
    } else if(this.ageOfState > 5 & this.repairState == rsGreen) {
        // set state and color to yellow
        // reset ageOfState and increase likelyhood of problem

        this.repairState = rsYellow;
        this.repairColor = color(255, 255, 0);
        this.ageOfState = 0;
        this.problemLikely = .01;
    } 

    // Check for failing services
    if(this.facilityIcon.slice(0, 1) == "D") {
        damFailure(this);
    } else if(this.facilityIcon.slice(0, 1) == "P" &
        this.repairState == rsRed) {
        pumpFailure(this);
    } else if(this.facilityIcon.slice(0,1) == "F" &
        this.repairState == rsRed) {
        filterFailure(this);
    }
}

function makeIconObj(tx, ty, fx, fy, fac) {
    // object def for game icons
    var icon = {trayx: tx,
                trayy: ty,
                facilityx: fx,
                facilityy: fy,
                traySize: 50,
                facilitySize: 35,
                facilityIcon: fac, 
                repairState: rsGreen,
                repairColor: color(0, 200, 0), 
                ageOfState: 0,
                problemLikely: .05,
                trayClick: false,
                facillityClick: false,
                drawFunction: drawIcon,
                stateFunction: changeState
                }
    return icon;
}

function makeWorker(x, y) {
    // object def for worker
    var icon = {wx: x,
                wy: y,
                size: 80,
                headSize: 30,
                clicked: false,
                drawFunction: drawWorker
                }
    return icon;
}

function makeHouse(x, y) {
    // object def for houses
    var house = {hx: x,
                hy: y,
                hw: 40, // house width
                hh: 30, // house height
                power: true,
                drawFunction: drawHouse,
                stateFunction: houseState}

    return house;
}

function makeStats() {
    // object definition for game stats
    var stats = {maxRepairs: 10,
                maxAge: 60,
                repairs: 0,
                floods: 0,
                filterFail: 0,
                powerLoss: 0}

    return stats;
}

function createIcons() {
    // create icons for equipment tray
    iconArray.push(makeIconObj(200, 430, 295, 140, fDam));
    iconArray.push(makeIconObj(270, 430, 60, 217, fFiltration));
    iconArray.push(makeIconObj(340, 430, 397, 300, fPumping));
    iconArray.push(makeIconObj(410, 430, 305, 235, fHydro));

    // create worker icon
    workerObj = makeWorker(70, 425);

    // create game stats object
    statsObj = makeStats();
}   

function createHouses() {
    // make numHouses number of houses
    for(i = 0; i < numHouses; i++) {
        houseArray[i] = makeHouse(houseX[i], houseY[i]);
    }    
}

function setup() {
    createCanvas(480, 480);
    createIcons();
    createHouses();
}

function draw() {
    background(204, 239, 255);
    drawScene();

    // draw houses
    for(j = 0; j < houseArray.length; j++) {
        var currentHouse = houseArray[j];
        currentHouse.stateFunction();
        currentHouse.drawFunction();
    }


    // draw game icons
    var cIcon = 10;
    for(i = 0; i < iconArray.length; i++) {
        var currentIcon = iconArray[i];

        currentIcon.stateFunction();
        currentIcon.drawFunction();

        // if the tray icon was clicked show on mouse
        if(currentIcon.trayClick == true) {
            fill(currentIcon.repairColor);

            // transform to make small icon at mouse pointer
            push();
            scale(.5);
            translate(mouseX, mouseY);
            drawIconHelper(mouseX - 70, mouseY + 30, 
                currentIcon.traySize,
                currentIcon.facilityIcon);
            pop();
        }
    }

    // draw worker icon
    workerObj.drawFunction();

    // if the worker and/or icon has been clicked,
    // draw worker/icon at mouse pointer
    if(workerObj.clicked == true) {
        push();
        // transformation to make small worker at mouse pointer
        scale(.5);
        translate(mouseX, mouseY);
        drawStickFigure(mouseX - 20, mouseY + 40, workerObj.headSize);
        pop();
    }

    drawScore();

    // at the start of game show instructions
    if(gameStart == true) {
        instructions();
    }

    // Display game stats
    // if at least maxRepairs 
    if(statsObj.repairs >= statsObj.maxRepairs || 
            iconArray[0].ageOfState >= 100) {
        gameStats();
        noLoop(); 
    }
}


function mouseClicked() {
    // start game
    if(gameStart == true) {
        gameStart = false;
        return;
    }

    // check if worker was clicked
    var d = dist(mouseX, mouseY, workerObj.wx, workerObj.wy);
    if(d < workerObj.size/2) {
        if(workerObj.clicked == false) {
            workerObj.clicked = true;
        } else {
            workerObj.clicked = false;
        }
        return;
    }

    // check if worker has been clicked
    if(workerObj.clicked == true) {
        // check if tool was clicked in equipement tray
        for(var i = 0; i < iconArray.length; i++) {
            var icon = iconArray[i];
            var d = dist(mouseX, mouseY, icon.trayx, icon.trayy);
            if(d < icon.traySize/2) {
                setTrayIcon(i);
                return;
            }

            // check if facility was clicked
            if(icon.trayClick == true) {
                var d = dist(mouseX, mouseY, icon.facilityx, icon.facilityy);
                if(d < icon.facilitySize/2) {
                    icon.facilityClick = true;
                }
            }
        }
    }
}

function gameStats() {
    // draw final game stats from statsObj
    push();

    fill(255, 255, 255, 235);
    rectMode(CENTER);
    rect(width/2, height/2, 400, 400);
    fill(5);
    textSize(24);
    strokeWeight(1);
    textAlign(CENTER);
    text("You made "+statsObj.repairs+" repairs", width/2, 75);

    textSize(16);
    text("The town flooded "+statsObj.floods+" time(s)", 
            width/2, 115);
    text("The town filtration failed "+statsObj.filterFail+" time(s)", 
            width/2, 145);
    text("The town lost all power "+statsObj.powerLoss+" time(s)", 
            width/2, 175);

    textSize(24);
    text("Refresh to Play Again", width/2, 380);

    pop();
}

function instructions() {
    // Create instruction front page
    fill(255, 255, 255, 235);
    push();
    rectMode(CENTER);
    rect(width/2, height/2, 400, 400);
    fill(5);
    textSize(24);
    strokeWeight(1);
    textAlign(CENTER);
    text("Municipal Water Management Game", width/2, 75);
 
    // create a click to start message
    text("Click Anywhere to Start", width/2, 420);

    textAlign(LEFT);
    textSize(14);

    var x = 55;
    var y = 110;
    // Display instructions
    text("Welcome! You are managing the repairs for a town's water",
        x, y);
    text("supply. If the dam or pump fail the town will flood. If the",
        x, y + 20);
    text("filtration system fails the water will be dirty. If the hydro",
        x, y + 40);
    text("electric plant falters the lights in the houses will flicker and",
        x, y + 60);
    text("go out. Circle icon colors change as urgency increases.",
        x, y + 80);

    textAlign(CENTER);
    textSize(20);
    text("To Play: Make Repairs", width/2, y + 120);

    textAlign(LEFT);
    textSize(14);
    text("1. Click the worker. The icon will appear at the mouse pointer.",
        x, y + 150);
    text("2. Click a yellow, orange, or red icon from the icon tray.",
        x, y + 180);
    text("3. Click the matching facility in need of repair.",
        x, y + 210);
    pop();
}

function setTrayIcon(index) {
    // when setting icon clicked = true, set the others = false
    for(i = 0; i < iconArray.length; i++) {
        if(i == index) {
            iconArray[i].trayClick = true;
        }else {
            iconArray[i].trayClick = false;
        }
    }
}

function drawScore() {
    fill(5);
    strokeWeight(1);
    textAlign(LEFT);
    text("Repairs made: " + statsObj.repairs, 3, 15);
}

function repairFacility(cur) {
    // reset icons, player repairs the service
    if(cur.facilityClick == true) {
        if(cur.repairState == rsGreen) {
            // there was nothing to repair
            return;
        }

        // increment number of fixes for player score
        statsObj.repairs++;

        cur.trayClick = false;
        cur.facilityClick = false;
        workerObj.clicked = false;

        // update for repaird facilities repaired
        if(cur.facilityIcon.slice(0, 1) == "D") {
            damFail = false;
        } else if(cur.facilityIcon.slice(0, 1) == "P") {
            pumpFail = false;
        } else if (cur.facilityIcon.slice(0, 1) == "F") {
            filterFail = false;
        } 

         // user clicked to repair
        // reset state to green
        cur.repairState = rsGreen;
        cur.repairColor = color(0, 200, 0);
        // reset age of facility to 0
        cur.ageOfState = 0;
        // reset probability of problem
        cur.problemLikely = .007;
        return;
    }
}

// variables to track failure
var damFail = false;
var pumpFail = false;
var filterFail = false;

function filterFailure(filter) {
    if(filter.repairState == rsRed) {
        filterFail = true;
    }
}

function pumpFailure(pump) {
    if(pump.repairState == rsRed) {
        pumpFail = true;
    }
}

function damFailure(dam) {
    if(dam.repairState == rsGreen & dYellow == true){
        // reset cracks
        dYellow = false;
        dOrange = false;
        dRed = false;
    }

    if(dam.repairState == rsRed) {
        dRed = true;
        damFail = true;
    } else if(dam.repairState == rsOrange) {
        dOrange = true;
    } else if(dam.repairState == rsYellow) {
        dYellow = true;
    }
}

function drawScene() {
    // draw horizon line
    fill(0, 102, 0);
    rect(0, height/3, width, height);

    drawStaticElements();
    drawCracks();
    drawFlood();
    drawFilterFail();
}


function drawStaticElements() {
    // draw static elements for the game background
    // function are in static.js
    drawMountains();
    drawWater(r, g, b);  
    drawDam();
    drawFiltration();
    drawPumpStation();
    drawHydro();

    drawIconTray();
}

var r = 0;
var g = 0;
var b = 204;
var ft = 0; // filter transparency

function drawFilterFail() {
    // turn water brown to show it is polluted
    // when the filter fails
    if(filterFail == true) {
        r = 134;
        g = 89;
        b = 45;
        ft = min(t + 1, 255);
    } else {
        r = 0;
        g = 0;
        b = 204;
        ft = 0; // filter transparency
    }
}

var t = 0; // variable to control transparency
function drawFlood() {

    // flood the town when the dam fails
    if(damFail == true || pumpFail == true) { 
        t = min(t + 3, 255);       
    }
    else if (damFail == false & pumpFail == false) {
        // dam is repaired decrease transparency
        // to make grass green again
        t = max(t - 3, 0);
    }
    fill(r, g, b, t);
    rect(0, height/3, width, height);
    drawStaticElements();
    drawCracks();
}

function drawCracks() {
    // draw crack in dam
    if(dYellow == true) {
    // draw small cracks
        noFill();
        strokeWeight(1);

        beginShape();
        vertex(355, 170);
        vertex(365, 162);
        vertex(370, 140);
       
        endShape();
    }

    if(dOrange == true) {
    // draw more cracks
        noFill();

        beginShape();
        vertex(350, 170);
        vertex(342, 180);
        vertex(333, 164);
        vertex(325, 168);
       
        endShape();
        strokeWeight(2);

        beginShape();
        vertex(355, 170);
        vertex(380, 180);
        vertex(392, 164);
       
        endShape();
    }

        if(dRed == true) {
        // draw many cracks
        noFill();

        beginShape();
        vertex(350, 170);
        vertex(347, 175);
        vertex(355, 182);
        vertex(358, 190);
        vertex(383, 192);
        vertex(400, 194);
        vertex(425, 205);
       
        endShape();
        strokeWeight(3);

        beginShape();
        vertex(345, 170);
        vertex(340, 160);
        vertex(325, 155);
        vertex(315, 152);
       
        endShape();
    }

    strokeWeight(1);
}

// Functions to draw static game elements

function drawIconTray() {
    fill(230);
    textAlign(LEFT);
    textSize(12);
    strokeWeight(1);
    text("Icon Tray", 165, 395);
    text("2. Click icon from tray          3. Click matching facility", 
        165, 475);
    rect(165, 400, 285, 60);
}

function drawHydro() {
    // draw hydro electric power
    fill(179, 179, 204); 
    beginShape(); 
    vertex(255, 220); 
    vertex(365, 237);
    vertex(365, 254); 
    vertex(255, 237); 
    endShape(CLOSE); 

    // draw top
    beginShape();
    vertex(255, 220);
    vertex(320, 200);
    vertex(388, 208);
    vertex(365, 237);
    endShape(CLOSE);;

    // draw side
    beginShape();
    vertex(365, 237);
    vertex(388, 208);
    vertex(388, 215);
    vertex(365, 254);
    endShape();
}

function drawPumpStation() {
    // draw pumpStation
    fill(179, 179, 204);
    rect(width * .75, 275, 75, 50);

    // pump station top
    beginShape();
    vertex(width *.75, 275);
    vertex(width *.75 + 15, 265);
    vertex(width * .75 + 90, 265);
    vertex(width * .75 + 75, 275);
    endShape(CLOSE);

    // pump station side
    beginShape();
    vertex(width * .75 + 90, 265);
    vertex(width * .75 + 90, 310);
    vertex(width * .75 + 75, 325);
    vertex(width * .75 + 75, 275);
    endShape(CLOSE);

    // Draw pipe
    beginShape();
    vertex(width * .75, 285);
    vertex(width * .75 - 30, 285);
    vertex(width * .75 - 30, 325);
    vertex(width * .75 - 15, 325);
    vertex(width * .75 - 15, 300);
    vertex(width * .75, 300);
    endShape(CLOSE);
}

function drawFiltration() {
    // draw filtration faciity
    fill(179, 179, 204);
    rect(width/14, height/2.5, 50, 50);

    // Draw top
    beginShape();
    vertex(width/14, height/2.5);
    vertex(width/14 + 20, height/2.5 - 10);
    vertex(width/14 + 70, height/2.5 - 10);
    vertex(width/14 + 50, height/2.5);
    endShape(CLOSE);

    // Draw side
    beginShape();
    vertex(width/14 + 70, height/2.5 - 10);
    vertex(width/14 + 70, height/2.5 + 40);
    vertex(width/14 + 50, height/2.5 + 50);
    vertex(width/14 + 50, height/2.5);
    endShape(CLOSE);

    // Draw pipe 1
    ellipse(100, 202, 10, 10);
    beginShape();
    vertex(98, 198);
    vertex(130, 200);
    vertex(130, 240);
    vertex(125, 240);
    vertex(125, 205);
    vertex(98, 205);
    endShape(CLOSE);

    // Draw pipe 2
    ellipse(95, 222, 10, 10);
    beginShape();
    vertex(93, 218);
    vertex(113, 218);
    vertex(113, 245);
    vertex(107, 245);
    vertex(107, 225);
    vertex(93, 225);
    endShape(CLOSE);
}

function drawWater(r, g, b) {
    // draw water as a multipoint curve vertex shape
    fill(r, g, b);
    beginShape();

    curveVertex(425, 100);
    curveVertex(280, 120);
    curveVertex(320, 200);
    curveVertex(250, 240);
    curveVertex(0, 260);
    curveVertex(0, 300);
    curveVertex(270, 280);
    curveVertex(470, 150);

    endShape(CLOSE);
}

function drawDam() {
    var x = width * .55;
    var y = height/3;

    fill(179, 179, 204);

    // draw face of dam
    beginShape();
    vertex(x, y + 50);
    vertex(x + 60, y + 45);
    vertex(x + 90, y + 50);
    vertex(x + 120, y + 55);
    vertex(x + 150, y + 70);
    vertex(x + 185, y + 90);
    vertex(x + 185, y -20);
    vertex(x, y - 50);
    endShape(CLOSE);

    // draw top ledge
    beginShape();
    vertex(x, y - 50);
    vertex(x + 30, y - 55);
    vertex(x + 215, y - 25);
    vertex(x + 185, y - 20)
    endShape();

    // draw right side
    beginShape();
    vertex(x + 185 , y - 20);
    vertex(x + 215, y - 25);
    vertex(x + 215, y + 85);
    vertex(x + 185, y + 90)
    endShape();
}

function drawMountains() {
    // set origin to 0, horizon line
    push();
    translate(0, height/3);

    // draw curves for background mountains
    fill(71, 71, 107);
    beginShape();
    vertex();
    vertex(-20, 20);
    vertex(0, -125);
    vertex(45, -80);
    vertex(120, -155);
    vertex(200, -85);
    vertex(250, -135);
    vertex(290, -90);
    vertex(360, -140);
    vertex(440, -40);
    vertex(500, -120);
    vertex(510, 0);
    endShape(CLOSE);

    // draw curves for mid range mountains
    fill(153, 153, 150);
    beginShape();
    vertex(-30, 30);
    vertex(30, -65);
    vertex(75, -30);
    vertex(120, -90);
    vertex(165, -35);
    vertex(210, -65);
    vertex(255, -25);
    vertex(310, -80);
    vertex(395, 25);
    endShape(CLOSE);

    // draw curves for near mountains
    fill(0, 153, 51);
    beginShape();
    curveVertex(-30, 60);
    curveVertex(0, -5);
    curveVertex(50, -15);
    curveVertex(95, -5);
    curveVertex(135, -20);
    curveVertex(175, -5);
    curveVertex(220, -15);
    curveVertex(275, 10);
    curveVertex(300, 25)
    endShape(CLOSE);

    pop();
}


Leave a Reply