diff --git a/client/package.json b/client/package.json
index b3e316777..8308f8d63 100644
--- a/client/package.json
+++ b/client/package.json
@@ -39,7 +39,7 @@
"react-dom": "^16.13.1",
"react-error-boundary": "^3.1.4",
"react-hot-toast": "^2.2.0",
- "react-moralis": "^1.2.1",
+ "react-moralis": "^1.4.0",
"react-query": "^3.39.0",
"react-router": "^5.2.1",
"react-router-dom": "^5.2.0",
diff --git a/client/public/Assets/Phaser/50cal.m4a b/client/public/Assets/Phaser/50cal.m4a
new file mode 100644
index 000000000..34bede74a
Binary files /dev/null and b/client/public/Assets/Phaser/50cal.m4a differ
diff --git a/client/public/Assets/Phaser/50cal.mp3 b/client/public/Assets/Phaser/50cal.mp3
new file mode 100644
index 000000000..6719df765
Binary files /dev/null and b/client/public/Assets/Phaser/50cal.mp3 differ
diff --git a/client/public/Assets/Phaser/50cal.ogg b/client/public/Assets/Phaser/50cal.ogg
new file mode 100644
index 000000000..82e9629b2
Binary files /dev/null and b/client/public/Assets/Phaser/50cal.ogg differ
diff --git a/client/public/Assets/Phaser/BankPanic.tps b/client/public/Assets/Phaser/BankPanic.tps
new file mode 100644
index 000000000..020a83299
--- /dev/null
+++ b/client/public/Assets/Phaser/BankPanic.tps
@@ -0,0 +1,375 @@
+
+
+
+ fileFormatVersion
+ 4
+ texturePackerVersion
+ 5.0.0
+ autoSDSettings
+
+
+ scale
+ 1
+ extension
+
+ spriteFilter
+
+ acceptFractionalValues
+
+ maxTextureSize
+
+ width
+ -1
+ height
+ -1
+
+
+
+ allowRotation
+
+ shapeDebug
+
+ dpi
+ 72
+ dataFormat
+ phaser
+ textureFileName
+
+ flipPVR
+
+ pvrCompressionQuality
+ PVR_QUALITY_NORMAL
+ atfCompressData
+
+ mipMapMinSize
+ 32768
+ etc1CompressionQuality
+ ETC1_QUALITY_LOW_PERCEPTUAL
+ etc2CompressionQuality
+ ETC2_QUALITY_LOW_PERCEPTUAL
+ dxtCompressionMode
+ DXT_PERCEPTUAL
+ jxrColorFormat
+ JXR_YUV444
+ jxrTrimFlexBits
+ 0
+ jxrCompressionLevel
+ 0
+ ditherType
+ NearestNeighbour
+ backgroundColor
+ 0
+ libGdx
+
+ filtering
+
+ x
+ Linear
+ y
+ Linear
+
+
+ shapePadding
+ 2
+ jpgQuality
+ 80
+ pngOptimizationLevel
+ 0
+ webpQualityLevel
+ 101
+ textureSubPath
+
+ atfFormats
+
+ textureFormat
+ png
+ borderPadding
+ 2
+ maxTextureSize
+
+ width
+ 2048
+ height
+ 2048
+
+ fixedTextureSize
+
+ width
+ -1
+ height
+ -1
+
+ algorithmSettings
+
+ algorithm
+ MaxRects
+ freeSizeMode
+ Best
+ sizeConstraints
+ AnySize
+ forceSquared
+
+ maxRects
+
+ heuristic
+ Best
+
+ basic
+
+ sortBy
+ Best
+ order
+ Ascending
+
+ polygon
+
+ alignToGrid
+ 1
+
+
+ dataFileNames
+
+ multiPack
+
+ forceIdenticalLayout
+
+ outputFormat
+ RGBA8888
+ alphaHandling
+ ClearTransparentPixels
+ contentProtection
+
+ key
+
+
+ autoAliasEnabled
+
+ trimSpriteNames
+
+ prependSmartFolderName
+
+ autodetectAnimations
+
+ globalSpriteSettings
+
+ scale
+ 1
+ scaleMode
+ Smooth
+ extrude
+ 0
+ trimThreshold
+ 16
+ trimMargin
+ 1
+ trimMode
+ Trim
+ tracerTolerance
+ 200
+ heuristicMask
+
+ defaultPivotPoint
+ 0.5,0.5
+ writePivotPoints
+
+
+ individualSpriteSettings
+
+ fileList
+
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit2Dead.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/cowboy1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/cowboy1Dead.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/cowboy2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/cowboy2Dead.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/doorBackground.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/gameOver.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/gold.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/hat.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/hat0.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/hat1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/hat2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/hat3.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/hat4.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/hat5.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/levelComplete.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/levelText.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/3.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/4.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/5.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/6.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/7.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/8.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/9.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/10.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/11.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/12.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit1Dead.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/door5.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/door1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/door2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/door3.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/door4.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit1shot1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit1shot2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit1smoke1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit1smoke2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit2shot1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit2shot2.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit2smoke1.png
+ ../../../../../../../Art/GameSalad rubbish/BankPanic.gameproj/images/bandit2smoke2.png
+ ../../../../../../../Dropbox/Phaser Backers/Bank Panic/cactus.png
+
+ ignoreFileList
+
+ replaceList
+
+ ignoredWarnings
+
+ commonDivisorX
+ 1
+ commonDivisorY
+ 1
+ packNormalMaps
+
+ autodetectNormalMaps
+
+ normalMapFilter
+
+ normalMapSuffix
+
+ normalMapSheetFileName
+
+ exporterProperties
+
+
+
diff --git a/client/public/Assets/Phaser/background.png b/client/public/Assets/Phaser/background.png
new file mode 100644
index 000000000..7bf94c64f
Binary files /dev/null and b/client/public/Assets/Phaser/background.png differ
diff --git a/client/public/Assets/Phaser/bank-panic.json b/client/public/Assets/Phaser/bank-panic.json
new file mode 100644
index 000000000..78634b4b2
--- /dev/null
+++ b/client/public/Assets/Phaser/bank-panic.json
@@ -0,0 +1,986 @@
+{
+ "textures": [
+ {
+ "image": "bank-panic.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 962,
+ "h": 1781
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "gameOver",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 800,
+ "h": 200
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 11,
+ "w": 798,
+ "h": 188
+ },
+ "frame": {
+ "x": 2,
+ "y": 2,
+ "w": 798,
+ "h": 188
+ }
+ },
+ {
+ "filename": "bandit2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 29,
+ "y": 5,
+ "w": 140,
+ "h": 386
+ },
+ "frame": {
+ "x": 802,
+ "y": 2,
+ "w": 140,
+ "h": 386
+ }
+ },
+ {
+ "filename": "levelComplete",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 800,
+ "h": 200
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 11,
+ "w": 798,
+ "h": 188
+ },
+ "frame": {
+ "x": 2,
+ "y": 192,
+ "w": 798,
+ "h": 188
+ }
+ },
+ {
+ "filename": "door1",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 200,
+ "h": 400
+ },
+ "frame": {
+ "x": 2,
+ "y": 382,
+ "w": 200,
+ "h": 400
+ }
+ },
+ {
+ "filename": "door2",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 200,
+ "h": 400
+ },
+ "frame": {
+ "x": 204,
+ "y": 382,
+ "w": 200,
+ "h": 400
+ }
+ },
+ {
+ "filename": "door3",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 200,
+ "h": 400
+ },
+ "frame": {
+ "x": 406,
+ "y": 382,
+ "w": 200,
+ "h": 400
+ }
+ },
+ {
+ "filename": "hat5",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 32,
+ "y": 8,
+ "w": 136,
+ "h": 384
+ },
+ "frame": {
+ "x": 608,
+ "y": 382,
+ "w": 136,
+ "h": 384
+ }
+ },
+ {
+ "filename": "bandit1smoke1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 28,
+ "y": 162,
+ "w": 38,
+ "h": 57
+ },
+ "frame": {
+ "x": 746,
+ "y": 382,
+ "w": 38,
+ "h": 57
+ }
+ },
+ {
+ "filename": "bandit1smoke2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 130,
+ "y": 162,
+ "w": 38,
+ "h": 57
+ },
+ "frame": {
+ "x": 746,
+ "y": 382,
+ "w": 38,
+ "h": 57
+ }
+ },
+ {
+ "filename": "bandit2smoke1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 36,
+ "y": 136,
+ "w": 38,
+ "h": 57
+ },
+ "frame": {
+ "x": 746,
+ "y": 382,
+ "w": 38,
+ "h": 57
+ }
+ },
+ {
+ "filename": "bandit2smoke2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 120,
+ "y": 140,
+ "w": 38,
+ "h": 57
+ },
+ "frame": {
+ "x": 746,
+ "y": 382,
+ "w": 38,
+ "h": 57
+ }
+ },
+ {
+ "filename": "cowboy1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 25,
+ "y": 47,
+ "w": 148,
+ "h": 344
+ },
+ "frame": {
+ "x": 786,
+ "y": 390,
+ "w": 148,
+ "h": 344
+ }
+ },
+ {
+ "filename": "1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 14,
+ "y": 0,
+ "w": 24,
+ "h": 36
+ },
+ "frame": {
+ "x": 936,
+ "y": 390,
+ "w": 24,
+ "h": 36
+ }
+ },
+ {
+ "filename": "4",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 7,
+ "y": 0,
+ "w": 38,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 441,
+ "w": 38,
+ "h": 36
+ }
+ },
+ {
+ "filename": "6",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 8,
+ "y": 0,
+ "w": 37,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 479,
+ "w": 37,
+ "h": 36
+ }
+ },
+ {
+ "filename": "9",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 8,
+ "y": 0,
+ "w": 37,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 517,
+ "w": 37,
+ "h": 36
+ }
+ },
+ {
+ "filename": "8",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 9,
+ "y": 0,
+ "w": 35,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 555,
+ "w": 35,
+ "h": 36
+ }
+ },
+ {
+ "filename": "3",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 9,
+ "y": 0,
+ "w": 34,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 593,
+ "w": 34,
+ "h": 36
+ }
+ },
+ {
+ "filename": "5",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 10,
+ "y": 0,
+ "w": 32,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 631,
+ "w": 32,
+ "h": 36
+ }
+ },
+ {
+ "filename": "2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 10,
+ "y": 0,
+ "w": 31,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 669,
+ "w": 31,
+ "h": 36
+ }
+ },
+ {
+ "filename": "7",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 12,
+ "y": 0,
+ "w": 28,
+ "h": 36
+ },
+ "frame": {
+ "x": 746,
+ "y": 707,
+ "w": 28,
+ "h": 36
+ }
+ },
+ {
+ "filename": "door4",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 200,
+ "h": 400
+ },
+ "frame": {
+ "x": 746,
+ "y": 745,
+ "w": 200,
+ "h": 400
+ }
+ },
+ {
+ "filename": "hat4",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 32,
+ "y": 39,
+ "w": 136,
+ "h": 353
+ },
+ "frame": {
+ "x": 608,
+ "y": 768,
+ "w": 136,
+ "h": 353
+ }
+ },
+ {
+ "filename": "door5",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 200,
+ "h": 400
+ },
+ "frame": {
+ "x": 2,
+ "y": 1123,
+ "w": 200,
+ "h": 400
+ }
+ },
+ {
+ "filename": "cactus",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 219,
+ "h": 321
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 219,
+ "h": 321
+ },
+ "frame": {
+ "x": 2,
+ "y": 784,
+ "w": 219,
+ "h": 321
+ }
+ },
+ {
+ "filename": "doorBackground",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 200,
+ "h": 400
+ },
+ "frame": {
+ "x": 204,
+ "y": 1107,
+ "w": 200,
+ "h": 400
+ }
+ },
+ {
+ "filename": "bandit2Dead",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 90,
+ "w": 198,
+ "h": 258
+ },
+ "frame": {
+ "x": 223,
+ "y": 784,
+ "w": 198,
+ "h": 258
+ }
+ },
+ {
+ "filename": "hat",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 60
+ },
+ "spriteSourceSize": {
+ "x": 41,
+ "y": 1,
+ "w": 118,
+ "h": 58
+ },
+ "frame": {
+ "x": 223,
+ "y": 1044,
+ "w": 118,
+ "h": 58
+ }
+ },
+ {
+ "filename": "levelText",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 156,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 156,
+ "h": 36
+ },
+ "frame": {
+ "x": 343,
+ "y": 1044,
+ "w": 156,
+ "h": 36
+ }
+ },
+ {
+ "filename": "hat0",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 19,
+ "y": 147,
+ "w": 159,
+ "h": 245
+ },
+ "frame": {
+ "x": 423,
+ "y": 784,
+ "w": 159,
+ "h": 245
+ }
+ },
+ {
+ "filename": "bandit1shot1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 21,
+ "y": 192,
+ "w": 58,
+ "h": 50
+ },
+ "frame": {
+ "x": 501,
+ "y": 1031,
+ "w": 58,
+ "h": 50
+ }
+ },
+ {
+ "filename": "bandit1shot2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 124,
+ "y": 192,
+ "w": 58,
+ "h": 50
+ },
+ "frame": {
+ "x": 501,
+ "y": 1031,
+ "w": 58,
+ "h": 50
+ }
+ },
+ {
+ "filename": "bandit2shot1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 29,
+ "y": 166,
+ "w": 58,
+ "h": 50
+ },
+ "frame": {
+ "x": 501,
+ "y": 1031,
+ "w": 58,
+ "h": 50
+ }
+ },
+ {
+ "filename": "bandit2shot2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 114,
+ "y": 170,
+ "w": 58,
+ "h": 50
+ },
+ "frame": {
+ "x": 501,
+ "y": 1031,
+ "w": 58,
+ "h": 50
+ }
+ },
+ {
+ "filename": "11",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 4,
+ "y": 0,
+ "w": 45,
+ "h": 36
+ },
+ "frame": {
+ "x": 561,
+ "y": 1031,
+ "w": 45,
+ "h": 36
+ }
+ },
+ {
+ "filename": "10",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 52,
+ "h": 36
+ },
+ "frame": {
+ "x": 406,
+ "y": 1082,
+ "w": 52,
+ "h": 36
+ }
+ },
+ {
+ "filename": "bandit1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 25,
+ "y": 47,
+ "w": 146,
+ "h": 344
+ },
+ "frame": {
+ "x": 460,
+ "y": 1083,
+ "w": 146,
+ "h": 344
+ }
+ },
+ {
+ "filename": "12",
+ "rotated": false,
+ "trimmed": false,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 0,
+ "w": 52,
+ "h": 36
+ },
+ "frame": {
+ "x": 406,
+ "y": 1120,
+ "w": 52,
+ "h": 36
+ }
+ },
+ {
+ "filename": "gold",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 52,
+ "h": 36
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 2,
+ "w": 52,
+ "h": 34
+ },
+ "frame": {
+ "x": 406,
+ "y": 1158,
+ "w": 52,
+ "h": 34
+ }
+ },
+ {
+ "filename": "hat2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 17,
+ "y": 102,
+ "w": 167,
+ "h": 290
+ },
+ "frame": {
+ "x": 406,
+ "y": 1429,
+ "w": 167,
+ "h": 290
+ }
+ },
+ {
+ "filename": "bandit1Dead",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 6,
+ "y": 110,
+ "w": 190,
+ "h": 254
+ },
+ "frame": {
+ "x": 204,
+ "y": 1509,
+ "w": 190,
+ "h": 254
+ }
+ },
+ {
+ "filename": "cowboy1Dead",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 6,
+ "y": 107,
+ "w": 190,
+ "h": 254
+ },
+ "frame": {
+ "x": 2,
+ "y": 1525,
+ "w": 190,
+ "h": 254
+ }
+ },
+ {
+ "filename": "hat3",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 25,
+ "y": 71,
+ "w": 149,
+ "h": 321
+ },
+ "frame": {
+ "x": 575,
+ "y": 1429,
+ "w": 149,
+ "h": 321
+ }
+ },
+ {
+ "filename": "hat1",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 13,
+ "y": 133,
+ "w": 175,
+ "h": 259
+ },
+ "frame": {
+ "x": 608,
+ "y": 1147,
+ "w": 175,
+ "h": 259
+ }
+ },
+ {
+ "filename": "cowboy2",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 28,
+ "y": 104,
+ "w": 152,
+ "h": 287
+ },
+ "frame": {
+ "x": 726,
+ "y": 1408,
+ "w": 152,
+ "h": 287
+ }
+ },
+ {
+ "filename": "cowboy2Dead",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 200,
+ "h": 400
+ },
+ "spriteSourceSize": {
+ "x": 15,
+ "y": 140,
+ "w": 169,
+ "h": 222
+ },
+ "frame": {
+ "x": 785,
+ "y": 1147,
+ "w": 169,
+ "h": 222
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:d5eefd4d31c0f168f47a58860cdb7df1:b0e0dda77887bc8465add10eea30d043:6361c45baea2beec238f704c4d91b19b$"
+ }
+}
diff --git a/client/public/Assets/Phaser/bank-panic.png b/client/public/Assets/Phaser/bank-panic.png
new file mode 100644
index 000000000..52fcea69e
Binary files /dev/null and b/client/public/Assets/Phaser/bank-panic.png differ
diff --git a/client/public/Assets/Phaser/bullet-hole.png b/client/public/Assets/Phaser/bullet-hole.png
new file mode 100644
index 000000000..cf9d0f77a
Binary files /dev/null and b/client/public/Assets/Phaser/bullet-hole.png differ
diff --git a/client/public/Assets/Phaser/complete.m4a b/client/public/Assets/Phaser/complete.m4a
new file mode 100644
index 000000000..d120375be
Binary files /dev/null and b/client/public/Assets/Phaser/complete.m4a differ
diff --git a/client/public/Assets/Phaser/complete.mp3 b/client/public/Assets/Phaser/complete.mp3
new file mode 100644
index 000000000..d8b352021
Binary files /dev/null and b/client/public/Assets/Phaser/complete.mp3 differ
diff --git a/client/public/Assets/Phaser/complete.ogg b/client/public/Assets/Phaser/complete.ogg
new file mode 100644
index 000000000..2e06a0618
Binary files /dev/null and b/client/public/Assets/Phaser/complete.ogg differ
diff --git a/client/public/Assets/Phaser/door.m4a b/client/public/Assets/Phaser/door.m4a
new file mode 100644
index 000000000..15cacd836
Binary files /dev/null and b/client/public/Assets/Phaser/door.m4a differ
diff --git a/client/public/Assets/Phaser/door.mp3 b/client/public/Assets/Phaser/door.mp3
new file mode 100644
index 000000000..0e323a5e3
Binary files /dev/null and b/client/public/Assets/Phaser/door.mp3 differ
diff --git a/client/public/Assets/Phaser/door.ogg b/client/public/Assets/Phaser/door.ogg
new file mode 100644
index 000000000..b095181a7
Binary files /dev/null and b/client/public/Assets/Phaser/door.ogg differ
diff --git a/client/public/Assets/Phaser/gameover.m4a b/client/public/Assets/Phaser/gameover.m4a
new file mode 100644
index 000000000..ec1a01265
Binary files /dev/null and b/client/public/Assets/Phaser/gameover.m4a differ
diff --git a/client/public/Assets/Phaser/gameover.mp3 b/client/public/Assets/Phaser/gameover.mp3
new file mode 100644
index 000000000..1124fbbc9
Binary files /dev/null and b/client/public/Assets/Phaser/gameover.mp3 differ
diff --git a/client/public/Assets/Phaser/gameover.ogg b/client/public/Assets/Phaser/gameover.ogg
new file mode 100644
index 000000000..01f8b016a
Binary files /dev/null and b/client/public/Assets/Phaser/gameover.ogg differ
diff --git a/client/public/Assets/Phaser/loading.png b/client/public/Assets/Phaser/loading.png
new file mode 100644
index 000000000..fe788a50d
Binary files /dev/null and b/client/public/Assets/Phaser/loading.png differ
diff --git a/client/public/Assets/Phaser/logo.png b/client/public/Assets/Phaser/logo.png
new file mode 100644
index 000000000..94f16ce98
Binary files /dev/null and b/client/public/Assets/Phaser/logo.png differ
diff --git a/client/public/Assets/Phaser/money.m4a b/client/public/Assets/Phaser/money.m4a
new file mode 100644
index 000000000..a6ee98b74
Binary files /dev/null and b/client/public/Assets/Phaser/money.m4a differ
diff --git a/client/public/Assets/Phaser/money.mp3 b/client/public/Assets/Phaser/money.mp3
new file mode 100644
index 000000000..e3a7fa37f
Binary files /dev/null and b/client/public/Assets/Phaser/money.mp3 differ
diff --git a/client/public/Assets/Phaser/money.ogg b/client/public/Assets/Phaser/money.ogg
new file mode 100644
index 000000000..6a654bed6
Binary files /dev/null and b/client/public/Assets/Phaser/money.ogg differ
diff --git a/client/public/Assets/Phaser/music.m4a b/client/public/Assets/Phaser/music.m4a
new file mode 100644
index 000000000..251329a99
Binary files /dev/null and b/client/public/Assets/Phaser/music.m4a differ
diff --git a/client/public/Assets/Phaser/music.mp3 b/client/public/Assets/Phaser/music.mp3
new file mode 100644
index 000000000..e2484a450
Binary files /dev/null and b/client/public/Assets/Phaser/music.mp3 differ
diff --git a/client/public/Assets/Phaser/music.ogg b/client/public/Assets/Phaser/music.ogg
new file mode 100644
index 000000000..48df69d56
Binary files /dev/null and b/client/public/Assets/Phaser/music.ogg differ
diff --git a/client/public/Assets/Phaser/scream1.m4a b/client/public/Assets/Phaser/scream1.m4a
new file mode 100644
index 000000000..8c9781858
Binary files /dev/null and b/client/public/Assets/Phaser/scream1.m4a differ
diff --git a/client/public/Assets/Phaser/scream1.mp3 b/client/public/Assets/Phaser/scream1.mp3
new file mode 100644
index 000000000..c35b12735
Binary files /dev/null and b/client/public/Assets/Phaser/scream1.mp3 differ
diff --git a/client/public/Assets/Phaser/scream1.ogg b/client/public/Assets/Phaser/scream1.ogg
new file mode 100644
index 000000000..55d890c71
Binary files /dev/null and b/client/public/Assets/Phaser/scream1.ogg differ
diff --git a/client/public/Assets/Phaser/scream2.m4a b/client/public/Assets/Phaser/scream2.m4a
new file mode 100644
index 000000000..7bf414b44
Binary files /dev/null and b/client/public/Assets/Phaser/scream2.m4a differ
diff --git a/client/public/Assets/Phaser/scream2.mp3 b/client/public/Assets/Phaser/scream2.mp3
new file mode 100644
index 000000000..7ee8262c4
Binary files /dev/null and b/client/public/Assets/Phaser/scream2.mp3 differ
diff --git a/client/public/Assets/Phaser/scream2.ogg b/client/public/Assets/Phaser/scream2.ogg
new file mode 100644
index 000000000..82fc800f3
Binary files /dev/null and b/client/public/Assets/Phaser/scream2.ogg differ
diff --git a/client/public/Assets/Phaser/scream3.m4a b/client/public/Assets/Phaser/scream3.m4a
new file mode 100644
index 000000000..c667936a1
Binary files /dev/null and b/client/public/Assets/Phaser/scream3.m4a differ
diff --git a/client/public/Assets/Phaser/scream3.mp3 b/client/public/Assets/Phaser/scream3.mp3
new file mode 100644
index 000000000..f1287e387
Binary files /dev/null and b/client/public/Assets/Phaser/scream3.mp3 differ
diff --git a/client/public/Assets/Phaser/scream3.ogg b/client/public/Assets/Phaser/scream3.ogg
new file mode 100644
index 000000000..14e114242
Binary files /dev/null and b/client/public/Assets/Phaser/scream3.ogg differ
diff --git a/client/public/Assets/Phaser/shot.m4a b/client/public/Assets/Phaser/shot.m4a
new file mode 100644
index 000000000..97ad52c27
Binary files /dev/null and b/client/public/Assets/Phaser/shot.m4a differ
diff --git a/client/public/Assets/Phaser/shot.mp3 b/client/public/Assets/Phaser/shot.mp3
new file mode 100644
index 000000000..dd82e2b85
Binary files /dev/null and b/client/public/Assets/Phaser/shot.mp3 differ
diff --git a/client/public/Assets/Phaser/shot.ogg b/client/public/Assets/Phaser/shot.ogg
new file mode 100644
index 000000000..c29019214
Binary files /dev/null and b/client/public/Assets/Phaser/shot.ogg differ
diff --git a/client/public/Assets/Phaser/start.png b/client/public/Assets/Phaser/start.png
new file mode 100644
index 000000000..3d5f144ad
Binary files /dev/null and b/client/public/Assets/Phaser/start.png differ
diff --git a/client/public/Assets/Phaser/title.png b/client/public/Assets/Phaser/title.png
new file mode 100644
index 000000000..a48564e98
Binary files /dev/null and b/client/public/Assets/Phaser/title.png differ
diff --git a/client/src/app.js b/client/src/App.jsx
similarity index 100%
rename from client/src/app.js
rename to client/src/App.jsx
diff --git a/client/src/components/Phaser/App.css b/client/src/components/Phaser/App.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/components/Phaser/Phaser.jsx b/client/src/components/Phaser/Phaser.jsx
new file mode 100644
index 000000000..0e34d0f0b
--- /dev/null
+++ b/client/src/components/Phaser/Phaser.jsx
@@ -0,0 +1,83 @@
+import Phaser from "phaser";
+import Boot from "./scenes/Boot.js";
+import Preloader, { authEvents, AUTH } from "./scenes/Preloader.js";
+import MainMenu, { nftEvents, LOAD_NFT } from "./scenes/MainMenu.js";
+import MainGame from "./scenes/Game.js";
+import { useState, useEffect } from "react";
+import { createStore, applyMiddleware } from "redux";
+import thunkMiddleware from "redux-thunk";
+import { createLogger } from "redux-logger";
+import axios from "axios";
+import {
+ useMoralis,
+ useMoralisWeb3Api,
+ useMoralisWeb3ApiCall,
+ useNFTBalances,
+ } from "react-moralis";
+
+let game = null;
+
+function PhaserApp() {
+ const { authenticate, isAuthenticated, isAuthenticating, logout } =
+ useMoralis();
+ const [loaded, setLoaded] = useState(false);
+
+ const login = async () => {
+ if (!isAuthenticated) {
+ await authenticate({ signingMessage: "Log in with Moralis" })
+ .then(function (user) {
+ console.log("logged in user: ", user);
+ console.log(user?.get("ethAddress"));
+ startGame();
+ })
+ .catch(function (error) {
+ console.log(error);
+ });
+ }
+ };
+
+ const logOut = async () => {
+ await logout();
+ console.log("logged out user");
+ };
+
+ if (!loaded) {
+ setLoaded(true);
+ // Phaser game configuration
+ const config = {
+ type: Phaser.AUTO,
+ parent: "game-container",
+ autoCenter: Phaser.Scale.CENTER_HORIZONTALLY,
+ autoFocus: true,
+ fps: {
+ target: 60,
+ },
+ physics: {
+ default: "arcade",
+ arcade: {
+ gravity: { y: 200 },
+ debug: false,
+ },
+ },
+ backgroundColor: "#282c34",
+ scale: {
+ mode: Phaser.Scale.ScaleModes.NONE,
+ },
+ scene: [Boot, Preloader, MainMenu, MainGame],
+ };
+
+ // Initialise the Phaser Canvas
+ if (game === null) {
+ game = new Phaser.Game(config);
+ }
+ }
+ return (
+
+
+
+ );
+}
+
+export default PhaserApp;
\ No newline at end of file
diff --git a/client/src/components/Phaser/Scenes/Boot.js b/client/src/components/Phaser/Scenes/Boot.js
new file mode 100644
index 000000000..372f969a1
--- /dev/null
+++ b/client/src/components/Phaser/Scenes/Boot.js
@@ -0,0 +1,19 @@
+import Phaser from "phaser";
+
+export default class Boot extends Phaser.Scene
+{
+ constructor ()
+ {
+ super('Boot');
+ }
+
+ preload ()
+ {
+ this.load.image('loading', 'assets/phaser/loading.png');
+ }
+
+ create ()
+ {
+ this.scene.start('Preloader');
+ }
+}
diff --git a/client/src/components/Phaser/Scenes/Door.js b/client/src/components/Phaser/Scenes/Door.js
new file mode 100644
index 000000000..dea41ca68
--- /dev/null
+++ b/client/src/components/Phaser/Scenes/Door.js
@@ -0,0 +1,289 @@
+import Phaser from "phaser";
+
+export default class Door extends Phaser.GameObjects.Container
+{
+ constructor (name, scene, x, y)
+ {
+ super(scene, x, y);
+
+ this.name = name;
+
+ this.background = scene.add.image(0, 0, 'assets', 'doorBackground');
+ this.door = scene.add.sprite(0, 0, 'assets', 'door1');
+ this.character = scene.add.image(0, 0, 'assets', 'bandit1');
+ this.characterFrame = 'bandit1';
+
+ this.isOpen = false;
+ this.isBandit = false;
+ this.isHats = false;
+ this.isDead = false;
+ this.wasBandit = (scene.doors.length % 2) ? true : false;
+
+ this.hats = 0;
+ this.timeToOpen = Number.MAX_SAFE_INTEGER;
+ this.timeToClose = Number.MAX_SAFE_INTEGER;
+ this.timeToKill = 0;
+
+ this.characters = [
+ 'bandit1',
+ 'bandit2',
+ 'cowboy1',
+ 'cowboy2',
+ 'hat'
+ ];
+
+ this.add([ this.background, this.character, this.door ]);
+
+ this.setSize(200, 400);
+ this.setInteractive();
+
+ this.on('pointerup', this.shoot, this);
+
+ scene.add.existing(this);
+ }
+
+ destroy ()
+ {
+ this.off('pointerup');
+ }
+
+ start (time)
+ {
+ this.timeToOpen = time + Phaser.Math.RND.between(500, 4000);
+ }
+
+ reset (time)
+ {
+ this.isOpen = false;
+ this.isBandit = false;
+ this.isHats = false;
+ this.isDead = false;
+
+ this.door.play('doorClose');
+
+ this.timeToOpen = time + Phaser.Math.RND.between(500, 4000);
+ }
+
+ openDoor (time)
+ {
+ this.isOpen = true;
+ this.isBandit = false;
+ this.isHats = false;
+ this.isDead = false;
+
+ this.characterFrame = Phaser.Utils.Array.GetRandom(this.characters);
+
+ // When should this door close again?
+ const duration = Phaser.Math.RND.between(this.scene.closeDurationLow, this.scene.closeDurationHigh);
+
+ this.timeToClose = time + duration;
+
+ if (this.characterFrame === 'bandit1' || this.characterFrame === 'bandit2')
+ {
+ this.isBandit = true;
+ }
+ else if (this.characterFrame === 'hat')
+ {
+ this.isHats = true;
+
+ // Pick random number of hats
+ this.hats = Phaser.Math.RND.between(2, 5);
+
+ this.characterFrame += this.hats.toString();
+ }
+ else
+ {
+ this.timeToClose = time + (duration / 2);
+ }
+
+ // If we had a citizen or hats on our last go, we have to have a bandit now
+ if (!this.wasBandit && !this.isBandit)
+ {
+ this.isHats = false;
+ this.hats = 0;
+ this.isBandit = true;
+ this.characterFrame = (Math.random() > 0.5) ? 'bandit1' : 'bandit2';
+ this.timeToClose = time + duration;
+ }
+
+ this.character.setFrame(this.characterFrame);
+ this.character.setScale(1).setAlpha(1);
+
+ if (this.isBandit)
+ {
+ this.timeToKill = time + (duration * this.scene.killDelay);
+ }
+
+ this.scene.sound.play('door');
+
+ this.door.play('doorOpen');
+ }
+
+ closeDoor (time)
+ {
+ this.door.play('doorClose');
+
+ this.isOpen = false;
+ this.wasBandit = this.isBandit;
+
+ if (!this.isBandit && !this.isHats && !this.isDead)
+ {
+ this.scene.addGold(this.x, this.y);
+ }
+
+ // When should this door open again?
+ this.timeToOpen = time + Phaser.Math.RND.between(2000, 4000);
+ }
+
+ shoot ()
+ {
+ if (!this.isOpen || this.scene.isPaused)
+ {
+ return;
+ }
+
+ this.scene.sound.play('shot');
+
+ if (this.isDead)
+ {
+ // We will want to hear the gunshot, but not actually do anything with it
+ return;
+ }
+
+ if (this.isBandit)
+ {
+ this.shootCharacter(true);
+ }
+ else
+ {
+ if (this.isHats)
+ {
+ this.shootHat();
+ }
+ else
+ {
+ this.shootCharacter(false);
+ }
+ }
+ }
+
+ shootCharacter (closeDoor)
+ {
+ this.isDead = true;
+
+ this.characterFrame += 'Dead';
+
+ this.character.setFrame(this.characterFrame);
+
+ this.scene.sound.play('scream' + Phaser.Math.RND.between(1, 3));
+
+ this.scene.tweens.add({
+ targets: this.character,
+ scaleX: 0.5,
+ scaleY: 0.5,
+ duration: 300,
+ onComplete: () => {
+ if (closeDoor)
+ {
+ this.closeDoor(this.scene.game.getTime());
+ }
+ else
+ {
+ this.scene.levelFail();
+ }
+ }
+ });
+
+ // No more shots at this door
+ if (!closeDoor)
+ {
+ this.off('pointerup');
+ this.scene.isPaused = true;
+ }
+ }
+
+ shootHat ()
+ {
+ if (this.hats > 0)
+ {
+ this.scene.addHat(this.x, this.y, this.hats);
+
+ this.hats--;
+
+ this.characterFrame = 'hat' + this.hats;
+ }
+
+ this.character.setFrame(this.characterFrame);
+ }
+
+ shootYou ()
+ {
+ this.off('pointerup');
+
+ this.scene.isPaused = true;
+
+ // Shots
+
+ let shot1 = this.scene.add.image(this.x, this.y, 'assets', this.characterFrame + 'shot1');
+ let shot2 = this.scene.add.image(this.x, this.y, 'assets', this.characterFrame + 'shot2');
+
+ this.scene.sound.play('banditShot');
+ this.scene.sound.play('banditShot', { delay: 0.25 });
+ this.scene.sound.play('banditShot', { delay: 0.5 });
+
+ this.scene.tweens.add({
+ targets: shot1,
+ alpha: 0,
+ duration: 200,
+ ease: 'Power2'
+ });
+
+ this.scene.tweens.add({
+ targets: shot2,
+ alpha: 0,
+ duration: 200,
+ delay: 200,
+ ease: 'Power2',
+ onComplete: () => {
+ this.scene.killed(this.x, this.y);
+ }
+ });
+
+ // Gun smoke rising from the bandit
+
+ let smoke1 = this.scene.add.image(this.x, this.y, 'assets', this.characterFrame + 'smoke1');
+ let smoke2 = this.scene.add.image(this.x, this.y, 'assets', this.characterFrame + 'smoke2');
+
+ this.scene.tweens.add({
+ targets: smoke1,
+ props: {
+ y: { value: 150, duration: 1000, ease: 'Sine.easeInOut' },
+ alpha: { value: 0, duration: 250, ease: 'Power2', delay: 750 }
+ }
+ });
+
+ this.scene.tweens.add({
+ targets: smoke2,
+ props: {
+ y: { value: 150, duration: 1000, ease: 'Sine.easeInOut', delay: 500 },
+ alpha: { value: 0, duration: 250, ease: 'Power2', delay: 1250 }
+ }
+ });
+ }
+
+ update (time)
+ {
+ if (!this.isOpen && time >= this.timeToOpen)
+ {
+ this.openDoor(time);
+ }
+ else if (this.isOpen && time >= this.timeToClose)
+ {
+ this.closeDoor(time);
+ }
+ else if (this.isOpen && this.isBandit && !this.isDead && time >= this.timeToKill)
+ {
+ this.shootYou();
+ }
+ }
+}
diff --git a/client/src/components/Phaser/Scenes/Game.js b/client/src/components/Phaser/Scenes/Game.js
new file mode 100644
index 000000000..d9d20276f
--- /dev/null
+++ b/client/src/components/Phaser/Scenes/Game.js
@@ -0,0 +1,281 @@
+import Phaser from "phaser";
+import Door from './Door.js';
+
+export default class MainGame extends Phaser.Scene
+{
+ constructor ()
+ {
+ super('MainGame');
+
+ this.hats = null;
+ this.goals = null;
+ this.gold = null;
+ this.doors = null;
+
+ this.isPaused = false;
+ this.goalsComplete = 0;
+ this.sign = null;
+
+ this.level = 1;
+ this.levelImage = null;
+
+ this.killDelay = 0.7;
+ this.closeDurationLow = 2000;
+ this.closeDurationHigh = 4000;
+ }
+
+ create ()
+ {
+ this.add.image(512, 384, 'background');
+
+ // Level text
+ this.add.image(450, 650, 'assets', 'levelText');
+
+ this.levelImage = this.add.image(600, 650, 'assets', '1');
+
+ this.createGoals();
+ this.createDoors();
+
+ this.hats = this.add.group({
+ defaultKey: 'assets',
+ defaultFrame: 'hat',
+ key: 'assets',
+ frame: 'hat',
+ active: false,
+ visible: false,
+ repeat: 32,
+ maxSize: 32
+ });
+
+ this.gold = this.add.group({
+ defaultKey: 'assets',
+ defaultFrame: 'gold',
+ key: 'assets',
+ frame: 'gold',
+ active: false,
+ visible: false,
+ repeat: 11,
+ maxSize: 12
+ });
+
+ this.isPaused = false;
+
+ this.level = 1;
+ this.killDelay = 0.8;
+ this.closeDurationLow = 2000;
+ this.closeDurationHigh = 4000;
+
+ this.doors.forEach((door) => {
+ door.start(this.game.getTime());
+ });
+ }
+
+ createGoals ()
+ {
+ this.goals = [];
+ this.goalsComplete = 0;
+
+ for (let i = 1; i <= 12; i++)
+ {
+ this.goals.push(this.add.image(0, 0, 'assets', i));
+ }
+
+ Phaser.Actions.GridAlign(this.goals, {
+ width: 12,
+ height: 1,
+ cellWidth: 80,
+ cellHeight: 36,
+ x: 80,
+ y: 86
+ });
+ }
+
+ createDoors ()
+ {
+ this.doors = [];
+
+ let doorWidth = 200;
+ let doorSpace = Math.floor((1024 - (doorWidth * 4)) / 5);
+
+ let x = 100 + doorSpace;
+ let y = 352;
+
+ for (let i = 1; i <= 4; i++)
+ {
+ this.doors.push(new Door('Door' + i, this, x, y))
+
+ x += doorWidth + doorSpace;
+ }
+ }
+
+ addGold (x, y)
+ {
+ let target = this.goals[this.goalsComplete];
+
+ let gold = this.gold.get(x + 50, y + 100);
+
+ gold.setActive(true).setVisible(true);
+
+ this.sound.play('money');
+
+ this.tweens.add({
+ targets: gold,
+ x: target.x,
+ y: target.y,
+ duration: 600,
+ ease: 'Quad.easeOut',
+ onComplete: () => {
+ target.setVisible(false);
+ }
+ });
+
+ this.goalsComplete++;
+
+ if (this.goalsComplete === 12)
+ {
+ this.levelComplete();
+ }
+ }
+
+ addHat (x, y, stackPosition)
+ {
+ y = 180 + (30 * (5 - stackPosition));
+
+ let hat = this.hats.get(x, y);
+
+ hat.setActive(true).setVisible(true);
+ hat.setScale(1).setAlpha(1);
+
+ const destX = Phaser.Math.RND.between(x - 400, x + 400);
+ const destY = y - 400;
+
+ this.tweens.add({
+ targets: hat,
+ x: destX,
+ y: destY,
+ angle: 960,
+ duration: 1000,
+ ease: 'Quad.easeOut',
+ onComplete: () => {
+ hat.setActive(false);
+ hat.setVisible(false);
+ }
+ });
+ }
+
+ levelFail ()
+ {
+ this.isPaused = true;
+
+ this.sign = this.add.image(512, -200, 'assets', 'gameOver');
+
+ this.sound.play('gameOver');
+
+ this.tweens.add({
+ targets: this.sign,
+ y: 384,
+ ease: 'Bounce.easeOut',
+ duration: 1500,
+ onComplete: () => {
+ this.input.once('pointerdown', () => this.scene.start('MainMenu'));
+ }
+ });
+ }
+
+ levelComplete ()
+ {
+ this.isPaused = true;
+
+ this.sign = this.add.image(512, -200, 'assets', 'levelComplete');
+
+ this.sound.play('levelComplete');
+
+ this.tweens.add({
+ targets: this.sign,
+ y: 384,
+ ease: 'Bounce.easeOut',
+ duration: 1500,
+ onComplete: () => {
+ this.input.once('pointerdown', () => this.nextLevel());
+ }
+ });
+ }
+
+ nextLevel ()
+ {
+ this.goals.forEach((goal, index) => {
+ goal.setFrame((index + 1).toString());
+ goal.setVisible(true);
+ });
+
+ this.gold.getChildren().forEach((gold) => {
+ gold.setVisible(false);
+ gold.setActive(false);
+ });
+
+ // Reset everything
+ this.doors.forEach((door) => {
+ door.reset(this.game.getTime());
+ });
+
+ this.goalsComplete = 0;
+
+ // Change difficulty
+
+ if (this.level < 5)
+ {
+ this.killDelay -= 0.1;
+ }
+
+ if (this.level < 10)
+ {
+ this.closeDurationLow -= 100;
+ this.closeDurationHigh -= 200;
+ }
+
+ // Change level counter
+ this.level++;
+
+ this.levelImage.setFrame(this.level);
+
+ this.sign.setVisible(false);
+
+ this.isPaused = false;
+ }
+
+ killed (x, y)
+ {
+ // Bullet holes on the screen
+
+ let offsetX = 100;
+
+ for (let i = 0; i < 3; i++)
+ {
+ let x = Phaser.Math.RND.between(offsetX, offsetX + 200);
+ let y = Phaser.Math.RND.between(200, 600);
+
+ let hole = this.add.image(x, y, 'bulletHole').setAlpha(0);
+
+ this.tweens.add({
+ targets: hole,
+ alpha: 1,
+ duration: 30,
+ delay: 200 * i
+ });
+
+ offsetX += 340;
+ }
+
+ this.levelFail();
+ }
+
+ update (time)
+ {
+ if (!this.isPaused)
+ {
+ this.doors.forEach((door) => {
+ door.update(time);
+ });
+ }
+ }
+}
diff --git a/client/src/components/Phaser/Scenes/MainMenu.js b/client/src/components/Phaser/Scenes/MainMenu.js
new file mode 100644
index 000000000..203f855a4
--- /dev/null
+++ b/client/src/components/Phaser/Scenes/MainMenu.js
@@ -0,0 +1,64 @@
+import Phaser from "phaser";
+
+export default class MainMenu extends Phaser.Scene
+{
+ constructor ()
+ {
+ super('MainMenu');
+ }
+
+ create ()
+ {
+ this.add.image(512, 384, 'title');
+
+ let sign = this.add.image(512, -400, 'logo');
+
+ this.tweens.add({
+ targets: sign,
+ y: 180,
+ ease: 'Bounce.easeOut',
+ duration: 2000
+ });
+
+ let cactus1 = this.add.image(150, 680, 'assets', 'cactus');
+ let cactus2 = this.add.image(880, 680, 'assets', 'cactus').setFlipX(true);
+
+ this.tweens.add({
+ targets: cactus1,
+ props: {
+ scaleX: { value: 0.9, duration: 250 },
+ scaleY: { value: 1.1, duration: 250 },
+ angle: { value: -20, duration: 500, delay: 250 },
+ y: { value: 660, duration: 250 }
+ },
+ ease: 'Sine.easeInOut',
+ repeat: -1,
+ yoyo: true
+ });
+
+ this.tweens.add({
+ targets: cactus2,
+ props: {
+ scaleX: { value: 0.9, duration: 250 },
+ scaleY: { value: 1.1, duration: 250 },
+ angle: { value: 20, duration: 500, delay: 250 },
+ y: { value: 660, duration: 250 }
+ },
+ ease: 'Sine.easeInOut',
+ repeat: -1,
+ yoyo: true
+ });
+
+ this.music = this.sound.play('music', { loop: true });
+
+ this.input.once('pointerdown', () => {
+
+ this.sound.stopAll();
+
+ this.sound.play('shot');
+
+ this.scene.start('MainGame');
+
+ });
+ }
+}
diff --git a/client/src/components/Phaser/Scenes/Preloader.js b/client/src/components/Phaser/Scenes/Preloader.js
new file mode 100644
index 000000000..10c5af18c
--- /dev/null
+++ b/client/src/components/Phaser/Scenes/Preloader.js
@@ -0,0 +1,59 @@
+import Phaser from "phaser";
+
+export default class Preloader extends Phaser.Scene
+{
+ constructor ()
+ {
+ super('Preloader');
+ }
+
+ preload ()
+ {
+ this.loading = this.add.image(512, 384, 'loading');
+
+ this.load.setPath('assets/phaser/');
+
+ this.load.image('start');
+ this.load.image('title');
+ this.load.image('logo');
+ this.load.image('background');
+ this.load.image('bulletHole', 'bullet-hole.png');
+ this.load.atlas('assets', 'bank-panic.png', 'bank-panic.json');
+
+ this.load.audio('shot', [ 'shot.ogg', 'shot.m4a', 'shot.mp3' ]);
+ this.load.audio('banditShot', [ '50cal.ogg', '50cal.m4a', '50cal.mp3' ]);
+ this.load.audio('money', [ 'money.ogg', 'money.m4a', 'money.mp3' ]);
+ this.load.audio('levelComplete', [ 'complete.ogg', 'complete.m4a', 'complete.mp3' ]);
+ this.load.audio('gameOver', [ 'gameover.ogg', 'gameover.m4a', 'gameover.mp3' ]);
+ this.load.audio('music', [ 'music.ogg', 'music.m4a', 'music.mp3' ]);
+ this.load.audio('door', [ 'door.ogg', 'door.m4a', 'door.mp3' ]);
+ this.load.audio('scream1', [ 'scream1.ogg', 'scream1.m4a', 'scream1.mp3' ]);
+ this.load.audio('scream2', [ 'scream2.ogg', 'scream2.m4a', 'scream2.mp3' ]);
+ this.load.audio('scream3', [ 'scream3.ogg', 'scream3.m4a', 'scream3.mp3' ]);
+ }
+
+ create ()
+ {
+ // Create our global animations
+
+ this.anims.create({
+ key: 'doorOpen',
+ frames: this.anims.generateFrameNames('assets', { prefix: 'door', start: 1, end: 5 }),
+ frameRate: 20
+ });
+
+ this.anims.create({
+ key: 'doorClose',
+ frames: this.anims.generateFrameNames('assets', { prefix: 'door', start: 5, end: 1 }),
+ frameRate: 20
+ });
+
+ this.loading.setTexture('start');
+
+ this.loading.setInteractive();
+
+ this.loading.once('pointerdown', () => {
+ this.scene.start('MainMenu');
+ });
+ }
+}
diff --git a/client/src/components/Phaser/Scenes/boot.json b/client/src/components/Phaser/Scenes/boot.json
new file mode 100644
index 000000000..5d0a2e912
--- /dev/null
+++ b/client/src/components/Phaser/Scenes/boot.json
@@ -0,0 +1,3 @@
+{
+ "module": true
+}
diff --git a/client/src/components/Phaser/index.css b/client/src/components/Phaser/index.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/components/Phaser/main.js b/client/src/components/Phaser/main.js
new file mode 100644
index 000000000..6d4ee600d
--- /dev/null
+++ b/client/src/components/Phaser/main.js
@@ -0,0 +1,15 @@
+import Boot from './Boot.js';
+import Preloader from './Preloader.js';
+import MainMenu from './MainMenu.js';
+import MainGame from './Game.js';
+
+const config = {
+ type: Phaser.AUTO,
+ width: 1024,
+ height: 768,
+ backgroundColor: '#2e91f3',
+ parent: 'phaser-example',
+ scene: [ Boot, Preloader, MainMenu, MainGame ]
+};
+
+let game = new Phaser.Game(config);
diff --git a/client/src/index.js b/client/src/index.js
deleted file mode 100644
index c417dbd6e..000000000
--- a/client/src/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import App from "./App";
-import "@magiclabs/ui/dist/cjs/index.css";
-import { ThemeProvider } from "@magiclabs/ui";
-
-ReactDOM.render(
-
-
-
-
- ,
- document.getElementById("root")
-);
\ No newline at end of file
diff --git a/client/src/index.jsx b/client/src/index.jsx
new file mode 100644
index 000000000..aee2c03f1
--- /dev/null
+++ b/client/src/index.jsx
@@ -0,0 +1,42 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import App from "./App";
+import { MoralisProvider } from "react-moralis";
+import "@magiclabs/ui/dist/cjs/index.css";
+import { ThemeProvider } from "@magiclabs/ui";
+
+// Moralis Configuration
+const APP_ID = process.env.REACT_APP_MORALIS_APPLICATION_ID;
+const SERVER_URL = process.env.REACT_APP_MORALIS_SERVER_URL;
+
+const Application = () => {
+ const isServerInfo = APP_ID && SERVER_URL ? true : false;
+
+ // Validate server info is in .env
+ if (!APP_ID || !SERVER_URL)
+ throw new Error(
+ "Missing Moralis Application ID or Server URL. Make sure to set your .env file."
+ );
+ if (isServerInfo)
+ return (
+
+
+
+ );
+ else {
+ return (
+
+ );
+ }
+};
+
+ReactDOM.render(
+
+
+ ,
+ document.getElementById("root")
+);
\ No newline at end of file
diff --git a/client/yarn.lock b/client/yarn.lock
index 142651492..b607617cc 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -18789,10 +18789,10 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-moralis@^1.2.1:
- version "1.3.5"
- resolved "https://registry.yarnpkg.com/react-moralis/-/react-moralis-1.3.5.tgz#d811e8c7e16d6ff3988fc2115a6bcce7e85263bd"
- integrity sha512-Z78gjQStR301biEzfJe4MZYGqWZh3bk8hfBzguobWM/98MR7DEBiCCWEEeUqkphc2zkCzo9mKRGV/YTdTdD/vg==
+react-moralis@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/react-moralis/-/react-moralis-1.4.0.tgz#782b17b01666d8da0f2d92951ca1371e4d2e089e"
+ integrity sha512-NYt/LMARpWVLQ/giWQllu2lZxNHfq/EzKmGz7n5HQ7DQBQwfRpZUv8dwvx6phlC8yNdp6osMP8Y0vnh4+Sf2qQ==
dependencies:
fast-deep-equal "^3.1.3"
immer "^9.0.7"