diff --git a/assets/Gumball_Font.png b/assets/font/Gumball_Font.png similarity index 100% rename from assets/Gumball_Font.png rename to assets/font/Gumball_Font.png diff --git a/assets/Gumball_Font.xml b/assets/font/Gumball_Font.xml similarity index 100% rename from assets/Gumball_Font.xml rename to assets/font/Gumball_Font.xml diff --git a/assets/font/SkinnyFont.png b/assets/font/SkinnyFont.png new file mode 100644 index 0000000..2fac59f Binary files /dev/null and b/assets/font/SkinnyFont.png differ diff --git a/assets/font/SkinnyFont.xml b/assets/font/SkinnyFont.xml new file mode 100644 index 0000000..3f32955 --- /dev/null +++ b/assets/font/SkinnyFont.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/atari-classic.png b/assets/font/atari-classic.png similarity index 100% rename from assets/atari-classic.png rename to assets/font/atari-classic.png diff --git a/assets/atari-classic.xml b/assets/font/atari-classic.xml similarity index 100% rename from assets/atari-classic.xml rename to assets/font/atari-classic.xml diff --git a/assets/gumball_talking.png b/assets/gumball_talking.png new file mode 100644 index 0000000..2172077 Binary files /dev/null and b/assets/gumball_talking.png differ diff --git a/assets/json/dialog.json b/assets/json/dialog.json index ab9c52a..b297942 100644 --- a/assets/json/dialog.json +++ b/assets/json/dialog.json @@ -1,9 +1,46 @@ [ - { - "speaker": "gumball", - "dialog" : "poop", - "newSpeaker": "true" - } - + [ + { + "speaker" : "gumball", + "dialog" : "I've trapped us inside a video game.", + "newSpeaker" : "true" + }, + + { + "speaker" : "gumball", + "dialog" : "AWESOMEEE" + }, + { + "speaker" : "darwin", + "dialog" : "Please tell me what has been going on.", + "newSpeaker" : "true" + }, + { + "speaker" : "anais", + "dialog" : "A fowl curse has been released by Gumball.", + "newSpeaker": "true" + }, + { + "speaker" : "darwin", + "dialog" : "You mean the gates of doom were opened by Gumball.", + "newSpeaker": "true" + }, + { + "speaker" : "anais", + "dialog" : "The fabric of the universe was ripped apart by Gumball." + } + ], + [ + { + "speaker" : "darwin", + "dialog" : "Gumball has made quite a mess.", + "newSpeaker" : "true" + }, + { + "speaker" : "darwin", + "dialog" : "Here is some healing ointment to help Gumball in the heat of battle." + } + + ] ] \ No newline at end of file diff --git a/assets/mana_bar.png b/assets/mana_bar.png new file mode 100644 index 0000000..684cce7 Binary files /dev/null and b/assets/mana_bar.png differ diff --git a/assets/80s_Fight_Music_1.mp3 b/assets/sound/80s_Fight_Music_1.mp3 similarity index 100% rename from assets/80s_Fight_Music_1.mp3 rename to assets/sound/80s_Fight_Music_1.mp3 diff --git a/assets/8BitArcade.mp3 b/assets/sound/8BitArcade.mp3 similarity index 100% rename from assets/8BitArcade.mp3 rename to assets/sound/8BitArcade.mp3 diff --git a/assets/text_box.png b/assets/text_box.png new file mode 100644 index 0000000..1414d17 Binary files /dev/null and b/assets/text_box.png differ diff --git a/index.html b/index.html index cd56c0a..565ecd7 100644 --- a/index.html +++ b/index.html @@ -8,9 +8,11 @@ + + diff --git a/src/main.js b/src/main.js index 593c3e3..31a62b9 100644 --- a/src/main.js +++ b/src/main.js @@ -42,7 +42,7 @@ let config = { physics: { default: "arcade", arcade: { - // debug: true, + debug: true, // gravity not needed // gravity: { // x: 0, @@ -50,7 +50,7 @@ let config = { // } } }, - scene: [ Menu, Fighting] + scene: [ Menu, Tutorial, Fighting] } @@ -64,9 +64,9 @@ const floorY = game.config.height / 10 * 6 const leftPos = game.config.width / 5 const rightPos = game.config.width / 5 * 4 const HP = 999 -const MP = 99 +const MP = 50 const temp_timer = 400 -// let cursors = null +let cursors = null diff --git a/src/prefabs/Character.js b/src/prefabs/Character.js index 6cd6abb..605c4ca 100644 --- a/src/prefabs/Character.js +++ b/src/prefabs/Character.js @@ -1,27 +1,30 @@ -class Character extends Phaser.GameObjects.Sprite { +class Character extends Phaser.Physics.Arcade.Sprite { // poop constructor(scene, x, y , texture, frame, health, mana, attack_dmg, name, power, index) { super(scene, x, y, texture) scene.add.existing(this) + scene.physics.add.existing(this) + this.body.setImmovable(true) this.x = x this.y = y + console.log('the y value is : ' + this.y) // setting character properties this.index = index this.health = health + this.mana = mana this.name = name // for prints this.hurtTimer = temp_timer this.power = power // creating a boolean value to check if the current character has attacked - + this.hurt = false this.hasAttacked = false // setting up fighting damage this.attack_dmg = attack_dmg - // this.hurt = false this.collapsed = false // temporary check this.check = '' - this.projectile = new Projectile(scene, this.x + this.width/2, this.y - this.height * 1.5, `${this.name}_projectile`, this) + this.projectile = new Projectile(scene, this.x + this.width/2, this.y - 5, `${this.name}_projectile`, this) scene.FSM_holder[index] = new StateMachine('idle', { idle: new IdleState(), @@ -30,18 +33,16 @@ class Character extends Phaser.GameObjects.Sprite { collapse: new CollapseState(), },[scene, this]) } - // possible solution - resetAttack() { - this.hasAttacked = false - } + } class IdleState extends State { // in this state the character may only enter the attack and hurt state enter (scene, character) { // player is not attacking in idle state - scene.dmgToEnemy = 0 character.clearTint() + // player is not hurt + character.hurt = false } execute(scene, character) { @@ -52,13 +53,25 @@ class IdleState extends State { if (character.willAttack == true && !character.hasAttacked){ this.stateMachine.transition('attack') } - // if the enemy is attacking - if(scene.enemy.hasAttacked && scene.enemy.selectedChar == character.index) { // test one character at a time - this.stateMachine.transition('hurt') + // if the enemy is attacking add a collider + if(scene.enemy.hasAttacked && scene.enemy.selectedChar == character.index) { + scene.physics.add.collider(scene.enemy.projectile, character, () => { + let collision = scene.enemy.projectile.handleCollision(character, scene.dmgToEnemy) + if ( collision == true){ + // reset that projectile once the collision is true + console.log('collision was true') + scene.enemy.projectile.resetProj(scene.enemy.projectile.startX, scene.enemy.projectile.startY) + this.stateMachine.transition('hurt') + // is entered + } + }, null, scene) } + + + } } @@ -68,9 +81,11 @@ class AttackState extends State { // remove the enemies health // scene.enemy.damaged = true scene.dmgToEnemy = character.attack_dmg + scene.selectionMenu.allowSelect = false + // console.log("selection allow is "+ scene.selectionMenu.allowSelect) character.setTint(0xDB91EF) - character.projectile.move(scene.enemyX, scene.enemyY) + character.projectile.move(scene.enemy.x + scene.enemy.width, scene.enemyY - scene.enemy.height) scene.time.delayedCall(character.hurtTimer, () => { @@ -92,13 +107,16 @@ class AttackState extends State { class HurtState extends State { enter (scene, character) { + // scene.enemy.hasAttacked = false + character.hurt = true // character.anims.play(`${character.name}_hurt`, true) character.setTint(0xFF0000) // decrease health and update bar character.health -= scene.enemy.dmgToPlayer + console.log(scene.enemy.selectedChar + "selected CHARACTER") scene.characters_hp[scene.enemy.selectedChar].match(character.health) let damage_txt = scene.add.bitmapText(character.x, character.y - tileSize*1.5, 'font', -scene.enemy.dmgToPlayer, 8).setOrigin(0, 0).setTint(0xFF0000) - + scene.changeTurn() this.attackText_below = scene.add.bitmapText(centerX, centerY+1, 'font', `${character.name} takes ${-scene.enemy.dmgToPlayer} damage`, 12).setOrigin(0.5).setTint(0x1a1200) this.attackText = scene.add.bitmapText(centerX, centerY, 'font', `${character.name} takes ${-scene.enemy.dmgToPlayer} damage`, 12).setOrigin(0.5) if (character.health > 0){ @@ -108,13 +126,15 @@ class HurtState extends State { damage_txt.setVisible(false) this.attackText_below.setVisible(false) this.attackText.setVisible(false) - + scene.enemy.selectedChar = -1 if (character.health > 0){ + this.stateMachine.transition('idle') } + }) } - + console.log('HURT') } @@ -123,6 +143,7 @@ class HurtState extends State { // if health depleted after hurt animation collapse this character this.stateMachine.transition('collapse') character.once('animationcomplete', () => { + this.stateMachine.transition('collapse') }) } diff --git a/src/prefabs/Enemy.js b/src/prefabs/Enemy.js index 006f9c7..1beff2b 100644 --- a/src/prefabs/Enemy.js +++ b/src/prefabs/Enemy.js @@ -1,8 +1,7 @@ -class Enemy extends Phaser.GameObjects.Sprite { +class Enemy extends Phaser.Physics.Arcade.Sprite { constructor(scene, x, y , texture, frame, health, mana, power, name) { super(scene, x, y, texture) scene.add.existing(this) - scene.physics.add.existing(this) this.body.setImmovable(true) // setting enemy properties @@ -17,7 +16,10 @@ class Enemy extends Phaser.GameObjects.Sprite { this.hasAttacked = false // setting up the character to attack - this.selectedChar = -1 + // this.selectedChar = -1 + + this.projectile = new Projectile(scene, this.x + this.width/2, this.y - this.height * 1.5, `${this.name}_projectile`, this) + this.startY = y // setting up state machines scene.enemyFSM = new StateMachine('default', { @@ -25,6 +27,7 @@ class Enemy extends Phaser.GameObjects.Sprite { single_attack: new SingleAttackState(), damaged: new DamagedState(), defeat: new DefeatState(), + reset: new ResetState(), },[scene, this]) } @@ -42,71 +45,102 @@ class Enemy extends Phaser.GameObjects.Sprite { // enemy specific state classes will be performed for each attack class DefaultState extends State { enter (scene, enemy) { + // enemy.selectedChar = -1 + console.log("ENEMY ENTERING DEFAULT") enemy.damaged = false // ensure enemy is not attacking in this scene - enemy.hasAttacked = false + // enemy.hasAttacked = false enemy.dmgToPlayer = 0 - + scene.dmgToEnemy = 0 // scene.player_turn = true enemy.clearTint() enemy.anims.play(`${enemy.name}_default`, true) - + // console.log(`${enemy.name} (boss) defaulting, damage = ${enemy.dmgToPlayer}`) } execute(scene, enemy) { const { left, right, up, down, space, shift } = scene.keys - // if it is not the player's turn attack - if(scene.player_turn == false && enemy.hasAttacked == false){ - this.stateMachine.transition('single_attack') - } + // if the enemy's y is not at the original location move it back - // if enemy has been damaged - if ( scene.dmgToEnemy ){ - // console.log(scene.selectionMenu.attackingPlayer.name + 'has just attacked') - - this.stateMachine.transition('damaged') + // Note: might make a brand new state + if (enemy.y < enemy.startY){ + enemy.body.setVelocityY(10) } - - // save the used projectile from the input from selectionmenu - if (scene.selectionMenu.attackingPlayer){ - // console.log(scene.selectionMenu.attackingPlayer.projectile) - - scene.physics.add.collider(scene.selectionMenu.attackingPlayer.projectile, enemy, scene.selectionMenu.attackingPlayer.projectile.handleCollision.bind(scene.selectionMenu.attackingPlayer.projectile), null, scene) + else if (enemy.y >= enemy.startY){ + enemy.body.setVelocityY(0) } + // if it is not the player's turn and there exists no currently attacking player + if(scene.player_turn == false && enemy.hasAttacked == false && !scene.selectionMenu.attackingPlayer){ + console.log("ENEMY ENTERING ATTACK") + // entered once + this.stateMachine.transition('single_attack') + + } + // save the used projectile from the input from selectionmenu + if (scene.selectionMenu.attackingPlayer){ + // console.log(scene.selectionMenu.attackingPlayer.projectile.x) + scene.physics.add.collider(scene.selectionMenu.attackingPlayer.projectile, enemy, () => { + let collision = scene.selectionMenu.attackingPlayer.projectile.handleCollision(enemy, scene.dmgToEnemy) + if ( collision == true){ + scene.selectionMenu.attackingPlayer.projectile.resetProj(scene.selectionMenu.attackingPlayer.projectile.startX, scene.selectionMenu.attackingPlayer.projectile.startY) + this.stateMachine.transition('damaged') + } + }, null, scene) + } } } class SingleAttackState extends State { // enemy will randomize their attack on a character enter (scene, enemy) { - // the damage to player becomes the attack power of this enemy + // turn off player selection + scene.selectionMenu.allowSelect = false enemy.anims.play(`${enemy.name}_singleAttack`, true) - - scene.time.delayedCall(enemy.damagedTimer, () => { - enemy.dmgToPlayer = enemy.attack_dmg - enemy.hasAttacked = true - }) + // select a character enemy.selectedChar = enemy.charAttacking(scene.checkLiving()) - scene.characters[enemy.selectedChar].hurt = true + console.log('character x is ' + scene.characters[enemy.selectedChar].x + 'enemy X' + enemy.x) } execute(scene, enemy) { - if (enemy.hasAttacked == true) { - // PROBLEM + // move enemy to the top + if (enemy.y > centerY){ + enemy.body.setVelocityY(-50) + } + // once we have reached the top + if (enemy.y <= centerY){ + // set the position + enemy.body.setVelocityY(0) + // move a projectile + enemy.projectile.move(scene.characters[enemy.selectedChar].x, scene.characters[enemy.selectedChar].y + scene.characters[enemy.selectedChar].height) + enemy.hasAttacked = true + enemy.dmgToPlayer = enemy.attack_dmg + // console.log('projectile is moving towards ' + scene.characters[enemy.selectedChar].name + 'at the y coordinate ' + scene.characters[enemy.selectedChar].y) + } - // reset the selected char here - this.selectedChar = -1 - scene.changeTurn() - this.stateMachine.transition('default') + // if the character has been hit + if (scene.characters[enemy.selectedChar].hurt == true) { + //selection menu + scene.selectionMenu.allowSelect = true + // scene.changeTurn() + console.log(scene.player_turn) + console.log('character has been hurt') + // enemy.hasAttacked = false + this.stateMachine.transition('default') + // go back to default } } } +class ResetState extends State { + +} + class DamagedState extends State { // animation play after finished character attack enter (scene, enemy) { // scene.choiceMenu.setVisible(false) + scene.selectionMenu.attackingPlayer = undefined enemy.health -= scene.dmgToEnemy enemy.setTint(0xFF0000) enemy.anims.play(`${enemy.name}_damaged`, true) @@ -115,6 +149,7 @@ class DamagedState extends State { enemy.once('animationcomplete', () => { damage.setVisible(false) + scene.selectionMenu.allowSelect = true if (enemy.health > 0){ this.stateMachine.transition('default') } diff --git a/src/prefabs/Health.js b/src/prefabs/Health.js index ffce525..c84027b 100644 --- a/src/prefabs/Health.js +++ b/src/prefabs/Health.js @@ -27,8 +27,8 @@ class HealthBar extends Phaser.GameObjects.Graphics{ scene.add.bitmapText(this.hp_pos, this.y, 'font', 'HP', 12) scene.add.bitmapText(this.name_pos, this.y - 1, 'font', character.name, 12).setTint(0xf5f576) - scene.add.bitmapText(this.name_pos, this.y + 1, 'font', character.name, 12).setTint(0x1a1200) - scene.add.bitmapText(this.name_pos, this.y, 'font', character.name, 12).setTint(0xa8832a) + scene.add.bitmapText(this.name_pos + 1, this.y + 1, 'font', character.name, 12).setTint(0x1a1200) + scene.add.bitmapText(this.name_pos, this.y, 'font', character.name, 12).setTint(0xaf904c) this.health_txt = scene.add.bitmapText(this.health_pos, this.y - 8, 'font', character.health, 8) } diff --git a/src/prefabs/Mana.js b/src/prefabs/Mana.js new file mode 100644 index 0000000..c0267d6 --- /dev/null +++ b/src/prefabs/Mana.js @@ -0,0 +1,63 @@ +class ManaBar extends Phaser.GameObjects.Graphics{ + // see: https://github.com/phaserjs/examples/blob/master/public/src/game%20objects/graphics/mana%20bars%20demo.js + constructor(scene, x, y, character){ + super(scene, x, y) + this.bar = new Phaser.GameObjects.Graphics(scene) + + this.x = x - tileSize * 2; + this.y = y; + this.value = character.mana; + // this.p = /100; + this.padding = 4 + this.width = 45 + this.height = 10 + this.draw(); + + // Note: should set up a variable for the global font size + // 12 is the font size + this.mp_pos = this.x + 12 // the text that shows up + this.name_pos = centerX - tileSize * 5.5 + this.mana_pos = this.width + this.x + 20 // the # amt of mana that shows + + // adding the bar to the scene + scene.add.existing(this.bar) + scene.add.image(x - 4, this.y+ 4, 'mana_bar').setOrigin(0.5) + + scene.add.bitmapText(this.mp_pos, this.y + 1, 'font', 'MP', 12).setTint(0x1a1200) + scene.add.bitmapText(this.mp_pos, this.y, 'font', 'MP', 12) + + this.mana_txt = scene.add.bitmapText(this.mana_pos, this.y - 8, 'font', character.mana, 8) + } + + match (amount) { + // whatever changes were made to the characters mana + this.value = amount + // ensure no negative values + if (this.value < 0){ + this.value = 0 + } + + this.draw() + this.mana_txt.text = this.value + + return (this.value === 0) + } + + draw() { + + this.bar.fillStyle(0xabaca7) + //#cb3938 + this.bar.fillRect(this.x + 40, this.y + 2, this.width - this.padding, this.height - this.padding) + + // setting up the red value + if (this.value < 30){ + this.bar.fillStyle(0xff0000) + } + else{ + this.bar.fillStyle(0x22b2e2) + } + + var mana_width = Math.floor(this.value / 1.25) + this.bar.fillRect(this.x + 40, this.y + 2, mana_width, this.height - this.padding) + } +} \ No newline at end of file diff --git a/src/prefabs/Projectile.js b/src/prefabs/Projectile.js index 97527fd..abcee23 100644 --- a/src/prefabs/Projectile.js +++ b/src/prefabs/Projectile.js @@ -6,9 +6,10 @@ class Projectile extends Phaser.Physics.Arcade.Sprite{ scene.physics.add.existing(this) // initialize variables - this.moveSpeed = 250 + this.moveSpeed = 350 this.character = character this.startX = this.x + this.startY = this.y // console.log(this.character.name) // this.setVisible(false) } @@ -16,47 +17,70 @@ class Projectile extends Phaser.Physics.Arcade.Sprite{ move(landX, landY) { - console.log('enemyX: ' + landX + 'enemyY:' + landY) - console.log('thisX: ' + this.x + 'thisY:' + this.y) + // console.log('moving ' + this.character.name) + // console.log('opposingX: ' + landX + 'thisX:' + this.x) + // console.log('thisX: ' + this.x + 'thisY:' + this.y) // this.setVisible(true) + let direction if(this.x >= landX){ + direction = 'left' this.anims.play(`${this.character.name}_projectileAttack`) + this.body.setVelocityX(-this.moveSpeed) + } + else if (landX >= this.x){ + direction = 'right' + this.body.setVelocityX(this.moveSpeed) + // this.body.setVelocityX(this.moveSpeed) + } - // increase if going to the right - // decrease if going to the left - if (landX > this.x){ - console.log("landX > this.x") - this.x += this.moveSpeed - // this.body.setVelocityX(this.moveSpeed) - } - - // for the current scene - if(landX < this.x){ - console.log("landX < this.x") - // this.x -= this.moveSpeed - this.body.setVelocityX(-this.moveSpeed) - } + // console.log('this height: ' + this.y + 'land destination ' +landY) + if (this.y > landY){ + this.body.setVelocityY(-20) } + else if (this.y < landY){ + // console.log('this.y = ' + this.y + 'landY = ' + landY) + // probably use pythagorean to ensure that it always lands at the character + this.body.setVelocityY(60) + } + + if (this.x == landX){ // this.setVisible(false) } - if (this.x >= landX){ - console.log(this.x) - this.resetProj(this.startX) + // console.log(this.width) + // console.log(landX) + // console.log(this.scene.enemy.x) + // console.log(this.scene.enemy.width) + + if (this.x - this.width <= landX && direction == 'left'){ + this.body.setVelocityY(0) + this.resetProj(this.startX, this.startY) + + } + + else if (this.x - this.width >= landX && direction == 'right'){ + console.log("REACHED? lanDX" + landX + 'this.x: ' + this.x) + this.body.setVelocityY(0) + this.resetProj(this.startX, this.startY) + } + } - resetProj(x){ - console.log("is being reached") + resetProj(x, y){ + // console.log('reset velocity' + this.body.velocity) + this.body.setVelocityY(0) + this.body.setVelocityX(0) this.x = x + this.y = y + // this.setVisible(false) } - handleCollision(){ - console.log("HANDLING COLLISION") + handleCollision(currently_Attacked, dmgDealt){ this.x = this.startX // console.log("original x = " + this.x + "set x " + this.startX) - + currently_Attacked.health -= dmgDealt // this.resetProj(this.startX) // set up animations for projectile LATER diff --git a/src/prefabs/Selection.js b/src/prefabs/Selection.js index 1fb36a7..deb779c 100644 --- a/src/prefabs/Selection.js +++ b/src/prefabs/Selection.js @@ -14,6 +14,7 @@ class SelectionMenu extends Phaser.GameObjects.Graphics{ this.current_selection = 2 // what the cursor is pointing at this.cursor_pos = -20 + this.allowSelect = true // to keep track if the player has attacked //see: https://github.com/phaserjs/examples/blob/master/public/src/game%20objects/text/simple%20text%20button.js this.container_bg = scene.add.image(x,y - 4, 'container') @@ -38,20 +39,22 @@ class SelectionMenu extends Phaser.GameObjects.Graphics{ // CURRENT PROBLEM - if the character we are looking at dies, they do not change characters // possible solution have a function updates the display or use charChange() select() { - this.availableChar = this.updateAvailable() - if (this.current_selection == 0 ){ - console.log('we have selected' + this.characters[this.availableChar[this.current_player]].name ) - // if cursor on the power selection - // Attack choice - if ( this.characters[this.availableChar[this.current_player]].collapsed == false && !this.characters[this.availableChar[this.current_player]].hasAttacked){ - // NOTE: check if character has died - this.characters[this.availableChar[this.current_player]].willAttack = true - this.attackingPlayer =this.characters[this.availableChar[this.current_player]] - this.charChange(1); + // only allow select if active + if (this.scene.player_turn == true && this.allowSelect == true){ + this.availableChar = this.updateAvailable() + if (this.current_selection == 0 ){ + // if cursor on the power selection + // Attack choice + if ( this.characters[this.availableChar[this.current_player]].collapsed == false && !this.characters[this.availableChar[this.current_player]].hasAttacked){ + // NOTE: check if character has died + this.characters[this.availableChar[this.current_player]].willAttack = true + this.attackingPlayer =this.characters[this.availableChar[this.current_player]] + this.charChange(1); + } + } + if (this.current_selection == 1){ + console.log("SUMMON") } - } - if (this.current_selection == 1){ - console.log("SUMMON") } } @@ -101,8 +104,6 @@ class SelectionMenu extends Phaser.GameObjects.Graphics{ this.charDisplay.text = this.characters[this.availableChar[this.current_player]].name this.powerDisplay.text = this.characters[this.availableChar[this.current_player]].power - - console.log("CURRENT CHARACTERS" + this.availableChar) } diff --git a/src/prefabs/Summon.js b/src/prefabs/Summon.js new file mode 100644 index 0000000..fe80aa2 --- /dev/null +++ b/src/prefabs/Summon.js @@ -0,0 +1,3 @@ +class Enemy extends Phaser.Physics.Arcade.Sprite { + +} \ No newline at end of file diff --git a/src/scenes/Fighting.js b/src/scenes/Fighting.js index 223e889..0fb7341 100644 --- a/src/scenes/Fighting.js +++ b/src/scenes/Fighting.js @@ -31,30 +31,37 @@ class Fighting extends Phaser.Scene { create() { // initializing a background // see: https://www.youtube.com/watch?v=OOo69t_-uok - this.background = this.add.image(this.scale.width / 2,this.scale.height / 2, 'background') + this.background = this.add.image(this.scale.width / 2,this.scale.height / 2 - tileSize * 1.55, 'background') // adding music this.music = this.sound.add('fight_music').setLoop(true).setVolume(0.4) // adding a character to scene - each character should have their own HP - this.gumball = new Character(this, rightPos-tileSize, floorY + tileSize, 'gumball', 0, this.hp, MP, 72, 'GUMBALL', 'MAGIC', 0).setOrigin(0,1) - this.anais = new Character(this, rightPos, floorY +tileSize, 'anais', 0, this.hp, MP, 150, 'ANAIS', 'SCIENCE', 1).setOrigin(0,1) - this.darwin = new Character(this, rightPos + tileSize, floorY + tileSize, 'darwin', 0, this.hp, MP, 10, 'DARWIN', 'SUPPORT', 2).setOrigin(0,1) + this.gumball = new Character(this, rightPos-tileSize, floorY + tileSize /1.5, 'gumball', 0, this.hp, MP, 72, 'GUMBALL', 'MAGIC', 0).setOrigin(0,1) + this.anais = new Character(this, rightPos, floorY +tileSize / 1.5, 'anais', 0, this.hp, MP, 150, 'ANAIS', 'SCIENCE', 1).setOrigin(0,1) + this.darwin = new Character(this, rightPos + tileSize, floorY + tileSize / 1.5, 'darwin', 0, this.hp, MP, 10, 'DARWIN', 'SUPPORT', 2).setOrigin(0,1) // adding each character health this.gumball_hp = new HealthBar(this, centerX, floorY + tileSize, this.gumball, 0) + this.gumball_mp = new ManaBar(this, centerX + tileSize * 3 + 12, floorY + tileSize, this.gumball, 0) + this.anais_hp = new HealthBar(this, centerX,floorY+ tileSize *1.5, this.anais, 0) + this.anais_mp = new ManaBar(this, centerX + tileSize * 3 + 12, floorY+ tileSize *1.5, this.anais, 1) + this.darwin_hp = new HealthBar(this, centerX, floorY + tileSize * 2, this.darwin, 2) + this.darwin_mp = new ManaBar(this, centerX + tileSize * 3 + 12, floorY + tileSize * 2, this.darwin, 1) + // adding all characters into an array to loop all the characters this.characters = [ this.gumball, this.anais, this.darwin ] this.characters_hp = [ this.gumball_hp, this.anais_hp, this.darwin_hp ] // adding enemy to scene - enemy has their own prefab - this.enemy = new Enemy(this, leftPos - tileSize, floorY + tileSize, 'penny', 0, HP, MP, 152, 'PENNY').setOrigin(0,1).setFlipX(true) + this.enemy = new Enemy(this, leftPos - tileSize, floorY + tileSize / 1.5, 'penny', 0, HP, MP, 152, 'PENNY').setOrigin(0,1).setFlipX(true) this.enemy_hp = new HealthBar(this, centerX, tileSize / 4, this.enemy) // setting up keyboard inputs this.keys = this.input.keyboard.createCursorKeys() - this.selectionMenu = new SelectionMenu(this, rightPos + tileSize / 2, floorY + tileSize + 25, this.characters) + // this.selectionMenu = new SelectionMenu(this, game.config.width - tileSize - 5, floorY + tileSize + 25, this.characters) + this.selectionMenu = new SelectionMenu(this, game.config.width - tileSize - 5, tileSize + 25, this.characters) // Game OVER flag this.gameOver = false @@ -109,7 +116,6 @@ class Fighting extends Phaser.Scene { if (Phaser.Input.Keyboard.JustDown(space)){ this.selectionMenu.select() } - // PROBLEM: the menu selection is spammable if (this.selectionMenu.current_selection == 2){ if (Phaser.Input.Keyboard.JustDown(right)){ this.selectionMenu.charChange(1) @@ -118,17 +124,12 @@ class Fighting extends Phaser.Scene { this.selectionMenu.charChange(-1) } } - if (Phaser.Input.Keyboard.JustDown(up)){ this.selectionMenu.lookChoice(1) } if (Phaser.Input.Keyboard.JustDown(down)){ this.selectionMenu.lookChoice(-1) } - - //this.physics.add.collider(this.p1Rocket, this.ship01, this.handleCollision, null, this) - // this.physics.add.collider(this.character[0].projectile, this.enemy, this.handleCollision, null, this) - } } checkActive(){ @@ -163,10 +164,12 @@ class Fighting extends Phaser.Scene { if (this.player_turn == false){ this.player_turn = true this.selectionMenu.setVisibility(true) + // this.selectionMenu.allowSelect = true } else if (this.player_turn == true){ this.player_turn = false this.selectionMenu.setVisibility(false) + // this.selectionMenu.allowSelect = true } } diff --git a/src/scenes/Menu.js b/src/scenes/Menu.js index 3b31105..e468dce 100644 --- a/src/scenes/Menu.js +++ b/src/scenes/Menu.js @@ -14,17 +14,31 @@ class Menu extends Phaser.Scene{ this.load.image('cursor', 'cursor.png') this.load.image('char_cursor', 'char_cursor.png') this.load.image('health_bar', 'health_bar.png') + this.load.image('mana_bar', 'mana_bar.png') + + // loading fonts + this.load.bitmapFont('font', 'font/atari-classic.png', 'font/atari-classic.xml') + // this.load.bitmapFont('font', 'font/gumball_font.png', 'font/gumball_font.xml') + this.load.bitmapFont('skinnyfont', 'font/SkinnyFont.png', 'font/SkinnyFont.xml') // setting up audio - this.load.audio('music', '8BitArcade.mp3') - this.load.audio('fight_music', '80s_Fight_Music_1.mp3') + this.load.audio('music', 'sound/8BitArcade.mp3') + this.load.audio('fight_music', 'sound/80s_Fight_Music_1.mp3') + + // load JSON (ie dialog text) + this.load.json('dialog', 'json/dialog.json') + // loading dialog box + this.load.image('dialogbox', 'text_box.png') //setting up character sprite sheet this.load.spritesheet('gumball', 'gumball_spritesheet.png', { frameWidth: 30, frameHeight: 42 }) + + this.load.image('gumball_talk', 'gumball_talking.png') + this.load.spritesheet('anais', 'anais_spritesheet.png', { frameWidth: 33, frameHeight: 34 @@ -55,9 +69,7 @@ class Menu extends Phaser.Scene{ frameHeight: 16 }) - // loading fonts - this.load.bitmapFont('font', 'atari-classic.png', 'atari-classic.xml') - // this.load.bitmapFont('font', 'gumball_font.png', 'gumball_font.xml') + } create() { @@ -74,8 +86,9 @@ class Menu extends Phaser.Scene{ // setting up inputs this.keys = this.input.keyboard.createCursorKeys() - // setting up animations + + // setting up animations this.anims.create({ key: 'GUMBALL_idle', frameRate: 8, @@ -170,9 +183,13 @@ class Menu extends Phaser.Scene{ update() { const { left, right, up, down, space, shift } = this.keys - if (left.isDown || right.isDown || up.isDown || down.isDown){ + if (left.isDown || right.isDown || up.isDown ){ this.scene.start('fightingScene') } + + if (down.isDown){ + this.scene.start('tutorialScene') + } } } diff --git a/src/scenes/Talking.js b/src/scenes/Talking.js deleted file mode 100644 index 9ff00b6..0000000 --- a/src/scenes/Talking.js +++ /dev/null @@ -1,6 +0,0 @@ -class Talking extends Phaser.Scene{ - constructor(){ - super('talkingScene') - } - // for dialog -} \ No newline at end of file diff --git a/src/scenes/Tutorial.js b/src/scenes/Tutorial.js new file mode 100644 index 0000000..f91e691 --- /dev/null +++ b/src/scenes/Tutorial.js @@ -0,0 +1,173 @@ +class Tutorial extends Phaser.Scene { + constructor() { + super("tutorialScene") + } + + init() { + // dialog constants + this.DBOX_X = centerX // dialog box x-position + this.DBOX_Y = 170 // dialog box y-position + this.DBOX_FONT = 'font' // dialog box font key + + this.TEXT_X = 20 // text w/in dialog box x-position + this.TEXT_Y = 180 // text w/in dialog box y-position + this.TEXT_SIZE = 8 // text font size (in pixels) + this.TEXT_MAX_WIDTH = 300 // max width of text within box + + this.NEXT_TEXT = '*SPACE*' // text to display for next prompt + this.NEXT_X = centerX // next text prompt x-position + this.NEXT_Y = 170 // next text prompt y-position + + this.LETTER_TIMER = 10 // # ms each letter takes to "type" onscreen + + // dialog variables + this.dialogConvo = 0 // current "conversation" + this.dialogLine = 0 // current line of conversation + this.dialogSpeaker = null // current speaker + this.dialogLastSpeaker = null // last speaker + this.dialogTyping = false // flag to lock player input while text is "typing" + this.dialogText = null // the actual dialog text + this.nextText = null // player prompt text to continue typing + + // character variables + this.tweenDuration = 500 // character in/out tween duration + + this.OFFSCREEN_X = -500 // x,y coordinates used to place characters offscreen + this.OFFSCREEN_Y = 1000 + } + + create() { + // parse dialog from JSON file + this.dialog = this.cache.json.get('dialog') + //console.log(this.dialog) + + // add dialog box sprite + this.dialogbox = this.add.sprite(this.DBOX_X, this.DBOX_Y, 'dialogbox').setOrigin(0.5, 0) + + // initialize dialog text objects (with no text) + this.dialogText = this.add.bitmapText(this.TEXT_X, this.TEXT_Y, this.DBOX_FONT, '', this.TEXT_SIZE) + this.nextText = this.add.bitmapText(this.NEXT_X, this.NEXT_Y, this.DBOX_FONT, '', this.TEXT_SIZE) + + // ready the character dialog images offscreen + this.gumball = this.add.sprite(this.OFFSCREEN_X, this.DBOX_Y+8, 'gumball_talk').setOrigin(0, 1) + this.anais = this.add.sprite(this.OFFSCREEN_X, this.DBOX_Y+8, 'anais_talk').setOrigin(0, 1).setScale(0.20) + this.darwin = this.add.sprite(this.OFFSCREEN_X, this.DBOX_Y+8, 'darwin_talk').setOrigin(0, 1) + + // input + cursors = this.input.keyboard.createCursorKeys() + + // start first dialog conversation + this.typeText() + } + + update() { + // check for spacebar press + if(Phaser.Input.Keyboard.JustDown(cursors.space) && !this.dialogTyping) { + this.typeText() // trigger dialog + } + } + + typeText() { + console.log('typing') + // lock input while typing + this.dialogTyping = true + + // clear text + this.dialogText.text = '' + this.nextText.text = '' + + /* JSON dialog structure: + - each array within the main JSON array is a "conversation" + - each object within a "conversation" is a "line" + - each "line" can have 3 properties: + 1. a speaker (required) + 2. the dialog text (required) + 3. an (optional) flag indicating if this speaker is new + */ + + // make sure there are lines left to read in this convo, otherwise jump to next convo + if(this.dialogLine > this.dialog[this.dialogConvo].length - 1) { + this.dialogLine = 0 + // I increment the conversation count here... + // ..but you could create logic to exit if each conversation was self-contained + this.dialogConvo++ + } + + // make sure we haven't run out of conversations... + if(this.dialogConvo >= this.dialog.length) { + // here I'm exiting the final conversation to return to the title... + // ...but you could add alternate logic if needed + console.log('End of Conversations') + // tween out prior speaker's image + if(this.dialogLastSpeaker) { + this.tweens.add({ + targets: this[this.dialogLastSpeaker], + x: this.OFFSCREEN_X, + duration: this.tweenDuration, + ease: 'Linear', + onComplete: () => { + this.scene.start('fightingScene') + } + }) + } + // make text box invisible + this.dialogbox.visible = false + + } else { + // if not, set current speaker + this.dialogSpeaker = this.dialog[this.dialogConvo][this.dialogLine]['speaker'] + // check if there's a new speaker (for exit/enter animations) + if(this.dialog[this.dialogConvo][this.dialogLine]['newSpeaker']) { + // tween out prior speaker's image + if(this.dialogLastSpeaker) { + this.tweens.add({ + targets: this[this.dialogLastSpeaker], + x: this.OFFSCREEN_X, + duration: this.tweenDuration, + ease: 'Linear' + }) + } + // tween in new speaker's image + this.tweens.add({ + targets: this[this.dialogSpeaker], + x: 30, + duration: this.tweenDuration, + ease: 'Linear' + }) + } + + // build dialog (concatenate speaker + colon + line of text) + this.combinedDialog = + this.dialog[this.dialogConvo][this.dialogLine]['speaker'].toUpperCase() + + ': ' + + this.dialog[this.dialogConvo][this.dialogLine]['dialog'] + + // create a timer to iterate through each letter in the dialog text + let currentChar = 0 + this.textTimer = this.time.addEvent({ + delay: this.LETTER_TIMER, + repeat: this.combinedDialog.length - 1, + callback: () => { + // concatenate next letter from dialogLines + this.dialogText.text += this.combinedDialog[currentChar] + // advance character position + currentChar++ + // check if timer has exhausted its repeats + // (necessary since Phaser 3 no longer seems to have an onComplete event) + if(this.textTimer.getRepeatCount() == 0) { + // show prompt for more text + this.nextText = this.add.bitmapText(this.NEXT_X, this.NEXT_Y, this.DBOX_FONT, this.NEXT_TEXT, this.TEXT_SIZE).setOrigin(1) + this.dialogTyping = false // un-lock input + this.textTimer.destroy() // destroy timer + } + }, + callbackScope: this // keep Scene context + }) + + // final cleanup before next iteration + this.dialogText.maxWidth = this.TEXT_MAX_WIDTH // set bounds on dialog + this.dialogLine++ // increment dialog line + this.dialogLastSpeaker = this.dialogSpeaker // set past speaker + } + } +} \ No newline at end of file