Sunday, May 4, 2014

Things That Didn't Work

With any project, there are ideas that I was not able to get to work in the actual programming. These are the things I have been unable to do so far:

1. Perfectly sync the magic - sometimes magic will appear in random places when casting the spell, from the last magic spell.

2. Victory/Defeat game music - I haven't yet been able to 'stop' music (the .stop() or .close() functions haven't worked for me) in order to have a separate theme for the victory and defeat screen.

Thursday, May 1, 2014

Sword

The sword works differently from the magic spells. Press 's' on the key to use the sword. When using the sword, the wand is redrawn to look like a sword.

  void display()
  {
    
   if (type == 5)
   {
     stroke(200);
    strokeWeight(10);
    line(x,y+20,x+swing,y-50);
    if (edge == 0)
    {
      line(x-10,y,x+10,y);
    }
    else if (edge == 1)
    {
      line(x-30,y+10,x-10,y-10);
    }
    else
    {
      line(x+30,y+10,x+10,y-10);
    }
   

   }

Unlike the wand, the sword can move across the entire screen, as no code is called to move it. To use the sword, you simply click on the mouse. 


void mousePressed()
{  
  if (!cast)
  {
    if (type == 5)
    {
         cast = !cast;
         
        if (level == 1)
       {     
        if (goblin.swing())
        {
          goblin.impact(); 
        }
       }
       
       else if (level == 2)
       {
        if (snake.swing())
        {
          snake.impact(); 
        }
         
       }
       else if (level == 3)
       {
        if (wizard.swing())
        {
          wizard.impact(); 
        }
         
       }
      else if (level == 4)
       {
        if (dragon.swing())
        {
          dragon.impact(); 
        }
         
       }
    }


If the mouse (sword) is over the creature, it calls the impact function.


  boolean swing()
  {
    if ((mouseX >= x - 50) && (mouseX <= x + 50) && (mouseY >= y - 50) && (mouseY <= y + 50))
      {
        return true;
      }
      else
      {
        return false;
      }
      
  }


 else if (type == 5)
    {
       userSound.play();
      parry = random(1,10);
      if (parry < parryChance)
      {
        hit = random(1,5) * armor;
        damage = round(hit);
        creatureHP -= damage;
      }
      else
      {
       if (!parryText)
      {
       parryText = !parryText;
      if (low)
      {
       low = !low; 
      }
      if (missText)
      {
       missText = !missText; 
      }
      if (attaText)
      {
        attaText = !attaText;
      }
       if (magiText)
      {
       magiText = !magiText; 
      }
      } 
        
      }
    }

When a creature is hit by the sword, it can parry the blow (the higher the level, the greater the chance of parry is). It then deals the appropriate amount of damage to the creature.


  void swing()
  {
    if (cast)
    {
      if (random(-1,1) < 0)
      {
        swing -= 70;
        edge = 1;

      }
      else
      {
        swing += 70;
        edge = 2;

      }
      cast = !cast;
    }
    else
    {
     swing = 0; 
     edge = 0;
    }
  }

Finally, the sword has a 50/50 chance of 'swinging' in either direction when used.
  



Falling Energy

In order to get back spell points, you can set your wand to "catcher" mode, and catch the falling purple triangles.

The triangles are set up as an class array, meaning 500 different triangles are created.

//class Energy

  color c =  color(255,0,255);
  float x1 = random(width);
  float y1 = -30;
  float x2 = x1 + 6;
  float y2 = -30;
  float x3 = x1 + 3;
  float y3 = -25;
  float f;
  float r = 3;                      //radius? Needed for intersect calculation.
  float shiftF = 24;
  float speed = 4;

The triangle's point 1 is set at a random x location - the triangle drawing is then computed from the x1 point. The 'radius' is used for the purposes of determining if the triangle was caught.

The triangles fall down in a straight line, simply by adding the speed to the 3 'y' locations.


  void move()
  {
     y1 += speed;
     y2 += speed;
     y3 += speed; 
  }

The function energyFall() will move the triangles down, and then check to see whether the magic intersects. Note that the intersection is ignored unless the wand is set to catch mode.


  for (int i = 0; i < e; i++)
  {
   energy[i].move();
  energy[i].fade();
  energy[i].display(); 
  if ((wand.intersect(energy[i])) && (type == 7))
   {
     energy[i].caught();
  }


To determine whether the player caught a triangle, we check with a function in the Wand class. The wandX value moves the catch point to the direction the wand is pointing:


  boolean intersect(Energy e)
  {
   float distance = dist(x-wandX,y,e.x2,e.y2);
   if (distance <= r - e.r)
   {
     return true;
   }
   else
   {
    return false; 
   }
   

Finally, if the triangle is in fact caught, spell points are awarded to the player and the triangle location resets:


  void caught()
  {
    y1 = -1005;
    y2 = -1005;
    y3 = -1000;
    userSound.rewind();
    userSound.play();


    if (sp < maxSP)
    {
        sp += 5;
        if (sp > maxSP)      //Under the current rules, there's no way to obtain an SP value between maxSP and maxSP-5, 
                            //but I'm adding this in the event I want to change this later.
        {
         sp = 100; 
        }
    }
  }

    



Wednesday, April 30, 2014

Creature Attacks

The creatures in the game include a goblin, a snake, a wizard, and a dragon. The creature attacks are all located in a single function, and the attack initiated depends on the level you are on. The attacks happen   on a clock schedule.

void creatureAttack()
{
  float miss;
  float hit;
  
  passedAttackTime = millis() - savedAttackTime;
  
  if (passedAttackTime > totalAttackTime)
  …

Let's look at the goblin attack on level 1:

if (level == 1)
  {
   enemySound = minim.loadFile("SWORD.wav");
   miss = random(1,10);
   if (miss < 7)
   { 
     hit = random(5,15);
     damage = round(hit);
     hp -= damage;
     fill(255,0,0,40);
     stroke(255,0,0,40);
     rect(0,0,width,height);
     if (!attaText)
     {
      attaText = !attaText; 
     }
  }
  else
  {
     fill(255,0,0,10);
     stroke(255,0,0,10);

     rect(0,0,width,height);
     if (!missText)
     {
      missText = !missText; 
     }
  }
 }

The goblin attacks with an axe, so a 'sword' sound effect is used to represent a goblin attack. Then, a random number determines whether the goblin actually hits or misses the player. The goblin thus has a 30% chance of missing.

If the goblin hits you, a random number is generated between 5 and 15.

The round command will round the number to an integer, so you can't lose 7.33423521 HP. The damage then be deducted from the player HP.

Finally, a transparent red light will fill the screen, to represent the hit, and a boolean is set to display text in a later function. If the goblin misses, the red light is even more transparent, and a different boolean is set.

Now, let's look at the snake attack.

 else if (level == 2)
 {
   miss = random(1,10);
    enemySound = minim.loadFile("SNAKE.wav");
   if (miss < 9)
  { 
     hit = random(8,25);
     damage = round(hit);
     hp -= damage * playerResistanceP;
     fill(255,0,0,40);
     stroke(255,0,0,40);

     rect(0,0,width,height);
     if (!attaText)
     {
      attaText = !attaText; 
     }
  }


It looks identical to the goblin attack, just with different variables (more damage, less chance of missing). However, notice that hp is deducted by damage * playerResistanceP, so if a poison shield is generated (setting your wand to cast poison), it cuts the blow into half of the generated damage.

Tuesday, April 29, 2014

Credits

CREDITS:

Code-

Adam Lastowka
http://www.openprocessing.org/sketch/58970

Konkuk University
Department of Dynamic Media
http://www.openprocessing.org/sketch/144900

Graphics-

gorchakov.artem
"Gloomy Forest"
https://www.flickr.com/photos/ghor/8394379683/

E-Zara
"Desert Sand Sand Dunes"
http://pixabay.com/en/desert-sand-sand-dunes-143124/

Eric E Castro
"John Muir Glacier"
http://commons.wikimedia.org/wiki/File:John_Muir_Glacier.jpg

Photochrom Print
"Neuschwanstein Castle LOC print rotated.jpg"
http://en.wikipedia.org/wiki/File:Neuschwanstein_Castle_LOC_print_rotated.jpg

W-Dueck
"Moonless Night"

"Kalamitsi Beach, Ionian Sea, Lefkada Island, Greece"

Creatures:

evilestmark
"Goblin"
http://openclipart.org/image/800px/svg_to_png/65551/goblin.png

dakadi
"Dragon"
http://openclipart.org/image/800px/svg_to_png/141691/dd.png

nizips
"Magician"
http://openclipart.org/image/800px/svg_to_png/29503/magician.png

sirrob
"Rattlesnake"
http://openclipart.org/image/800px/svg_to_png/170138/Rattlesnake.png

Music-

"The Descent", "Crusade", "All That", "Truth of the Legends" by Kevin MacLeod
incompetech.com

Sound Effects

Erdie
"Sword04"
http://www.freesound.org/people/Erdie/sounds/27858/

Jamius
"Snake Attack, Verbal Pulse"
http://www.freesound.org/people/Jamius/sounds/41531

Julien Matthey
"JM FX Fireball"
http://www.freesound.org/people/Julien%20Matthey/sounds/105016/

Qubodup
"Poison Spell Magic"
http://www.freesound.org/people/qubodup/sounds/219566/

RICHERlandTV
"Magic"
http://www.freesound.org/people/RICHERlandTV/sounds/216089/

RobinHood76
"Dragon's Breath"
http://www.freesound.org/people/Robinhood76/sounds/66642

Timbre
"Another Magic Wand Spell Tinkle"
https://www.freesound.org/people/Timbre/sounds/221683/

Urupin
"Fast Freezing Ice"
http://www.freesound.org/people/urupin/sounds/192415/

Zixem
"Mystic 2"
https://www.freesound.org/people/Zixem/sounds/69506/








Using Magic

The bulk of the gameplay involves the player casting magic spells on opponents. The wand cannot move more than 1/3rd of the screen from the bottom. Additionally, the wand can be pointing to the northwest, north, or northeast. First, the player will select the spell to use:

void keyPressed()
{
  
  if (key == CODED)
  {
    if ((keyCode == LEFT) && (wandX < 35))
    {
      wandX += 35; 
      
    }
    else if ((keyCode == RIGHT) && (wandX > -35))
    {
      wandX -= 35; 
    }
    
  }
  else if (key == 'f')
    {
     type = 1; 
     userSound = minim.loadFile("FIRE.wav");
     magicEffect = color(255,0,0);
     magicEffect2 = color(255,100,0);
                playerResistanceM = 1;
          playerResistanceI = 1;
                          playerResistanceP = 1;

    }
   else if (key == 'i')
   {

    type = 2;
         userSound = minim.loadFile("ICE.wav");
         magicEffect = color(0,0,255);
         magicEffect2 = color(0,200,255);
                    playerResistanceM = 1;
          playerResistanceI = 0.5;
                          playerResistanceP = 1;


   }
   else if  (key == 'm')
   {
          userSound = minim.loadFile("MAGIC.wav");

     type = 3;
          magicEffect = color(155,0,155);
          magicEffect2 = color(255,0,0);
          playerResistanceM = 0.5;
          playerResistanceI = 1;
                playerResistanceP = 1;




   }
    else if (key == 'p')
    {
          userSound = minim.loadFile("ICE.wav");    //Right now, the poison file doesn't work, substituting ice sound.

      type = 4; 
      magicEffect = color(0,155,0);
      magicEffect2 = color(0,255,100);
      playerResistanceI = 1;
      playerResistanceM = 1;
      playerResistanceP = 0.5;



    }
    else if (key == 's')
    {
      type = 5;
           userSound = minim.loadFile("SWORD.wav");
      playerResistanceM = 1;
          playerResistanceI = 1;
                          playerResistanceP = 1;


      magicEffect = color(200);
    }
    else if (key == 'h')
    {
     type = 6; 
     userSound = minim.loadFile("HEAL.wav");
     magicEffect = color(255,255,0);
     magicEffect2 = color(255,255,100);
           playerResistanceM = 1;
          playerResistanceI = 1;
                          playerResistanceP = 1;

    }
    else if (key == 'c')
    {
     type = 7;
          userSound = minim.loadFile("RECHARGE.wav");

     magicEffect = color(0,255,155);
     magicEffect2 = color(0,255,255); 
           playerResistanceM = 1;
          playerResistanceI = 1;
                          playerResistanceP = 1;

      
    }
    {
    }
    
}

When the appropriate key is pressed, the user sound effect and spell color. If the player types 's' for the sword, the wand is redrawn to look like a sword. Likewise, the arrow keys move the wand direction.

When the player clicks (to use the wand), the program will check:



1. Player SP (to make sure the player has enough spell points)
2. What spell (to know what to use)



//Mouse Pressed

void mousePressed()
{  
  if (!cast)
  {
    if (type == 5)
    {
         cast = !cast;
         
        if (level == 1)
       {     
        if (goblin.swing())
        {
          goblin.impact(); 
        }
       }
       
       else if (level == 2)
       {
        if (snake.swing())
        {
          snake.impact(); 
        }
         
       }
       else if (level == 3)
       {
        if (wizard.swing())
        {
          wizard.impact(); 
        }
         
       }
      else if (level == 4)
       {
        if (dragon.swing())
        {
          dragon.impact(); 
        }
         
       }
    }
    else
    {
    
    if (!checkSP())
    {
      if (!low)
      {
          low = !low;
          if (attaText)
          {
            attaText = !attaText;
          }
          if (missText)
          {
           missText = !missText; 
          }
      }
    }
    else
    {
      if (low)
      {
        low = !low;
      }
   cast = !cast; 
   if (type == 1)
   {
        sp -= 10; 
   }
   else if (type == 2)
   {
        sp -= 10; 
   }
   else if (type == 3)
   {
        sp -= 20; 
   }
   else if (type == 4)
   {
        sp -= 5; 
   }
   else if (type == 6)
   {
     sp -= 15;
   }


If the wand is a catcher, it does nothing if clicked.

   else if (type == 7)
   {
    cast = !cast; 
   }
    }

  }
  }
}

Meanwhile, a boolean variable will check whether the mouse has been clicked. If so, it will cast the spell and reset the variable.If you are using a healing spell, the for loop is ignored and simply uses an ellipse emitting from the wand.

if (cast)
  {
     userSound.rewind();  //sound effect, see (Sound & music)
     userSound.play();
    if (type == 6)
    {
      if (hp < maxHP)
      {
      hp += 25;
      }
      if (hp > maxHP)
      {
       hp = maxHP; 
      }
      
      fill(magicEffect, 70);
      stroke(magicEffect,70);
      ellipse(castPosX,castPosY,150,150);
      cast = !cast;
    }
   else 
   {

   if (sparks <= 15) 
   {
     magic.add(new Magic(castPosX,castPosY,wandX));
   }

A for loop will then check to see if any of the magic effects hit the creature.  The design is adapted from Park Hyebin, Konkuk University. 

   for (int i = magic.size()-1; i >= 0; i--)        //Adapted from circle motion by Park Hyebin, Konkuk University - http://www.openprocessing.org/sketch/144900
    {

       Magic m = (Magic) magic.get(i);
       m.move(); 
       m.display();
       if ((level == 1) && (goblin.hit(m)))
       {
         goblin.impact();
         magic.remove(i);
         sparks++;
       }
       else if ((level == 2) && (snake.hit(m)))
       {
         snake.impact();
         magic.remove(i);
         sparks++;
       }
       else if ((level == 3) && (wizard.hit(m)))
       {
         wizard.impact();
         magic.remove(i);
         sparks++;
       }
     else if ((level == 4) && (dragon.hit(m)))
       {
         dragon.impact();
         magic.remove(i);
         sparks++;
       }
       if (m.finished())
       {
          magic.remove(i); 
          sparks++;
        }

If the creature is defeated, the for loop is ended (so the next creature can be created).

      if (defeated)
       {
        i = magic.size()-1;

        defeated = !defeated; 
       }


Monday, April 28, 2014

Game Rules

Battle of Magic: Game Rules

Objective: Defeat four creatures (goblin, snake, wizard, ice dragon)

To cast a spell, simply click on the mouse. The spell will cast if you have enough SP. The wand can only be moved in the bottom third of the screen. Use the left/right arrow keys to change directions of the wand.

PLAYER SPELLS:

Fire: Type 'f' on the keyboard to switch to the spell. The fire spell releases red pellets. Each pellet that hits a creature will do between 2 and 8 damage to the creature, before resistances. It costs 10 Spell Points.

Ice: Type 'i' on the keyboard to switch to the spell. The ice spell releases blue  pellets. Each pellet that hits a creature will do between 1 and 10 damage to the creature, before resistances. It costs 10 Spell Points. (Obviously, the difference here is it can do more damage than fire, but it can also do less.)

Magic: Type 'm' on the keyboard to switch to the spell. The general magic spell releases purple pellets. Each pellet that hits a creature will do between 5 and 25 damage to the creature, before resistances. It costs 20 spell points - the more powerful, the more draining.

Poison: Type 'p' on the keyboard to switch to the spell. The poison spell releases green pellets. Each pellet that hits a creature will do between 1 and 5 damage to the creature, before resistances. It costs 5 spell points.

Healing: Type 'h' on the keyboard to switch to the spell. The healing spell releases a yellow light, and will heal the player for 25 points. It costs 15 spell points.


Other Uses:

Sword -  Type 's' on the keyboard to switch to the spell. The sword can be used by clicking directly on the creature. It does between 1 and 8 damages to the creature, before resistances. The advantages of the sword are that, unlike the wand, it can be moved anywhere on the screen and costs no SP to use. Obviously, the disadvantage of the sword are that (1) cumulatively, there are more resistances to the sword, and (2), creatures can parry a blow.

Catcher - Small energy triangles will fall from the sky throughout the game. These can restore spell points, if you switch your wand to catcher mode and catch some (5 SP for each triangle).

(10 SP is returned to the player every several seconds)


CREATURES:

Level 1 - Goblin: The goblin is the easiest creature to defeat. He has 1500 HP, attacks with a melee weapon, and can do between 5 to 15 damage. The goblin has 20% armor, and no resistances otherwise.

Level 2 - Snake: The snake has 2000 HP and bites, doing between 8 to 25 damage. The snake is immune to poison, has a 50% resistance to fire, and a 20% resistance to ice.

Level 3 - Wizard: The wizard has 2500 HP and uses magic to attack. He is immune to magic, and resists fire and ice by 50%.

Level 4 - Ice Dragon: The most powerful creature of all, the Ice Dragon boasts 3000 HP and immunity to ice. Additionally, the ice dragon resists fire by 25%, magic by 65%, poison by 50%, and has an armor level of 70%

Note that, if you set your wand to the creature's immunity, while you can't attack, it reduces the player's blow by 50%.