Yiran Xuan – Final Project – Dueling Dragons

sketch

/*
Yiran Xuan
Section A
yxuan@andrew.cmu.edu
Final Project
*/

var redframes = []; // array for red dragon sprites
var blueframes = []; //array for blue dragon sprites

var beat; //sound variables
var impact;
var fire;
var growl;
var charge;
/*
status key:
0 = default
1 = attack
2 = shield
3 = reload
4 = impact
5 = shielded impact
6 = death (not real status)
*/

//---------------------------------------
function preload(){ //load all sprites into respective arrays
    //each sprite's index in array should match the status it represents
    //ex) the red dragon's default sprite should have index of zero
    /*
    redframes.push(loadImage("./DragonDefault.png"));  
    blueframes.push(loadImage("./DragonDefault2.png"));

    redframes.push(loadImage("./DragonAttack.png"));  
    blueframes.push(loadImage("./DragonAttack2.png"));

    redframes.push(loadImage("./DragonShield.png"));  
    blueframes.push(loadImage("./DragonShield2.png"));

    redframes.push(loadImage("./DragonReload.png"));  
    blueframes.push(loadImage("./DragonReload2.png"));

    redframes.push(loadImage("./DragonImpact.png"));  
    blueframes.push(loadImage("./DragonImpact2.png"));

    redframes.push(loadImage("./DragonShieldedImpact.png"));  
    blueframes.push(loadImage("./DragonShieldedImpact2.png"));

    redframes.push(loadImage("./DragonDeath.png"));  
    blueframes.push(loadImage("./DragonDeath2.png"));  

    beat = loadSound("rockyoubeat.wav"); //import sounds
    impact = loadSound("Strong_Punch.wav");
    fire = loadSound("Flame.wav");
    growl = loadSound("Growling Lion.wav");
    charge = loadSound("lasercharge.wav");
    */
    
    //default
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDefault.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDefault2.png")); 
    //attack
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonAttack.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonAttack2.png"));
    //shield
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShield.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShield2.png"));
    //reload
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonReload.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonReload2.png"));
    //impact
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonImpact.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonImpact2.png"));
    //shielded impact
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShieldedImpact.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShieldedImpact2.png"));
    //death
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDeath.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDeath2.png"));  

    beat = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/rockyoubeat.wav"); //import sounds
    impact = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/Strong_Punch.wav");
    fire = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/Flame.wav");
    growl = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/Growling-Lion.wav");
    charge = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/lasercharge.wav");
    

}

//--------------------------------------- Game Process functions

var RedDragon;
var BlueDragon;

var secondsbeforereload = 3;
var deadframecounter = 0; //after the endgame, counts number of frames before the game restarts

var cycle = 24; //number of frames in a cycle
var secondspercycle = 1; //how long one cycle
var framerate = cycle/secondspercycle;
var framecounter = 0; //counts frames to control the cycle


var actionwindow = cycle*2/3; //window for player input

function setup() {
    createCanvas(600, 400);
    imageMode(CENTER);
    
    textStyle(BOLD);
    textAlign(CENTER);
    frameRate(framerate);

    RedDragon = createDragon(3, 0, 1, redframes); //start with one health and no ammo
    BlueDragon = createDragon(3, 0, -1, blueframes);    

}

var redturn = true; //becomes false once a player has made move, ensures each player only makes one action per round
var blueturn = true;

function draw() {
    background('white');
    
    if(RedDragon.death || BlueDragon.death){ //if one dragon had died, just display default renders
        GameEnd();
    }

    else{
        if(framecounter < actionwindow){ //Stage 1: allowing player action input, displaying default renders
            fill('green');
            RedDragon.DefaultRender();
            BlueDragon.DefaultRender();
        }

        else if (framecounter == actionwindow){ //Stage 2: processing player actions
            fill('red');
            RedDragon.Action();
            BlueDragon.Action();
            RedDragon.opponentaction = BlueDragon.currentaction;
            BlueDragon.opponentaction = RedDragon.currentaction;
        }
        else if (framecounter < cycle*5/6){ //Stage 3: display player actions
            fill('red');
            RedDragon.ActionRender();
            BlueDragon.ActionRender();
        }
        else if (framecounter == cycle*5/6){ //Stage 4: process opponent actions and end result
            fill('red');
            RedDragon.Result();
            BlueDragon.Result();
        }
        else{ //Stage 5: display result of interaction
            fill('red');
            RedDragon.ResultRender();
            BlueDragon.ResultRender();
        }

        framecounter++; //framecounter is here music stops after end of the game
    }

    textSize(30);
    fill('green');
    text("Health: " + RedDragon.health, RedDragon.x, RedDragon.y - 75); //display current healths
    text("Health: " + BlueDragon.health, BlueDragon.x, BlueDragon.y - 75); 


    textSize(15);
    fill('red');
    text("J to attack, K to shield, L to reload", RedDragon.x, RedDragon.y - 125); //display current healths
    fill('blue');
    text("A to attack, S to shield, D to reload", BlueDragon.x, BlueDragon.y - 125);


    if (framecounter == cycle){ //when cycle is complete
        framecounter = 0;
        redturn = true; //allows players action again
        blueturn = true; 
        RedDragon.Reset(); //and reset round variables (currentmove, opponentmove, status) 
        BlueDragon.Reset();
        beat.play();//beat starts playing here
    }
}


//--------------------------------------- Event functions

function GameEnd(){
    
    RedDragon.DefaultRender(); //display the dragons; DefaultRender contains death render as well
    BlueDragon.DefaultRender();

    var winner = "Nobody"; //default is draw 
    if(RedDragon.health > BlueDragon.health){ //comparing health to determine victor
        winner = "Red Dragon";
    }
    if(RedDragon.health < BlueDragon.health){ 
        winner = "Blue Dragon";
    }

    fill('black');
    textSize(50);
    text(winner + " Wins!", width/2, 75); //writing victory text

    deadframecounter++; 
    if(deadframecounter == framerate*secondsbeforereload){ //reloads entire page after 3 seconds to restart game
        location.reload();
        }

}


function keyPressed(){
    if(key == 'a' || key == 's' || key == 'd'){ //is the pressed key a valid blue action?
        if(framecounter < actionwindow & blueturn){ //is it within the action window, and has blue made a move already?
            switch(key){
                case 'a':
                    BlueDragon.currentaction = 1; //A to fire
                    break;
                case 's':
                    BlueDragon.currentaction = 2; //S to shield
                    break;
                case 'd':
                    BlueDragon.currentaction = 3; //D to reload
                    break;
            }
            blueturn = false; //blue has spent its move
        }
    }

    if(key == 'j' || key == 'k' || key == 'l'){ //is the pressed key a valid red action?
        if(framecounter < actionwindow & redturn){ //is it within the action window, and has blue made a move already?
            switch(key){
                case 'j':
                    RedDragon.currentaction = 1; //A to fire
                    break;
                case 'k':
                    RedDragon.currentaction = 2; //S to shield
                    break;
                case 'l':
                    RedDragon.currentaction = 3; //D to reload
                    break;
            }
            redturn = false; //red has spent its move
        }
    }
}

//--------------------------------------- Dragon creation functions

function createDragon(startinghealth, startingammo, drdirection, drsprites) {
    var dragon = {
        
        health: startinghealth,
        death: 0,
        ammo: startingammo,
        shield: false,
        status: 0, 
        currentaction: 0, //action key: 0 = nonaction, 1 = shoot, 2 = shield, 3 = reload
        opponentaction: 0,

        direction: drdirection,
        sprites: drsprites,
        x: width/2 + drdirection*150, //where to render the sprite
        y: 225,

        Action: dragonAction, //processing gameplay
        Result: dragonResult,
        Reset: dragonReset,
        GameOver: isAlive,

        ActionRender: dragonActionRender,
        ResultRender: dragonResultRender,
        DefaultRender: dragonDefaultRender };
    return dragon;
}

/*
status key:
0 = default
1 = attack
2 = shield
3 = reload
4 = impact
5 = shielded impact
6 = death (not real status)
*/

function dragonAction(){ //function to process dragon's action
    switch(this.currentaction){ //processing currentaction
        case 0: //if no action
            break;

        case 1: //if attacking
            if(this.ammo == 0){ 
                this.currentaction = 0; //if attempted to attack without ammo, will change to no action
            }
            else{
                this.ammo--;
                fire.play();
            }
            break;

        case 2: //if shielding
            this.shield = true;
            break;

        case 3: //if reloading
            this.ammo++; //can reload indefinitely; reloads even if attacked
            charge.play();
            break;
    }

    this.ActionRender();
}


function dragonResult(){//function to process result of both dragon's actions
    this.status = this.currentaction; //by default, status is the same as action

    if(this.opponentaction == 1){ //unless attacked by other dragon
        if(this.shield){
            this.status = 5; //shield is up, attack is blocked
            impact.play();
        }
        else{
            this.status = 4;
            this.health--; //otherwise, attack hits, takes damage
            impact.play();
        }
    }
    this.GameOver();
    this.ResultRender();
}

function isAlive(){ //checks if dragon is alive or dead
    if (this.health < 1){
        this.death = 1;
        growl.play();
    }
    else this.death = 0;
}


function dragonActionRender(){ //function to display the dragon
    image(this.sprites[this.currentaction], this.x, this.y, 300, 225);
}

function dragonResultRender(){ //function to display the dragon
    image(this.sprites[this.status], this.x, this.y, 300, 225);
}

function dragonDefaultRender(){
    image(this.sprites[this.death*6], this.x, this.y, 300, 225); //renders either alive or dead state

}

function dragonReset(){ //resets the dragon actions and status to default at the end of the round
    this.currentaction = 0;
    this.status = 0;
    this.opponentaction = 0;
    this.shield = false;
}

(Using 1 Grace Day.)

(Recommend clicking the game to start it)

Dueling Dragons is the virtual, 1-computer-2-players version of the playground game “Shotgun”, in which players attempt to defeat their opponent by playing one of 3 moves (shoot, shield, reload) in accordance to a beat, usually clapping twice.

In my version, two dragons are fighting each other while “We Will Rock You”s stomp-stomp-clap plays; The final clap is starts a short time window in which both players can perform an action using the keys displayed.

Each player starts with 3 health and no ammo. Health is displayed, but ammo isn’t, for tactical reasons.

A player can make 4 moves:
0) no action (don’t press a key or press it out of the timing window); left vulnerable
1) attack; will expend an ammo only if there is any, otherwise will become no action
2) shield; protects the player from damage
3) reload; increases ammo by one, can increase indefinitely. A reload is still successful even if the player is damaged.

If both players are attacked, both of them will suffer damage

The game continues until at least one player’s health drops to zero, after which a win screen appears for 3 seconds, and then the page attempts to refresh (recommend manually refreshing the page)

Of course, there are sound effects!

Leave a Reply