/*
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!