/**
* File Name: game.js
* Bonus File Content URL: https://leanpub.com/LoRD
* Description: monolithic File for controlling and
* displaying game scenes; managing global variables
* throughout game state.
* Author: Stephen Gose
* Version: 0.0.0.8 (Chapter 2 complete examples game-cmbtDynamicMenuHUDtiled-AI)
* Author URL: https://www.stephen-gose.com/
* Support: support@pbmcube.com
*
* Copyright © 1974-2017 Stephen Gose LLC. All rights reserved.
*
* Do not sell! Do not distribute!
* This is a licensed permission file. Please refer to Terms of Use
* and End Users License Agreement (EULA).
* Redistribution of part or whole of this file and
* the accompanying files is strictly prohibited.
*
*/
// =========================================================
// -------------------------------------------
// game name space section
// -------------------------------------------
//Typical name space creation found in all current online Phaser tutorials
//Create game name space away from the window global
var GAMEAPP = GAMEAPP || {};
// =========================================================
//Create game name space away from the window global
var GAMEAPP = {
//US Copr. or Copyright;UTF8 circled c is \u00A9 equal to ©
Copr: "Copyright © \u00A9 1974-2016, Stephen Gose. All rights reserved.\n",
// Here we have some global level vars that persist regardless of State.
score: 0,
clicks: 0,
// If the music in your game, and it needs to play through-out a few State
// swaps, then you could reference it below.
//music: null,
//Toggle background music theme on or off; starts in "on/true" state
//musicToggle: true,
//Your game can check MYGAMEAPP.orientated in the game loops
// to know if it should pause or not.
//orientated: false
_audioMgr: function(mode, game) {
switch(mode) {
case 'init': {
GAMEAPP.Storage.initUnset('GAMEAPP.audio', true);
GAMEAPP._audioStatus = GAMEAPP.Storage.get('GAMEAPP.audio');
// GAMEAPP._soundClick = game.add.audio('audio-click');
GAMEAPP._sound = [];
GAMEAPP._sound['click'] = game.add.audio('audio-click');
if(!GAMEAPP._soundMusic) {
GAMEAPP._soundMusic = game.add.audio('audio-theme',1,true);
GAMEAPP._soundMusic.volume = 0.5;
}
break;
}
case 'on': {
GAMEAPP._audioStatus = true;
break;
}
case 'off': {
GAMEAPP._audioStatus = false;
break;
}
case 'switch': {
GAMEAPP._audioStatus =! GAMEAPP._audioStatus;
break;
}
default: {}
}
if(GAMEAPP._audioStatus) {
GAMEAPP._audioOffset = 0;
if(GAMEAPP._soundMusic) {
if(!GAMEAPP._soundMusic.isPlaying) {
GAMEAPP._soundMusic.play('',0,1,true);
}
}
}
else {
GAMEAPP._audioOffset = 4;
if(GAMEAPP._soundMusic) {
GAMEAPP._soundMusic.stop();
}
}
GAMEAPP.Storage.set('GAMEAPP.audio',GAMEAPP._audioStatus);
game.buttonAudio.setFrames(GAMEAPP._audioOffset+1,
GAMEAPP._audioOffset+0, GAMEAPP._audioOffset+2);
},
_playAudio: function(sound) {
if(GAMEAPP._audioStatus) {
if(GAMEAPP._sound && GAMEAPP._sound[sound]) {
GAMEAPP._sound[sound].play();
}
}
}
};
// =========================================================
//Create game name space away from the window global
//Create game boot state
GAMEAPP.Boot = function (game) { };
//End of name space creation found in all current online Phaser tutorials
// =========================================================
; //Closes any previous scripts
// =========================================================
// -------------------------------------------
// Main game Handler methods - Chapter 1
// -------------------------------------------
// Chapter 1 uses separate JS Objects for game states
// This methods is still used in Phaser v3.x.x and
// called: "States from Objects"
// =========================================================
//Example: 1.2 Starting the Game.js begins
;//Closes any previous scripts
//(function() {
var gWidth = 800; //Using Golden Ration is important.
var gHeight = 500; //Using Golden Ration is important.
//Example 2.2: Grid-ed Combat begins
//Grid Tile-Map configurations
var tileSize = 64; //twice avatar icon size? or same size?
var numRows = 4; //adjustable for your game
var numCols = 4; //adjustable for your game
var tileSpacing = 2; //adjustable for your game
var map; //tile map as background
var layer; //tile map layer
//var tilesArray = []; //one way; thousand more to choose
//Example 2.2: Grid-ed Combat ends
var game = new Phaser.Game(gWidth, gHeight, Phaser.AUTO, "gContent");
//Example: 1.2 ends
// =========================================================
//Example 1.3 to 1.19: Creating States in Game.js begins
//Step 3) new game state additions (similar to Phaser v3
// state from objects):
//new game state additions:
var mainState = {
init: function(){
//stuff to generate in this function
},
create: function() {
console.log("mainState Ready!");
//Set a neutral background color
game.stage.backgroundColor = "#369";
//Set game to ARCADE physics systemLanguage
game.physics.startSystem(Phaser.Physics.ARCADE);
game.renderer.renderSession.roundPixels = true;
game.world.enableBody = true;
//create a character avatar
this.player =
game.add.sprite(32,32,box({length:32,width:32,color:'#00F'}));
this.cursor = game.input.keyboard.createCursorKeys();
this.player.body.collideWorldBounds = true;
//create an opponet
this.enemy = game.add.sprite(200,32,
box({length:32,width:32,color:'#0F0'}));
this.enemy.body.collideWorldBounds = true;
this.enemy.enableBody = true;
this.enemy.body.immovable = true;
game.physics.enable(this.enemy, Phaser.Physics.ARCADE);
//Create Room Walls on stage
this.Room = game.add.group();
this.Room.enableBody = true;
game.physics.enable(this.Room, Phaser.Physics.ARCADE);
var NorthWall = game.add.sprite(0,0,
box({length:game.world.width,width:16,color:'#999'}));
NorthWall.enableBody = true;
NorthWall.body.immovable = true;
this.Room.add(NorthWall);
var SouthWall = game.add.sprite(0,game.world.height-16,
box({length:game.world.width,width:16,color:'#999'}));
SouthWall.body.immovable = true;
this.Room.add(SouthWall);
var WestWall = game.add.sprite(0,16,
box({length:16,width:game.world.height-32,color:'#999'}));
WestWall.body.immovable = true;
this.Room.add(WestWall);
var EastWall = game.add.sprite(game.world.width-16,16,
box({length:16,width:game.world.height-32,color:'#999'}));
EastWall.body.immovable = true;
this.Room.add(EastWall);
var interior1Wall = game.add.sprite(game.world.width/6,16,
box({length:16,width:game.world.width/4,color:'#AAA'}));
interior1Wall.body.immovable = true;
this.Room.add(interior1Wall);
var interior2Wall = game.add.sprite(16,game.world.height/2,
box({length:game.world.width/2,width:16,color:'#AAA'}));
interior2Wall.body.immovable = true;
this.Room.add(interior2Wall);
}, //the comma is very important.
update: function() {
//frame refresh and display updates
var speed = 250;
this.player.body.velocity.x = 0;
this.player.body.velocity.y = 0;
//monitor player's movement input
if (this.cursor.up.isDown){
this.player.body.velocity.y -= speed;
}else if (this.cursor.down.isDown){
this.player.body.velocity.y += speed;
}else if (this.cursor.right.isDown){
this.player.body.velocity.x += speed;
}else if (this.cursor.left.isDown){
this.player.body.velocity.x -= speed;
}
game.physics.arcade.collide(this.player, this.Room);
game.physics.arcade.overlap
(this.player,this.enemy,combatEncounter,null,this);
}
}; //the semi-colon is very important.
// =========================================================
//Example 1.17: New Game Over State begins
var gameOverState = {
create: function(){
label = game.add.text(game.world.width/2,
game.world.height/2,
"Game Over \n Press the SPACE bar to start again",
{font: "22px Arial", fill: "#FFF", align:"center"});
label.anchor.setTo(0.5,0.5);
this.spacebar = this.game.input.keyboard.addKey
(Phaser.Keyboard.SPACEBAR);
}, //comma very important here
update: function(){
if(this.spacebar.isDown){
game.state.start('main');
}
}
};
//Example 1.17: ends
// =========================================================
//Process conflict; refer to book
var combat = {
preload: function () {
this.load.crossOrigin = 'anonymous';
//game background;static title and copyright
//Example 2.1: Dynamic Combat Menus begins
this.load.image('background', 'assets/images/menubkgrnd.jpg');
this.load.atlas('fireButton',
'assets/spriteSheets/mmog-sprites-silver.png',
'assets/spriteSheets/mmog-sprites.json');
this.load.atlas('attackButton',
'assets/spriteSheets/mmog-sprites-silver.png',
'assets/spriteSheets/mmog-sprites.json');
this.load.spritesheet('button',
'assets/spriteSheets/mmog-sprites-silver.png', 129, 30);
//Example 2.1 ends
},
create: function(){
//Set a neutral background color
game.stage.backgroundColor = "#300";
//Set game to ARCADE physics systemLanguage
game.physics.startSystem(Phaser.Physics.ARCADE);
game.renderer.renderSession.roundPixels = true;
game.world.enableBody = true;
label = game.add.text(game.world.width/2,
game.world.height-64,"(Grid-ed Tiled) Combat Encouter,
Dynamic Menu, HUD, & AI \n Press the SPACE bar to return",
{font: "22px Arial",fill: "#FFF", align:"center"});
label.anchor.setTo(0.5,0.5);
this.spacebar = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
//Example 2.3: Hexagonal Grid-ed Combat begins
//Creates a new blank layer and sets the map dimensions.
//In this case the map is 40x30 tiles in size and
// the tiles are 32x32 pixels in size.
//New Combat Grid - simplistic hexagon grid tiles using squares
this.HXTilesFloor = game.add.group();
var hxOffSetY = 0; // odd columns are pushed down half a square
var spacingX = tileSize * 0.75;
for(j=0;j <numRows;j++){
for(i=0;i <numCols;i++){
if ((i % 2) == 1){
hxOffSetY = tileSize * 0.5;
}else{
hxOffSetY = 0;
}
gameX = tileSize * i + 32 + tileSpacing ;
gameY = tileSize * j + 32 + tileSpacing + hxOffSetY;
var tileGridHx =
game.add.sprite(gameX+(game.world.width/2),gameY,
box({length:60,width:60,color:'#333'}));
this.HXTilesFloor.add(tileGridHx);
}
}
//Example 2.3: Hexagonal Grid-ed Combat ends
//Example 2.3: Grid-ed Combat Squares begins
//New Combat Grid - generic square tiles
this.SQTilesFloor = game.add.group();
for(j=0;j < numRows;j++){
for(i=0;i <numCols;i++){
gameX = tileSize * i + 32 + tileSpacing;
gameY = tileSize * j + 32 + tileSpacing;
var tileGridSQ =
game.add.sprite(gameX,gameY,
box({length:60,width:60,color:'#333'}));
this.SQTilesFloor.add(tileGridSQ);
}
}
//Example 2.3: Grid-ed Combat Squares ends
//Create New Dynamic menu & HUD
//Example 2.1: Dynamic Combat Menus deployed begins
var style = { font: "24px Arial", fill: "#033", align: "center" };
var attacktxt = this.add.text(0, 0, "Attack" , style);
var firetxt = this.add.text(0, 0, "Fire" , style);
// Attack button deployed off screen
this.attackButton = this.add.button(this.world.centerX-800,
game.world.height-160,
'button', this.MeleeStrike, this, 2, 1, 0);
this.attackButton.anchor.set(0.5,0);
this.attackButton.addChild(attacktxt).anchor.set(0.5,0);
// Fire button deployed
this.fireButton = this.add.button(this.world.centerX+80,
game.world.height-160,
'button', this.MissileFire, this, 2, 1, 0);
this.fireButton.anchor.set(0.5,0);
this.fireButton.addChild(firetxt).anchor.set(0.5,0);
//Example 2.1: Dynamic Combat Menus deployed ends
//create a character avatar
this.player =
game.add.sprite(32,32,box({length:32,width:32,color:'#00F'}));
this.cursor = game.input.keyboard.createCursorKeys();
this.player.body.collideWorldBounds = true;
this.SQTilesFloor.add(this.player);
//create an opponent
this.enemy = game.add.sprite((numCols*tileSize),
(numRows*tileSize),box({length:32,width:32,color:'#0F0'}));
this.enemy.body.collideWorldBounds = true;
this.enemy.enableBody = true;
this.enemy.body.immovable = true;
game.physics.enable(this.enemy, Phaser.Physics.ARCADE);
this.SQTilesFloor.add(this.enemy);
//Create Room Walls on stage
this.Room = game.add.group();
this.Room.enableBody = true;
game.physics.enable(this.Room, Phaser.Physics.ARCADE);
var NorthWall = game.add.sprite(0,0,
box({length:game.world.width,width:16,color:'#999'}));
NorthWall.enableBody = true;
NorthWall.body.immovable = true;
this.Room.add(NorthWall);
var SouthWall = game.add.sprite(0,game.world.height-16,
box({length:game.world.width,width:16,color:'#999'}));
SouthWall.body.immovable = true;
this.Room.add(SouthWall);
var WestWall = game.add.sprite(0,16,
box({length:16,width:game.world.height-32,color:'#999'}));
WestWall.body.immovable = true;
this.Room.add(WestWall);
var EastWall = game.add.sprite(game.world.width-16,16,
box({length:16,width:game.world.height-32,color:'#999'}));
EastWall.body.immovable = true;
this.Room.add(EastWall);
}, //comma very important here
update: function(){
if(this.spacebar.isDown){
//game.state.start('main');
window.open("https://adventurers-of-renown.com/book/ch2/index.html", "_blank");
}
//frame refresh and display updates
var speed = 250;
this.player.body.velocity.x = 0;
this.player.body.velocity.y = 0;
this.enemy.body.velocity.x = 0;
this.enemy.body.velocity.y = 0;
//monitor player's movement input
//Example 2.7 Enemy AI mirrored movement
if (this.cursor.up.isDown){
this.player.body.velocity.y -= speed;
this.enemy.body.velocity.y += speed;
}else if (this.cursor.down.isDown){
this.player.body.velocity.y += speed;
this.enemy.body.velocity.y -= speed;
}else if (this.cursor.right.isDown){
this.player.body.velocity.x += speed;
this.enemy.body.velocity.x -= speed;
}else if (this.cursor.left.isDown){
this.player.body.velocity.x -= speed;
this.enemy.body.velocity.x += speed;
}
//Example 2.1: Dynamic Combat Menus buttons swapped begins
//Not engaged in melee
this.attackButton.x = this.world.centerX-800;
this.fireButton.x = this.world.centerX+80;
//Example 2.1: Dynamic Combat Menus buttons swapped ends
game.physics.arcade.collide(this.player, this.Room);
game.physics.arcade.overlap
(this.player,this.enemy,meleeCombat,null,this);
}
};
// =========================================================
// -------------------------------------------
// Supporting game Function & Classes
// -------------------------------------------
// =========================================================
//Example 2.1a: Dynamic Combat Menus support begins
//melee combat: Dynamic Menu; engaged in melee
var meleeCombat = function(player,enemy){
this.attackButton.x = this.world.centerX-80;
this.fireButton.x = this.world.centerX+800;
};
var MeleeStrike = function(){
console.log('attacked');
};
var MissileFire = function(){
console.log('fired');
};
//Example 2.1a: Dynamic Combat Menus support ends
// =========================================================
//Example 1.16: Collision Results Determination begins
//character's death
var handlePlayerDeath = function(player,enemy){
player.kill(); //kill off the avatar
game.state.start("gameOver"); //change to Game Over scene
};
//Example 1.16: ends
// =========================================================
//character's initiates combat
var combatEncounter = function(player,enemy){
game.state.start("combat"); //change to Game Over scene
};
// =========================================================
//Example 1.7: Prototyping Graphics begins
//create a box Image (pseudo graphics) for the HTML5 canvas.
var box = function(options) {
var bxImg = this.game.add.bitmapData(options.length,options.width);
bxImg.ctx.beginPath();
bxImg.ctx.rect(0,0,options.length,options.width);
bxImg.ctx.fillStyle = options.color;
bxImg.ctx.strokeStyle = "#FFF";
bxImg.ctx.fill();
return bxImg;
};
// =========================================================
//Step 1) Let’s tell Phaser about our new scenes
//Phaser uses our code and gives it a name of ‘main’.
//Let’s tell Phaser about our new scenes
//Phaser uses our code and gives it a name of ‘main’.
game.state.add("main", mainState);
game.state.add("gameOver", gameOverState);
game.state.add("combat", combat);
//tells Phase to start using it.
game.state.start("main"); //tells Phase to start using it.
//Example 1.7: ends
// =========================================================
See Examples here
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8" /> <title>Phaser 3 Game Prototyping - (Your Game Title Here)</title> <meta name="description" content="Phaser 3 Game Prototyping Template" /> <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /> <style> body{margin:0;padding:0;} canvas {margin: 0 auto;} </style> <script src="https://cdn.jsdelivr.net/phaser/2.6.2/phaser.min.js"></script> </head> <body> <div id="game"></div> <script type="text/javascript">
//Canvas dimensions: world and viewports' Height and Width //**TODO** adjust for your game deployment var game = new Phaser.Game(880, 550, Phaser.AUTO, 'game'); var NUMBER_OF_ATTACKS = 8; //maximum for each attack blade var difficulty = 1; //Level of difficulty // Our Missile class template for ranged attacks // This uses an image Sprite with our secret sauce properties // It launches the Weapon class plugin manager //Step 4) Dedicated functions for missile objects var Bullet = function (game, key) { Phaser.Sprite.call(this, game, 0, 0, key); //tell Pixi to use nearest neighbor scaling. //When a bullet is scaled from its default size //it won't be automatically 'smoothed' and will retain its pixel crispness. this.texture.baseTexture.scaleMode = PIXI.scaleModes.NEAREST; this.anchor.set(0.5); this.inputEnabled = true; //check if the bullet is inside the world boundaries and //if not kill it, return it to bullet pool. this.checkWorldBounds = true; this.outOfBoundsKill = true; this.exists = false; //Re-align the Bullet toward the direction it is traveling. this.tracking = false; //how fast the bullet should grow in size as it travels this.scaleSpeed = 0; }; //See Phaser Plugin Appendix for style debate //this method required when accessing classes. Bullet.prototype = Object.create(Phaser.Sprite.prototype); Bullet.prototype.constructor = Bullet; Bullet.prototype.fire = function (x, y, angle, speed, gx, gy) { gx = gx || 0; gy = gy || 0; this.reset(x, y); this.scale.set(1); //important to maintain correct facing this.rotation = this.body.rotation; this.game.physics.arcade.velocityFromAngle(angle, speed, this.body.velocity); //don't use in Guitar-Hero style games. //this.angle = angle; this.body.gravity.set(gx, gy); }; Bullet.prototype.update = function () { //console.log(this.deltaY); if (this.tracking) { this.rotation = Math.atan2(this.body.velocity.y, this.body.velocity.x); } if (this.scaleSpeed > 0) { this.scale.x += this.scaleSpeed; this.scale.y += this.scaleSpeed; } }; //Step 4) Dedicated functions for missile objects. // add the following to the end of Example 3.2 var Weapon = {}; /** My concept: Set movie at 30fps; therefore 15fps = .5 second Level 0 monsters are slower; so sTempo should be a variable number between 16 - 20 Level 1 monster are slightly faster; so sTempo is 13 - 15 fps Level 2 monsters are quick; set sTempo at 10 - 14 fps Level 3 monsters are very fast; 6 - 11 fps Set sTempo at a "Base" rate and then modify with a variable roll Phaser.bulletSpeedVariance is: var SpeedVar = Math.round(Math.random()*6); Phaser.fireRate is: var sTempo = 14 + (SpeedVar - GAMEAPP.difficulty); var ReadiedWeapon = 1; //should come from avatar inventory? */ /////////////////////////////////////////////////////////////// // launch a single missile from avatar's facing direction // /////////////////////////////////////////////////////////////// Weapon.LtArrowAttack = function (game,key) { Phaser.Group.call(this, game, game.world, 'Chopping Attack', false, true, Phaser.Physics.ARCADE); this.name = "Chopping Attack"; this.nextFire = 0; //this.bulletSpeed = 600; //pixels per update; fixed value this.bulletSpeed = 100 + Math.round(Math.random() * ((difficulty+1) * 160)); if (this.bulletSpeed > 900){this.bulletSpeed = 870;} this.bulletSpeedVariance = 30; console.log("Lt Attack speed: ",this.bulletSpeed); //this.fireRate = 100; //in milliseconds; fixed value //more difficult levels have a faster fire rate this.fireRate = 1000 - this.bulletSpeed; if (this.fireRate < 100){this.fireRate = 100;} console.log("Lt Attack fire rate: ",this.fireRate); for (var i = 0; i < NUMBER_OF_ATTACKS; i++) { this.add(new Bullet(game, 'bullet1'), true); } return this; }; //See Phaser Plugin Appendix for style debate //this method required when accessing classes. Weapon.LtArrowAttack.prototype = Object.create(Phaser.Group.prototype); Weapon.LtArrowAttack.prototype.constructor = Weapon.LtArrowAttack; //Step 4) Dedicated functions for missile objects. // add the following to the end of Example 3.3 //Launching thrown weapons using `Weapon.fire` Weapon.LtArrowAttack.prototype.fire = function (source) { if (this.game.time.time < this.nextFire) { return; } var x = source.x + 40; var y = source.y + 17; //270 degrees throws this weapon attack up the scene. this.getFirstExists(false).fire(x, y, 270, this.bulletSpeed, 0, 0); this.nextFire = this.game.time.time + this.fireRate; }; ////////////////////////// // Lunge Attack // ////////////////////////// Weapon.UpArrowAttack = function (game,key) { Phaser.Group.call(this, game, game.world, 'Lunge Attack', false, true, Phaser.Physics.ARCADE); this.name = "Lunge Attack"; this.nextFire = 0; //this.bulletSpeed = 600; //pixels per update; fixed value this.bulletSpeed = 100 + Math.round(Math.random() * ((difficulty+1) * 160)); if (this.bulletSpeed > 900){this.bulletSpeed = 870;} this.bulletSpeedVariance = 30; console.log("Up Attack speed: ",this.bulletSpeed); //this.fireRate = 100; //in milliseconds; fixed value //more difficult levels have a faster fire rate this.fireRate = 1000 - this.bulletSpeed; if (this.fireRate < 100){this.fireRate = 100;} console.log("Up Attack fire rate: ",this.fireRate); for (var i = 0; i < NUMBER_OF_ATTACKS; i++) { this.add(new Bullet(game, 'bullet2'), true); } return this; }; Weapon.UpArrowAttack.prototype = Object.create(Phaser.Group.prototype); Weapon.UpArrowAttack.prototype.constructor = Weapon.UpArrowAttack; Weapon.UpArrowAttack.prototype.fire = function (source) { if (this.game.time.time < this.nextFire) { return; } var x = this.game.world.centerX-36; var y = 490; this.getFirstExists(false).fire(x, y, 270, this.bulletSpeed, 0, 0); this.nextFire = this.game.time.time + this.fireRate; }; /////////////////////////////////////// // Stabbing Attack (directly above) // /////////////////////////////////////// Weapon.DnArrowAttack = function (game,key) { Phaser.Group.call(this, game, game.world, 'Stabbing Attack', false, true, Phaser.Physics.ARCADE); this.name = "Stabbing Attack"; this.nextFire = 0; //this.bulletSpeed = 600; //pixels per update; fixed value this.bulletSpeed = 100 + Math.round(Math.random() * ((difficulty+1) * 160)); if (this.bulletSpeed > 900){this.bulletSpeed = 870;} this.bulletSpeedVariance = 30; console.log("Dn Attack speed: ",this.bulletSpeed); //this.fireRate = 100; //in milliseconds; fixed value //more difficult levels have a faster fire rate this.fireRate = 1000 - this.bulletSpeed; if (this.fireRate < 100){this.fireRate = 100;} console.log("Dn Attack fire rate: ",this.fireRate); for (var i = 0; i < NUMBER_OF_ATTACKS; i++) { this.add(new Bullet(game, 'bullet3'), true); } return this; }; Weapon.DnArrowAttack.prototype = Object.create(Phaser.Group.prototype); Weapon.DnArrowAttack.prototype.constructor = Weapon.DnArrowAttack; Weapon.DnArrowAttack.prototype.fire = function (source) { if (this.game.time.time < this.nextFire) { return; } var x = this.game.world.centerX+36; var y = 490; this.getFirstExists(false).fire(x, y, 270, this.bulletSpeed, 0, 0); this.nextFire = this.game.time.time + this.fireRate; }; ///////////////////////////////////////////// // Slice Attack - Right Arrow // ///////////////////////////////////////////// Weapon.RtArrowAttack = function (game,key) { Phaser.Group.call(this, game, game.world, 'Slice Attack', false, true, Phaser.Physics.ARCADE); this.name = "Slice Attack"; this.nextFire = 0; //this.bulletSpeed = 600; //pixels per update; fixed value this.bulletSpeed = 100 + Math.round(Math.random() * ((difficulty+1) * 160)); if (this.bulletSpeed > 900){this.bulletSpeed = 870;} this.bulletSpeedVariance = 30; console.log("Rt Attack speed: ",this.bulletSpeed); //this.fireRate = 100; //in milliseconds; fixed value //more difficult levels have a faster fire rate this.fireRate = 1000 - this.bulletSpeed; if (this.fireRate < 100){this.fireRate = 100;} console.log("Rt Attack fire rate: ",this.fireRate); for (var i = 0; i < NUMBER_OF_ATTACKS; i++) { this.add(new Bullet(game, 'bullet4'), true); } return this; }; Weapon.RtArrowAttack.prototype = Object.create(Phaser.Group.prototype); Weapon.RtArrowAttack.prototype.constructor = Weapon.RtArrowAttack; Weapon.RtArrowAttack.prototype.fire = function (source) { if (this.game.time.time < this.nextFire) { return; } var x = this.game.world.centerX+110; var y = 490; this.getFirstExists(false).fire(x,y,270,this.bulletSpeed,0,0); this.nextFire = this.game.time.time + this.fireRate; }; // The core game loop var RhythmCombat = function () { this.background = null; this.foreground = null; this.player = null; this.cursors = null; this.speed = 300; this.weapons = []; this.currentWeapon = 0; this.weaponName = null; }; RhythmCombat.prototype = { init: function () { this.game.renderer.renderSession.roundPixels = true; this.physics.startSystem(Phaser.Physics.ARCADE); }, preload: function () { console.log("loading play state"); // We need this because the assets are on Amazon S3 // Remove the next line if running locally this.load.crossOrigin = 'anonymous'; var levelSelected = 0; //demo - rfs-Phaser.jpg this.load.image('play', 'assets/images/rfs-Phaser-play2.jpg'); this.load.image('menu', 'assets/images/rfs-Phaser.jpg'); this.load.image('receptor', 'assets/images/receptorTrans.png'); this.load.image('dnArrow', 'assets/images/down.png'); this.load.image('ltArrow', 'assets/images/left.png'); this.load.image('upArrow', 'assets/images/up.png'); this.load.image('rtArrow', 'assets/images/right.png'); this.load.image('header', 'assets/gameFrame/silverHeader.jpg'); this.load.image('footer', 'assets/gameFrame/silverFooter.jpg'); this.load.atlas('button-continue','assets/spriteSheets/RightArrow-Phaser.png','assets/spriteSheets/arrowR-sprites.json\ '); this.load.spritesheet('button', 'assets/spriteSheets/mmm-sprites.png', 129, 30); for (var i = 1; i <= 4; i++) { this.load.image('bullet' + i, 'assets/bullet' + i + '.png'); } //////////////////////////////////////////// // required assets for following states here }, create: function () { console.log("starting play state"); var InfoText = "Combat Begins!"; this.background= this.add.image(0, 0, 'play'); var style = { font: "bold 32px Arial", fill: "#fff", boundsAlignH: "center", boundsAlignV: "middle" }; this._toolTip = this.game.add.text(this.world.centerX, this.world.centerY,InfoText, style); this._toolTip.anchor.set(0.5,0.5); this._toolTip.setShadow(3, 3, 'rgba(0,0,0,0.5)', 5); this._toolTip.fontWeight = 'bold'; this.weapons.push(new Weapon.LtArrowAttack(this.game)); this.weapons.push(new Weapon.UpArrowAttack(this.game)); this.weapons.push(new Weapon.DnArrowAttack(this.game)); this.weapons.push(new Weapon.RtArrowAttack(this.game)); this.currentWeapon = 0; //must be last added to shield attacks this.player = this.add.sprite(this.world.centerX-260,0, 'header'); //embedded receptor as background image is default //un comment for attacks to go under the receptor //this.receptor = this.add.sprite(this.world.centerX-146,53, 'receptor'); this.UpAttack = this.add.sprite(this.world.centerX-73,490,'upArrow'); this.DnAttack = this.add.sprite(this.world.centerX,490,'dnArrow'); this.LtAttack = this.add.sprite(this.world.centerX-146,490,'ltArrow'); this.RtAttack = this.add.sprite(this.world.centerX+73,490,'rtArrow'); this.add.image(165, 0, 'header'); //is the player's sprite this.add.image(165, 500, 'footer'); this.physics.arcade.enable(this.player); this.player.body.collideWorldBounds = true; // Cursor keys to fly + space to fire this.cursors = this.input.keyboard.createCursorKeys(); this.input.keyboard.addKeyCapture([ Phaser.Keyboard.SPACEBAR ]); var changeKey = this.input.keyboard.addKey(Phaser.Keyboard.ENTER); changeKey.onDown.add(this.nextWeapon, this); }, nextWeapon: function () { // Tidy-up the current weapon if (this.currentWeapon > 4) { this.weapons[this.currentWeapon].reset(); } else { this.weapons[this.currentWeapon].visible = false; this.weapons[this.currentWeapon].callAll('reset',null,0,0); this.weapons[this.currentWeapon].setAll('exists', false); } // Activate the new one this.currentWeapon++; if (this.currentWeapon === this.weapons.length) { this.currentWeapon = 0; } this.weapons[this.currentWeapon].visible = true; InfoText = this.weapons[this.currentWeapon].name; console.log("Current Attack Blade: ", this.weapons[this.currentWeapon].name); }, update: function () { //auto fire: //this.currentWeapon = Math.round(Math.random()*4); //console.log("Current Attacking blade: ",this.currentWeapon); if (this.cursors.left.isDown) { //this.player.body.velocity.x = -this.speed; console.log("Current Attack Blade: ", this.weapons[this.currentWeapon].name); console.log(" - Y: ",this.weapons[this.currentWeapon].UpArrowAttack.name); } if (this.cursors.right.isDown) { //this.player.body.velocity.x = this.speed; } if (this.cursors.up.isDown) { //this.player.body.velocity.y = -this.speed; } if (this.cursors.down.isDown) { //this.player.body.velocity.y = this.speed; } //debug:
to manually test fire; //debug: to change attacks if (this.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR)) { this.weapons[this.currentWeapon].fire(this.LtAttack); this.weapons[this.currentWeapon].fire(this.UpAttack); this.weapons[this.currentWeapon].fire(this.DnAttack); this.weapons[this.currentWeapon].fire(this.RtAttack); } }, render: function(){ //console.log(game.time.now); } }; //game.stage.setBackgroundColor(0xbfbfbf); game.state.add('Game', RhythmCombat, true); </script> </body> </html>
See Examples here
var plotType = [
"Here is Your Randomly generated Quest ...",
"Here's how your new Adventure begins ...",
"Here's how your new Quest begins ...",
"Here's your background tale ...",
"Once upon a time ..."
];
var authorityFigure = [
"A brilliant yet infamous rogue",
"A famous paladin or warrior",
"A friendly ranger",
"A mighty warrior hero",
"A noble of high esteem",
"A pious and powerful cleric",
"A powerful legendary wizard",
"A rich guildmaster ",
"The chancellor/advisor to the local lord",
"The head of the local adventurer's guild",
"The local captain of the guard",
"The mentor of one of the player characters"
];
var crisisEvent = [
"arcane lights and unidentified sounds that come at night ",
"desecration of a local temple or cemetery",
"destruction of property by fire",
"increase in activity from a long-standing threat of brigands and assassins",
"kidnappings of local children",
"mysterious rumors of wild magics and powerful summonings in the area",
"outbreaks of disease",
"rash of major thefts from the rich and powerful",
"rise in the local monstrous population",
"seemingly random wildfires claiming the local farms and forests",
"unexplained deaths among the local nobility",
"unexplained murders among the poor"
];
var localAdventure = [
"a desecrated temple or shrine",
"a major city",
"a nearby poorly-explored forest",
"a small village",
"isolated farms and outlying areas",
"local swamps and marshes",
"the family tombs of rich noblemen",
"the graveyard of your home town",
"the ruins of a castle",
"the ruins of a lost city",
"the ruins of an abbey",
"the sewers of a major city"
];
var startLocation= [
"the deep forest",
"the estate of a local lord",
"the frontier wilderness away from most civilization",
"the local adventurer's guild",
"the local tavern",
"the lower mountain passes",
"the rich farmlands near a major city",
"the streets of a large city",
"the streets of a small village",
"the trade roads between the major cities",
"the wilderness around your previous adventure",
"your home or rented rooms"
];
var rewardBounty = [
"all the supplies and clothing you can carry",
"local land and property grants",
"the adulation and gratitude of the locals",
"the reward of 100s of gold pieces",
"the reward of a minor magical item",
"whatever the town can gather (2d20 gold pieces each)",
"whatever the village can gather (2d20 gold pieces)",
"your armor and weapons repaired for free",
"your armor improved or upgraded with magical properties",
"your armor improved or upgraded with non-magical properties",
"your weapons improved or upgraded with magical properties ",
"your weapons improved or upgraded with non-magical properties "
];
var miscreant = [
"a brilliant and ruthless assassin",
"a marauding dragon",
"a mighty villainous warrior for hire",
"a nobleman of questionable character",
"a pious and powerful cleric of an evil faith",
"a powerful legendary wizard",
"a rich guild-master ",
"an infamous barbarian warrior or evil knight",
"the corrupt commander of the local army",
"the corrupt head of the local adventurer's guild",
"the nemesis of one of the player characters",
"the traitorous former captain of the guard"
];
var Legend = "\n";
function display(id, str) {
if ((id == "titlecontainer") || (id == "formatcontainer")) {
Legend = Legend+str+"\n";
} else {
Legend = "\n";
}
}
function displayPhaser(id, str) {
if (id == "titlecontainer") {
Legend = this.game.add.text(0, 0, str , GAMEAPP.styleRA);
Legend = Legend+str+"\n";
}
if (id == "formatcontainer") {
Legend = this.game.add.text(0, 0, str , GAMEAPP.styleHUD);
Legend = Legend+str+"\n";
} else {
Legend = "\n";
}
}
function displayhtml0() {
var format = "0";
var authfig = Math.floor(Math.random()*authorityFigure.length);
var conflict = Math.floor(Math.random()*crisisEvent.length);
var locadv = Math.floor(Math.random()*localAdventure.length);
var locstart = Math.floor(Math.random()*startLocation.length);
var bounty = Math.floor(Math.random()*rewardBounty.length);
var sinner = Math.floor(Math.random()*miscreant.length);
display("NONE", "");
display("titlecontainer", "<html><body><B>"+plotType[0]+"</B>");
display("formatcontainer", "As you wander through "+startLocation[locstart]+", you soon meet a familiar face, who treats you\
to a meal in exchange for willing ears. "+authorityFigure[authfig]+" waits until most of the crowds have drawn away from you b\
efore telling you any news beyond small talk. Finally, the tale begins.\n\nRecently, there has been "+crisisEvent[conflict]\
+". This obviously has many locals concerned. Rumors say it is the work of "+miscreant[sinner]+", but none know the truth\
. It's up to you to get to the bottom of the controversy and learn what it means for all the folk and lands around "+localAdventure[locadv]+". In exchange for your aid, you'll gain "+rewardBounty[bounty]+" as well as additional accolades as heroes of "+localAdventure[locadv]+".</body></html>");
}
function displayhtml1() {
var format = "1";
var authfig = Math.floor(Math.random()*authorityFigure.length);
var conflict = Math.floor(Math.random()*crisisEvent.length);
var locadv = Math.floor(Math.random()*localAdventure.length);
var locstart = Math.floor(Math.random()*startLocation.length);
var bounty = Math.floor(Math.random()*rewardBounty.length);
var villian = Math.floor(Math.random()*miscreant.length);
display("NONE", "");
display("titlecontainer", "<html><body><B>"+plotType[1]+" </B>");
display("formatcontainer", "Once all was right in "+localAdventure[locadv]+" -- until "+miscreant[sinner]+" arrived. Despite the\
best efforts of "+authorityFigure[authfig]+" and others, none could stop the "+crisisEvent[conflict]+". The only ones left to figh\
t "+miscreant[sinner]+" are you, the intrepid heroes, backed only by the hopes of those from "+startLocation[locstart]+" and t\
heir promised reward of "+rewardBounty[bounty]+".</body></html>");
}
function displayhtml2() {
var format = "2";
var authfig = Math.floor(Math.random()*authorityFigure.length);
var conflict = Math.floor(Math.random()*crisisEvent.length);
var locadv = Math.floor(Math.random()*localAdventure.length);
var locstart = Math.floor(Math.random()*startLocation.length);
var bounty = Math.floor(Math.random()*rewardBounty.length);
var villian = Math.floor(Math.random()*miscreant.length);
display("NONE", "");
display("titlecontainer", "<html><body><B>"+plotType[2]+" </B>");
display("formatcontainer", "• You begin the adventure in "+startLocation[locstart]+".\n• You meet "+authorityFigure[authfig]+", who \
gives you your mission.<BR>• You have to resolve the "+crisisEvent[conflict]+".<BR>• They've determined it is the work of "\
+miscreant[sinner]+", and it's up to you to stop it.<BR>• You must travel to "+localAdventure[locadv]+" to find "+villian[villian\
]+".<BR>• End the threat in exchange for "+rewardBounty[bounty]+".</font></td></tr></table>");
}
function displayCanvas0() {
var format = "0";
var authfig = Math.floor(Math.random()*authorityFigure.length);
var conflict = Math.floor(Math.random()*crisisEvent.length);
var locadv = Math.floor(Math.random()*localAdventure.length);
var locstart = Math.floor(Math.random()*startLocation.length);
var bounty = Math.floor(Math.random()*rewardBounty.length);
var villian = Math.floor(Math.random()*miscreant.length);
displayPhaser("NONE", "");
displayPhaser("titlecontainer", " "+plotType[0]+"");
displayPhaser("formatcontainer", "As you wander through "+startLocation[locstart]+", you soon meet a familiar face, who trea\
ts you to a meal in exchange for willing ears. "+authorityFigure[authfig]+" waits until most of the crowds have drawn away from\
you before telling you any news beyond small talk. Finally, the tale begins.\n\nRecently, there has been "+crisisEvent[con \
flict]+". This obviously has many locals concerned. Rumors say it is the work of "+miscreant[sinner]+", but none know the\
truth. It's up to you to get to the bottom of the controversy and learn what it means for all the folk and lands around\
"+localAdventure[locadv]+". In exchange for your aid, you'll gain "+rewardBounty[bounty]+" as well as additional accolades as heroes \
of "+localAdventure[locadv]+".");
}
function displayCanvas1() {
var format = "1";
var authfig = Math.floor(Math.random()*authorityFigure.length);
var conflict = Math.floor(Math.random()*crisisEvent.length);
var locadv = Math.floor(Math.random()*localAdventure.length);
var locstart = Math.floor(Math.random()*startLocation.length);
var bounty = Math.floor(Math.random()*rewardBounty.length);
var villian = Math.floor(Math.random()*miscreant.length);
displayPhaser("NONE", "");
displayPhaser("titlecontainer", " "+plotType[1]+" ");
displayPhaser("formatcontainer", "Once all was right in "+localAdventure[locadv]+" -- until "+miscreant[sinner]+" arrived. Despi\
te the best efforts of "+authorityFigure[authfig]+" and others, none could stop the "+crisisEvent[conflict]+". The only ones left t\
o fight "+miscreant[sinner]+" are you, the intrepid heroes, backed only by the hopes of those from "+startLocation[locstart]+"\
and their promised reward of "+rewardBounty[bounty]+".");
}
function displayCanvas2() {
var format = "2";
var authfig = Math.floor(Math.random()*authorityFigure.length);
var conflict = Math.floor(Math.random()*crisisEvent.length);
var locadv = Math.floor(Math.random()*localAdventure.length);
var locstart = Math.floor(Math.random()*startLocation.length);
var bounty = Math.floor(Math.random()*rewardBounty.length);
var villian = Math.floor(Math.random()*miscreant.length);
displayPhaser("NONE", "");
displayPhaser("titlecontainer", " "+plotType[2]+" ");
displayPhaser("formatcontainer", "• You begin the adventure in "+startLocation[locstart]+".\n• You meet "+authorityFigure[authfig]+"\
, who gives you your mission.\n• You have to resolve the "+crisisEvent[conflict]+".\n• They've determined it is the work of\
"+miscreant[sinner]+", and it's up to you to stop it.\n• You must travel to "+localAdventure[locadv]+" to find "+villian[villian\
]+".\n• End the threat in exchange for "+rewardBounty[bounty]+".");
}
See Examples here
Copyright © 2017, Stephen Gose LLC.
All rights reserved.