Compare commits

..

No commits in common. "main" and "spritesheet" have entirely different histories.

80 changed files with 383 additions and 539 deletions

View File

@ -26,7 +26,8 @@ List of things to implement following the "release" of the minimum viable produc
## Other ## Other
- [ ] Disable player action during combat phase.
- [ ] Add sound effects - [ ] Add sound effects
- [x] Tutorial image/mission - [x] Tutorial image/mission
- [x] Pause menu - [ ] Pause menu
- [x] Score screen when winning/losing map - [x] Score screen when winning/losing map

View File

@ -4,19 +4,8 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="style.css" />
<title>Bastion: The Watchers Lament</title> <link href="https://fonts.googleapis.com/css?family=Aclonica" rel="stylesheet" />
<meta property="og:title" content="Bastion: The Watchers Lament" /> <title>Bastion: The Watcher's Lament</title>
<meta
property="og:description"
content="A free, open source, browser based, tower defense game inspired by GemCraft: Frostborn Wrath. Playable NOW on PC!"
/>
<meta property="og:image" content="https://bastion.overflow.fun/favicon.png" />
<meta property="og:url" content="https://bastion.overflow.fun" />
<meta name="twitter:title" content="Bastion: The Watchers Lament" />
<meta
name="twitter:description"
content="A free, open source, browser based, tower defense game inspired by GemCraft: Frostborn Wrath. Playable NOW on PC!"
/>
</head> </head>
<body> <body>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>

BIN
public/aclonica.woff2 Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

BIN
public/assets/gui/banner_03.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

BIN
public/assets/gui/frame_c2_02.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
public/assets/gui/gem_frame.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

BIN
public/assets/gui/inventory.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 KiB

BIN
public/assets/gui/plank_14.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/assets/gui/shield_01.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/assets/gui/shield_02.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

BIN
public/assets/gui/skull_01.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/assets/gui/star.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
public/assets/gui/star_empty.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
public/assets/gui/title.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

View File

@ -2,15 +2,14 @@
{ {
"name": "basic", "name": "basic",
"sprite": "wood", "sprite": "wood",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 12, "textureArrayLength": 12,
"stats": { "stats": {
"health": 2, "health": 5,
"speed": 3, "speed": 4,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0.05, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0,
@ -21,12 +20,11 @@
{ {
"name": "quick", "name": "quick",
"sprite": "zombie", "sprite": "zombie",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 8, "textureArrayLength": 8,
"stats": { "stats": {
"health": 1, "health": 2,
"speed": 5, "speed": 6,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0,
@ -40,11 +38,10 @@
{ {
"name": "tank", "name": "tank",
"sprite": "skeleton", "sprite": "skeleton",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 12, "textureArrayLength": 12,
"stats": { "stats": {
"health": 5, "health": 12,
"speed": 2, "speed": 2,
"special": null, "special": null,
"resistance": { "resistance": {
@ -59,50 +56,29 @@
{ {
"name": "cloaker", "name": "cloaker",
"sprite": "hood", "sprite": "hood",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 12, "textureArrayLength": 12,
"stats": { "stats": {
"health": 7, "health": 12,
"speed": 3, "speed": 2,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0,
"divine": 1, "divine": 0,
"fire": 1, "fire": 0,
"ice": 1, "ice": 0,
"frostfire": 1 "frostfire": 0
} }
} }
}, },
{ {
"name": "demon", "name": "demon",
"sprite": "demon", "sprite": "demon",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 8, "textureArrayLength": 8,
"stats": { "stats": {
"health": 5, "health": 12,
"speed": 3, "speed": 2,
"special": null,
"resistance": {
"physical": 1,
"divine": -0.25,
"fire": 1,
"ice": 1,
"frostfire": 1
}
}
},
{
"name": "maker",
"sprite": "pumpkin",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 11,
"stats": {
"health": 5,
"speed": 3,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0,
@ -113,57 +89,17 @@
} }
} }
}, },
{ {
"name": "monster", "name": "maker",
"sprite": "green", "sprite": "pumpkin",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 12, "textureArrayLength": 11,
"stats": { "stats": {
"health": 30, "health": 11,
"speed": 1, "speed": 2,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0.05, "physical": 0,
"divine": 0.05,
"fire": -0.25,
"ice": 0.05,
"frostfire": 0.05
}
}
},
{
"name": "remaker",
"sprite": "orange",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 2,
"speed": 3,
"special": null,
"resistance": {
"physical": 0.05,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0
}
}
},
{
"name": "elite",
"sprite": "phood",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 2,
"speed": 3,
"special": null,
"resistance": {
"physical": 0.05,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0,

View File

@ -1,14 +1,14 @@
[ [
{ {
"name": "Fire Gem", "name": "Fire Gem",
"description": "Forged from molten lava, the Fire Gem imbues your tower's attacks and add extra fire damage. Well researched and common.", "description": "Forged from molten lava, the Fire Gem imbues your tower's attacks and add extra fire damage. It can be merged with any gem and is common.",
"color": "red", "color": "red",
"type": "Fire", "type": "Fire",
"totalLevels": 9, "totalLevels": 9,
"textures": [], "textures": [],
"cantCombineWith": [], "cantCombineWith": [],
"specialCombine": [], "specialCombine": [],
"initialGemValue": 75, "initialGemValue": 50,
"genericImprovements": [ "genericImprovements": [
{ {
"damageUp": 1, "damageUp": 1,
@ -16,6 +16,14 @@
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 0, "pierceUp": 0,
"gemValueUp": 50
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100 "gemValueUp": 100
}, },
{ {
@ -24,15 +32,7 @@
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 150 "gemValueUp": 100
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 2,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 250
}, },
{ {
"damageUp": 2, "damageUp": 2,
@ -40,31 +40,7 @@
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 400 "gemValueUp": 100
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 650
},
{
"damageUp": 2,
"attackSpeedUp": 100,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 750
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 900
}, },
{ {
"damageUp": 2, "damageUp": 2,
@ -72,25 +48,42 @@
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 1000 "gemValueUp": 100
}, },
{ {
"damageUp": 2, "damageUp": 2,
"attackSpeedUp": 250, "attackSpeedUp": 0,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 1200 "gemValueUp": 100
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
} }
], ],
"gemResistanceModifications": [ "gemResistanceModifications": [
{
"physical": 0,
"divine": 0,
"fire": 0.05,
"ice": 0,
"frostfire": 0
},
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
@ -101,14 +94,7 @@
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0.15, "fire": 0.25,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0.2,
"ice": 0, "ice": 0,
"frostfire": 0 "frostfire": 0
}, },
@ -129,7 +115,14 @@
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0.35, "fire": 0.3,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0.3,
"ice": 0, "ice": 0,
"frostfire": 0 "frostfire": 0
}, },
@ -146,93 +139,93 @@
"fire": 0.45, "fire": 0.45,
"ice": 0, "ice": 0,
"frostfire": 0 "frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0.5,
"ice": 0,
"frostfire": 0
} }
] ]
}, },
{ {
"name": "Yeti Gem", "name": "Yeti Gem",
"description": "Crafted from the essence of the frozen bears, the Yeti Gem enhances your tower's attacks with the chilling power of ice. It gives ice damage and is known for its rarity.", "description": "Yeti gem description. Something something, write this while drunk or something.",
"color": "#32e4fc", "color": "#32e4fc",
"type": "Yeti", "type": "Yeti",
"totalLevels": 8, "totalLevels": 8,
"textures": [], "textures": [],
"cantCombineWith": [], "cantCombineWith": [],
"specialCombine": [], "specialCombine": [],
"initialGemValue": 80, "initialGemValue": 10,
"genericImprovements": [ "genericImprovements": [
{
"damageUp": 1,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 100
},
{ {
"damageUp": 2, "damageUp": 2,
"attackSpeedUp": 0, "attackSpeedUp": 10,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 150 "gemValueUp": 0
}, },
{ {
"damageUp": 2, "damageUp": 2,
"attackSpeedUp": 0, "attackSpeedUp": 10,
"rangeUp": 2,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 250
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 400 "gemValueUp": 10
}, },
{ {
"damageUp": 2, "damageUp": 2,
"attackSpeedUp": 0, "attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 650
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 750 "gemValueUp": 0
}, },
{ {
"damageUp": 2, "damageUp": 2,
"attackSpeedUp": 0, "attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 900
},
{
"damageUp": 2,
"attackSpeedUp": 100,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 1000 "gemValueUp": 10
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
} }
], ],
"gemResistanceModifications": [ "gemResistanceModifications": [
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.05,
"frostfire": 0
},
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
@ -240,13 +233,6 @@
"ice": 0.1, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.15,
"frostfire": 0
},
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
@ -258,98 +244,112 @@
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0.25, "ice": 0.1,
"frostfire": 0.1 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0.3, "ice": 0.2,
"frostfire": 0.1 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0.35, "ice": 0.1,
"frostfire": 0.2 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0.4, "ice": 0.2,
"frostfire": 0.25 "frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.1,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.2,
"frostfire": 0
} }
] ]
}, },
{ {
"name": "Titalium Gem", "name": "Titalium Gem",
"description": "Created from distilled creep eyes, the Titalium gem is known for boosting tower range, at the sacrifice for other stats.", "description": "Titalium gem description. Something something zombie creeps working for you something something.",
"color": "pink", "color": "pink",
"type": "Titalium", "type": "Titalium",
"totalLevels": 7, "totalLevels": 7,
"textures": [], "textures": [],
"cantCombineWith": [], "cantCombineWith": [],
"specialCombine": [], "specialCombine": [],
"initialGemValue": 100, "initialGemValue": 10,
"genericImprovements": [ "genericImprovements": [
{
"damageUp": 1,
"attackSpeedUp": 0,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 200
},
{ {
"damageUp": 2, "damageUp": 2,
"attackSpeedUp": 0, "attackSpeedUp": 10,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 350
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 2,
"gemValueUp": 450
},
{
"damageUp": 2,
"attackSpeedUp": 200,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 600
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 850
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 0, "pierceUp": 1,
"gemValueUp": 900 "gemValueUp": 0
}, },
{ {
"damageUp": 2, "damageUp": 2,
"attackSpeedUp": 0, "attackSpeedUp": 10,
"rangeUp": 2, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 0, "pierceUp": 1,
"gemValueUp": 900 "gemValueUp": 10
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
} }
], ],
"gemResistanceModifications": [ "gemResistanceModifications": [
@ -357,190 +357,191 @@
"physical": 0, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0.05, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.2,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0.1, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0.1, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.2,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0.15, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0.2, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.2,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0.25, "physical": 0,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0
},
{
"physical": 0.3,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0 "frostfire": 0
} }
] ]
}, },
{ {
"name": "Soulforge Gem", "name": "Soulforge Gem",
"description": "Made from the wandering souls of the damned, the Soulforge gem accelerates your towers cooldowns and acts as a repellant for all things unholy.", "description": "Soulforge gem description, have to write later.",
"color": "gray", "color": "gray",
"type": "Soulforge", "type": "Soulforge",
"totalLevels": 7, "totalLevels": 2,
"textures": [], "textures": [],
"cantCombineWith": [], "cantCombineWith": [],
"specialCombine": [], "specialCombine": [],
"initialGemValue": 150, "initialGemValue": 10,
"genericImprovements": [ "genericImprovements": [
{ {
"damageUp": 0, "damageUp": 2,
"attackSpeedUp": 100, "attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 200
},
{
"damageUp": 0,
"attackSpeedUp": 100,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 350 "gemValueUp": 0
}, },
{ {
"damageUp": 0, "damageUp": 2,
"attackSpeedUp": 150, "attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 450
},
{
"damageUp": 0,
"attackSpeedUp": 100,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 600
},
{
"damageUp": 0,
"attackSpeedUp": 250,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 1, "pierceUp": 1,
"gemValueUp": 850 "gemValueUp": 10
}, },
{ {
"damageUp": 0, "damageUp": 2,
"attackSpeedUp": 400, "attackSpeedUp": 10,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 0, "pierceUp": 1,
"gemValueUp": 900 "gemValueUp": 0
}, },
{ {
"damageUp": 0, "damageUp": 2,
"attackSpeedUp": 500, "attackSpeedUp": 10,
"rangeUp": 0, "rangeUp": 0,
"timeToLiveUp": 0, "timeToLiveUp": 0,
"pierceUp": 0, "pierceUp": 1,
"gemValueUp": 900 "gemValueUp": 10
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
} }
], ],
"gemResistanceModifications": [ "gemResistanceModifications": [
{ {
"physical": 0, "physical": 0,
"divine": 0.2, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0.3, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.2,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0.4, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0.5, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.2,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0.6, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0.7, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.2,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0.75, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.1,
"frostfire": 0 "frostfire": 0
}, },
{ {
"physical": 0, "physical": 0,
"divine": 0.8, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0.2,
"frostfire": 0 "frostfire": 0
} }
] ]
}, },
{ {
"name": "Golden Gem", "name": "Golden Gem",
"description": "An inherently useless gem, providing no tower buffs, but high in monetary value. Highly sought after by all rich people.", "description": "An inherently useless gem, but high monetary value. Perhaps someone would pay a lot for this.",
"color": "gold", "color": "gold",
"type": "Gold", "type": "Gold",
"totalLevels": 1, "totalLevels": 1,
@ -570,21 +571,21 @@
}, },
{ {
"name": "Artifact", "name": "Artifact",
"description": "An insanely rare gem of unknown origin, packed with power. No one knows how it works, yet your towers oddly resonate stronger with it.", "description": "A wildly rare gem, packed with power. No monetary value, yet your towers oddly resonate stronger with it.",
"color": "blue", "color": "blue",
"type": "Artifact", "type": "Artifact",
"totalLevels": 1, "totalLevels": 1,
"textures": [], "textures": [],
"cantCombineWith": [], "cantCombineWith": [],
"specialCombine": [], "specialCombine": [],
"initialGemValue": 1000, "initialGemValue": 0,
"genericImprovements": [ "genericImprovements": [
{ {
"damageUp": 10, "damageUp": 10,
"attackSpeedUp": 100, "attackSpeedUp": 100,
"rangeUp": 2, "rangeUp": 2,
"timeToLiveUp": 200, "timeToLiveUp": 200,
"pierceUp": 3, "pierceUp": 20,
"gemValueUp": 0 "gemValueUp": 0
} }
], ],

View File

@ -9,13 +9,13 @@
"projectileTexturesArrayLength": 5, "projectileTexturesArrayLength": 5,
"description": "The building block of society, nothing more basic exists.", "description": "The building block of society, nothing more basic exists.",
"stats": { "stats": {
"damage": 1, "damage": 2,
"cooldown": 1500, "cooldown": 1500,
"gemSlotsAmount": 1, "gemSlotsAmount": 1,
"cost": 25, "cost": 50,
"range": 3, "range": 3,
"timeToLive": 20, "timeToLive": 20,
"pierce": 1 "pierce": 2
} }
}, },
{ {
@ -26,15 +26,15 @@
"projectile": "blue", "projectile": "blue",
"projectileTextures": [], "projectileTextures": [],
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Shoots 8 projectiles in a circle, they may miss.", "description": "If you feel a little circular.",
"stats": { "stats": {
"damage": 1, "damage": 3,
"cooldown": 2000, "cooldown": 4000,
"gemSlotsAmount": 2, "gemSlotsAmount": 2,
"cost": 55, "cost": 100,
"range": 3, "range": 2.5,
"timeToLive": 18, "timeToLive": 12,
"pierce": 5 "pierce": 30
} }
}, },
{ {
@ -45,15 +45,15 @@
"projectile": "yellow", "projectile": "yellow",
"projectileTextures": [], "projectileTextures": [],
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Zap zap zap! This towers shots connect to other enemies!", "description": "Zap zap zap!",
"stats": { "stats": {
"damage": 2, "damage": 3,
"cooldown": 3500, "cooldown": 3500,
"gemSlotsAmount": 2, "gemSlotsAmount": 2,
"cost": 110, "cost": 150,
"range": 3, "range": 3,
"timeToLive": 12, "timeToLive": 12,
"pierce": 1 "pierce": 10
} }
}, },
{ {
@ -64,13 +64,13 @@
"projectile": "blue", "projectile": "blue",
"projectileTextures": [], "projectileTextures": [],
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Doesn't shoot, instead buffs other towers with some of its power.", "description": "Doesn't shoot, instead buffs other towers with 50% of its power.",
"stats": { "stats": {
"damage": 3, "damage": 4,
"cooldown": 1000, "cooldown": 1000,
"gemSlotsAmount": 3, "gemSlotsAmount": 3,
"cost": 200, "cost": 200,
"range": 2.5, "range": 2,
"timeToLive": 15, "timeToLive": 15,
"pierce": 5 "pierce": 5
} }
@ -83,15 +83,15 @@
"projectile": "white", "projectile": "white",
"projectileTextures": [], "projectileTextures": [],
"projectileTexturesArrayLength": 5, "projectileTexturesArrayLength": 5,
"description": "Behaves like the Basic Tower, only its shots stop creeps in their tracks!", "description": "Like a regular tower, just slows down creeps.",
"stats": { "stats": {
"damage": 2, "damage": 2,
"cooldown": 2200, "cooldown": 2000,
"gemSlotsAmount": 2, "gemSlotsAmount": 3,
"cost": 80, "cost": 125,
"range": 3.25, "range": 2.5,
"timeToLive": 12, "timeToLive": 12,
"pierce": 2 "pierce": 1
} }
}, },
{ {
@ -104,13 +104,13 @@
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Shoots a quick, high pierce rail projectile at creeps.", "description": "Shoots a quick, high pierce rail projectile at creeps.",
"stats": { "stats": {
"damage": 5, "damage": 2,
"cooldown": 5020, "cooldown": 2000,
"gemSlotsAmount": 3, "gemSlotsAmount": 3,
"cost": 134, "cost": 125,
"range": 2.5, "range": 2.5,
"timeToLive": 12, "timeToLive": 12,
"pierce": 10 "pierce": 30
} }
}, },
{ {
@ -121,15 +121,15 @@
"projectile": "stone", "projectile": "stone",
"projectileTextures": [], "projectileTextures": [],
"projectileTexturesArrayLength": 1, "projectileTexturesArrayLength": 1,
"description": "Randomly places expiring traps on the track, instead of attacking creeps.", "description": "Randomly places traps on the track, instead of attacking creeps.",
"stats": { "stats": {
"damage": 2, "damage": 2,
"cooldown": 2000, "cooldown": 2000,
"gemSlotsAmount": 3, "gemSlotsAmount": 3,
"cost": 150, "cost": 125,
"range": 3, "range": 2.5,
"timeToLive": 400, "timeToLive": 400,
"pierce": 3 "pierce": 2
} }
}, },
{ {
@ -143,12 +143,12 @@
"description": "On top of a regular attack, this tower gives creeps a debuff, making them take more damage.", "description": "On top of a regular attack, this tower gives creeps a debuff, making them take more damage.",
"stats": { "stats": {
"damage": 2, "damage": 2,
"cooldown": 1750, "cooldown": 2000,
"gemSlotsAmount": 3, "gemSlotsAmount": 3,
"cost": 200, "cost": 125,
"range": 2.5, "range": 2.5,
"timeToLive": 12, "timeToLive": 12,
"pierce": 2 "pierce": 30
} }
} }
] ]

View File

@ -62,30 +62,15 @@
{ {
"firstCreepSpawnTick": 500, "firstCreepSpawnTick": 500,
"spawnIntervalTicks": 1000, "spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0, 0] "creeps": [0, 0, 0]
}, },
{ {
"firstCreepSpawnTick": 4000, "firstCreepSpawnTick": 4000,
"spawnIntervalTicks": 1000, "spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0, 0] "creeps": [0, 0, 0, 0]
} }
], ],
"offeredGems": [0, 1, 0, 1] "offeredGems": [0, 1]
},
{
"waves": [
{
"firstCreepSpawnTick": 500,
"spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
{
"firstCreepSpawnTick": 1000,
"spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}
],
"offeredGems": [0, 1, 0, 1]
} }
] ]
} }

View File

@ -99,7 +99,7 @@
{ {
"firstCreepSpawnTick": 500, "firstCreepSpawnTick": 500,
"spawnIntervalTicks": 500, "spawnIntervalTicks": 500,
"creeps": [0] "creeps": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 2]
} }
], ],
"offeredGems": [0, 0, 0, 0] "offeredGems": [0, 0, 0, 0]

View File

@ -10,13 +10,17 @@ export default class GameAssets {
public static Frame03Texture: PIXI.Texture; public static Frame03Texture: PIXI.Texture;
public static Frame04Texture: PIXI.Texture; public static Frame04Texture: PIXI.Texture;
public static Frame05Texture: PIXI.Texture; public static Frame05Texture: PIXI.Texture;
public static FrameInventory: PIXI.Texture;
public static FrameBackground: PIXI.Texture;
public static FrameTowerTab: PIXI.Texture; public static FrameTowerTab: PIXI.Texture;
public static VioletBackground: PIXI.Texture;
public static RedBackground: PIXI.Texture; public static RedBackground: PIXI.Texture;
public static GreenBackground: PIXI.Texture; public static GreenBackground: PIXI.Texture;
public static BlueBackground: PIXI.Texture; public static BlueBackground: PIXI.Texture;
public static YellowBackground: PIXI.Texture; public static YellowBackground: PIXI.Texture;
public static Button01Texture: PIXI.Texture; public static Button01Texture: PIXI.Texture;
public static Button02Texture: PIXI.Texture; public static Button02Texture: PIXI.Texture;
public static ButtonSmallTexture: PIXI.Texture;
public static HealthTexture: PIXI.Texture; public static HealthTexture: PIXI.Texture;
public static GoldTexture: PIXI.Texture; public static GoldTexture: PIXI.Texture;
public static WaveTexture: PIXI.Texture; public static WaveTexture: PIXI.Texture;
@ -36,6 +40,8 @@ export default class GameAssets {
public static PauseIconTexture: PIXI.Texture; public static PauseIconTexture: PIXI.Texture;
public static ExclamationIconTexture: PIXI.Texture; public static ExclamationIconTexture: PIXI.Texture;
public static FastForwardIconTexture: PIXI.Texture; public static FastForwardIconTexture: PIXI.Texture;
public static HomeIconTexture: PIXI.Texture;
public static HammerIconTexture: PIXI.Texture;
public static XIconTexture: PIXI.Texture; public static XIconTexture: PIXI.Texture;
public static PlusIconTexture: PIXI.Texture; public static PlusIconTexture: PIXI.Texture;
public static GemAmountIcons: PIXI.Texture[] = []; public static GemAmountIcons: PIXI.Texture[] = [];
@ -47,10 +53,8 @@ export default class GameAssets {
public static Gems: GemDefinition[]; public static Gems: GemDefinition[];
private static text; private static text;
private static counter = 0;
private static async Load(src) { private static async Load(src) {
this.text.text = `Loading asset: ${src} (${this.counter}/102)`; this.text.text = 'Loading asset: ' + src;
this.counter++;
return await PIXI.Assets.load({ return await PIXI.Assets.load({
src: src, src: src,
}); });
@ -87,14 +91,19 @@ export default class GameAssets {
Engine.app.stage.addChild(this.text); Engine.app.stage.addChild(this.text);
await Promise.all([ await Promise.all([
this.Load('./aclonica.woff2'),
this.Load('./assets/gui/button_01.png').then((texture) => (this.Button01Texture = texture)), this.Load('./assets/gui/button_01.png').then((texture) => (this.Button01Texture = texture)),
this.Load('./assets/gui/button_02.png').then((texture) => (this.Button02Texture = texture)), this.Load('./assets/gui/button_02.png').then((texture) => (this.Button02Texture = texture)),
this.Load('./assets/gui/button_small.png').then((texture) => (this.ButtonSmallTexture = texture)),
this.Load('./assets/gui/frame_01.png').then((texture) => (this.Frame01Texture = texture)), this.Load('./assets/gui/frame_01.png').then((texture) => (this.Frame01Texture = texture)),
this.Load('./assets/gui/frame_02.png').then((texture) => (this.Frame02Texture = texture)), this.Load('./assets/gui/frame_02.png').then((texture) => (this.Frame02Texture = texture)),
this.Load('./assets/gui/frame_03.png').then((texture) => (this.Frame03Texture = texture)), this.Load('./assets/gui/frame_03.png').then((texture) => (this.Frame03Texture = texture)),
this.Load('./assets/gui/frame_04.png').then((texture) => (this.Frame04Texture = texture)), this.Load('./assets/gui/frame_04.png').then((texture) => (this.Frame04Texture = texture)),
this.Load('./assets/gui/frame_05.png').then((texture) => (this.Frame05Texture = texture)), this.Load('./assets/gui/frame_05.png').then((texture) => (this.Frame05Texture = texture)),
this.Load('./assets/gui/frame_inv.png').then((texture) => (this.FrameInventory = texture)),
this.Load('./assets/gui/background_01.png').then((texture) => (this.FrameBackground = texture)),
this.Load('./assets/gui/background_02.png').then((texture) => (this.FrameTowerTab = texture)), this.Load('./assets/gui/background_02.png').then((texture) => (this.FrameTowerTab = texture)),
this.Load('./assets/gui/frame_violet.png').then((texture) => (this.VioletBackground = texture)),
this.Load('./assets/gui/frame_red.png').then((texture) => (this.RedBackground = texture)), this.Load('./assets/gui/frame_red.png').then((texture) => (this.RedBackground = texture)),
this.Load('./assets/gui/frame_green.png').then((texture) => (this.GreenBackground = texture)), this.Load('./assets/gui/frame_green.png').then((texture) => (this.GreenBackground = texture)),
this.Load('./assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = texture)), this.Load('./assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = texture)),
@ -116,7 +125,10 @@ export default class GameAssets {
this.Load('./assets/gui/title01.png').then((texture) => (this.TitleTexture = texture)), this.Load('./assets/gui/title01.png').then((texture) => (this.TitleTexture = texture)),
this.Load('./assets/gui/icons/play.png').then((texture) => (this.PlayIconTexture = texture)), this.Load('./assets/gui/icons/play.png').then((texture) => (this.PlayIconTexture = texture)),
this.Load('./assets/gui/icons/pause.png').then((texture) => (this.PauseIconTexture = texture)), this.Load('./assets/gui/icons/pause.png').then((texture) => (this.PauseIconTexture = texture)),
this.Load('./assets/gui/icons/pause.png').then((texture) => (this.PauseIconTexture = texture)),
this.Load('./assets/gui/icons/fastforward.png').then((texture) => (this.FastForwardIconTexture = texture)), this.Load('./assets/gui/icons/fastforward.png').then((texture) => (this.FastForwardIconTexture = texture)),
this.Load('./assets/gui/icons/home.png').then((texture) => (this.HomeIconTexture = texture)),
this.Load('./assets/gui/icons/hammer.png').then((texture) => (this.HammerIconTexture = texture)),
this.Load('./assets/gui/icons/cross.png').then((texture) => (this.XIconTexture = texture)), this.Load('./assets/gui/icons/cross.png').then((texture) => (this.XIconTexture = texture)),
this.Load('./assets/gui/icons/plus.png').then((texture) => (this.PlusIconTexture = texture)), this.Load('./assets/gui/icons/plus.png').then((texture) => (this.PlusIconTexture = texture)),
this.LoadMissions(), this.LoadMissions(),
@ -136,27 +148,9 @@ export default class GameAssets {
for (let idx = 0; idx < gems.length; idx++) { for (let idx = 0; idx < gems.length; idx++) {
const gem = this.Gems[idx]; const gem = this.Gems[idx];
const texture = await this.Load(`./assets/gems/${gem.type}_spritesheet.png`);
for (let i = 1; i <= gem.totalLevels; i++) { for (let i = 1; i <= gem.totalLevels; i++) {
const spritesheet = new PIXI.Spritesheet(texture, { const texture = await this.Load(`./assets/gems/${gem.type}/${i}.png`);
frames: { gem.textures[i - 1] = texture;
[`${gem.type}_${i}.png`]: {
frame: { x: (i - 1) * 64, y: 0, w: 64, h: 64 },
rotated: false,
trimmed: false,
spriteSourceSize: { x: 0, y: 0, w: 64, h: 64 },
sourceSize: { w: 64, h: 64 },
},
},
meta: {
image: `./assets/gems/${gem.type}_spritesheet.png`,
format: 'RGBA8888',
size: { w: 64 * gem.totalLevels, h: 64 },
scale: '1',
},
});
await spritesheet.parse();
gem.textures[i - 1] = spritesheet.textures[`${gem.type}_${i}.png`];
} }
} }
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
@ -170,8 +164,8 @@ export default class GameAssets {
this.Creeps = creeps; this.Creeps = creeps;
for (let idx = 0; idx < this.Creeps.length; idx++) { for (let idx = 0; idx < this.Creeps.length; idx++) {
const creep = this.Creeps[idx]; const creep = this.Creeps[idx];
const texture = await this.Load(`./assets/creeps/${creep.sprite}_spritesheet.png`);
for (let i = 0; i < creep.textureArrayLength; i++) { for (let i = 0; i < creep.textureArrayLength; i++) {
const texture = await this.Load(`./assets/creeps/${creep.sprite}/spritesheet.png`);
const spritesheet = new PIXI.Spritesheet(texture, { const spritesheet = new PIXI.Spritesheet(texture, {
frames: { frames: {
[`${creep.sprite}_${i}.png`]: { [`${creep.sprite}_${i}.png`]: {
@ -191,6 +185,7 @@ export default class GameAssets {
}); });
await spritesheet.parse(); await spritesheet.parse();
creep.textures[i] = spritesheet.textures[`${creep.sprite}_${i}.png`]; creep.textures[i] = spritesheet.textures[`${creep.sprite}_${i}.png`];
// creep.textures[i] = texture;
} }
} }
} }
@ -213,8 +208,6 @@ export default class GameAssets {
for (let idx = 0; idx < this.Towers.length; idx++) { for (let idx = 0; idx < this.Towers.length; idx++) {
const tower = this.Towers[idx]; const tower = this.Towers[idx];
for (let i = 0; i < tower.projectileTexturesArrayLength; i++) { for (let i = 0; i < tower.projectileTexturesArrayLength; i++) {
// My only grievance is that projectiles have to load like this, not like a spritesheet
// due to them not being a fixed w/h.
const projTexture = await this.Load(`./assets/projectiles/${tower.projectile}/${i}.png`); const projTexture = await this.Load(`./assets/projectiles/${tower.projectile}/${i}.png`);
tower.projectileTextures[i] = projTexture; tower.projectileTextures[i] = projTexture;
} }
@ -228,7 +221,7 @@ export default class GameAssets {
private static async LoadMission(missionUrl: string) { private static async LoadMission(missionUrl: string) {
const res = await fetch(missionUrl); const res = await fetch(missionUrl);
const mission = await res.json(); const mission = await res.json();
// console.log(`Loading mission: ${missionUrl} [${mission.name} / ${mission.mapImage.url}]`); console.log(`Loading mission: ${missionUrl} [${mission.name} / ${mission.mapImage.url}]`);
GameAssets.Missions.push(mission); GameAssets.Missions.push(mission);
GameAssets.MissionBackgrounds.push(await this.Load(mission.mapImage.url)); GameAssets.MissionBackgrounds.push(await this.Load(mission.mapImage.url));
} }

View File

@ -34,13 +34,12 @@ export class Engine {
public static TestSuite() { public static TestSuite() {
let params = new URLSearchParams(location.href); let params = new URLSearchParams(location.href);
if (params.entries().next().value[1] != 'game') return; if (params.entries().next().value[1] != 'game') return;
Engine.NotificationManager.Notify('Loaded.', 'danger'); Engine.NotificationManager.Notify('Loaded testing suite.', 'danger');
let tower = GameAssets.Towers[TowerType.Electric]; let tower = GameAssets.Towers[TowerType.Electric];
Engine.TowerManager.ToggleChoosingTowerLocation('RESET'); Engine.TowerManager.ToggleChoosingTowerLocation('RESET');
Engine.TowerManager.PlaceTower(tower, 6, 10, tower.behaviour, true); Engine.TowerManager.PlaceTower(tower, 6, 10, tower.behaviour, true);
Engine.GameScene.MissionStats.earnGold(2000); for (let i = 0; i < 29; i++) {
for (let i = 0; i < 24; i++) { this.GameScene.MissionStats.giveGem(new Gem(i % 4), true);
this.GameScene.MissionStats.giveGem(new Gem(i % 6), true);
} }
} }
} }

View File

@ -32,7 +32,6 @@ export type WaveDefinition = {
export type CreepDefinition = { export type CreepDefinition = {
name: string; name: string;
sprite: string; sprite: string;
tint: PIXI.ColorSource;
textures: PIXI.Texture[]; textures: PIXI.Texture[];
textureArrayLength: number; textureArrayLength: number;
stats: CreepStatsDefinition; stats: CreepStatsDefinition;
@ -114,9 +113,6 @@ export enum CreepType {
Cloaker = 3, Cloaker = 3,
Demon = 4, Demon = 4,
Maker = 5, Maker = 5,
Monster = 6,
Remaker = 7,
Elite = 8,
} }
export enum GemType { export enum GemType {

View File

@ -49,7 +49,6 @@ export default class Creep extends GameObject {
this.sprite.scale.x *= -1; this.sprite.scale.x *= -1;
this.sprite.anchor.set(0.5, 0.5); this.sprite.anchor.set(0.5, 0.5);
this.sprite.animationSpeed = 0.3; this.sprite.animationSpeed = 0.3;
this.sprite.tint = Assets.Creeps[this.creepType].tint;
this.sprite.play(); this.sprite.play();
this.id = id; this.id = id;
// Explanation: WaveManager spawns all creeps instantly, and since I don't want // Explanation: WaveManager spawns all creeps instantly, and since I don't want
@ -64,20 +63,20 @@ export default class Creep extends GameObject {
this.health = this.stats.health; this.health = this.stats.health;
this.maxHealth = this.stats.health; this.maxHealth = this.stats.health;
this.path = path; this.path = path;
// Added + 32 to center them.
this.x = path[0][0] * Engine.GridCellSize + Engine.GridCellSize / 2; this.x = path[0][0] * Engine.GridCellSize + Engine.GridCellSize / 2;
this.y = path[0][1] * Engine.GridCellSize + Engine.GridCellSize / 2; this.y = path[0][1] * Engine.GridCellSize + Engine.GridCellSize / 2;
// TODO: Unsubscribe from events once the scene is destroyed
Engine.GameScene.events.on( Engine.GameScene.events.on(
CreepEvents.TakenDamage, CreepEvents.TakenDamage,
(creepID, damage, gemResistanceModifications: CreepResistancesDefinition) => { (creepID, damage, gemResistanceModifications: CreepResistancesDefinition) => {
if (creepID != this.id) return; if (creepID != this.id) return;
if (this.effects.find((e) => e.effectEnum == CreepEffects.DebuffTowerDebuff)) { if (this.effects.find((e) => e.effectEnum == CreepEffects.DebuffTowerDebuff)) {
damage = damage * 1.5; damage = damage * 1.5;
console.log('multiplying damage, ' + damage);
} }
// Apply resistances. // Apply resistances.
this.health -= Math.max( this.health -= damage + damage * (gemResistanceModifications.physical - this.stats.resistance.physical);
damage + damage * (gemResistanceModifications.physical - this.stats.resistance.physical),
0
);
if (gemResistanceModifications.fire != 0) if (gemResistanceModifications.fire != 0)
this.health -= Math.max(damage * (gemResistanceModifications.fire - this.stats.resistance.fire), 0); this.health -= Math.max(damage * (gemResistanceModifications.fire - this.stats.resistance.fire), 0);
if (gemResistanceModifications.ice != 0) if (gemResistanceModifications.ice != 0)
@ -99,6 +98,7 @@ export default class Creep extends GameObject {
CreepEvents.GiveEffect, CreepEvents.GiveEffect,
(creepID: number, effect: CreepEffects, durationInMS: number) => { (creepID: number, effect: CreepEffects, durationInMS: number) => {
if (creepID != this.id) return; if (creepID != this.id) return;
console.log(' I CAUGHT THE EVENT!');
if (this.effects.find((e) => e.effectEnum == effect) == undefined) if (this.effects.find((e) => e.effectEnum == effect) == undefined)
this.effects.push(new Effect(effect, durationInMS)); this.effects.push(new Effect(effect, durationInMS));
} }
@ -224,13 +224,13 @@ export default class Creep extends GameObject {
this.container.y = this.y; this.container.y = this.y;
} }
// public takeDamage(amount: number) { public takeDamage(amount: number) {
// this.health -= amount; this.health -= amount;
// if (this.health < 0 && !this.died) { if (this.health < 0 && !this.died) {
// this.died = true; this.died = true;
// this.events.emit(CreepEvents.Died, this); this.events.emit(CreepEvents.Died, this);
// } }
// } }
public destroy() { public destroy() {
super.destroy(); super.destroy();

View File

@ -10,9 +10,9 @@ export default class MissionStats extends GameObject {
private gold: number = 0; private gold: number = 0;
private goldEarned: number = 0; private goldEarned: number = 0;
private goldSpent: number = 0; private goldSpent: number = 0;
public damageDealt: number = 0; private wavesSurvived: number = 0;
public wavesSurvived: number = 0; private damageDealt: number = 0;
public creepsKilled: number = 0; private creepsKilled: number = 0;
private goldText: PIXI.Text; private goldText: PIXI.Text;
private healthText: PIXI.Text; private healthText: PIXI.Text;
private waveText: PIXI.Text; private waveText: PIXI.Text;
@ -44,13 +44,11 @@ export default class MissionStats extends GameObject {
public earnGold(gold: number) { public earnGold(gold: number) {
this.gold += gold; this.gold += gold;
this.goldText.text = this.gold; this.goldText.text = this.gold;
this.goldEarned += gold;
} }
public spendGold(amount: number) { public spendGold(amount: number) {
this.gold -= amount; this.gold -= amount;
this.goldText.text = this.gold; this.goldText.text = this.gold;
this.goldSpent += amount;
} }
public giveGem(gem: Gem, noNotify?) { public giveGem(gem: Gem, noNotify?) {
@ -84,7 +82,6 @@ export default class MissionStats extends GameObject {
super(); super();
this.hp = initialHP; this.hp = initialHP;
this.gold = initialGold; this.gold = initialGold;
this.goldEarned = initialGold;
this.container.x = 0; this.container.x = 0;
this.container.y = 20; this.container.y = 20;
Engine.GameMaster.currentScene.stage.addChild(this.container); Engine.GameMaster.currentScene.stage.addChild(this.container);
@ -164,25 +161,20 @@ export default class MissionStats extends GameObject {
} }
private calculateScore() { private calculateScore() {
const gems = []; const uniqueGems = [];
for (const gem of this.inventory) { for (const gem of this.inventory) {
gems.push(gem.definition.name); if (!uniqueGems.includes(gem.definition.name)) {
uniqueGems.push(gem.definition.name);
}
} }
Engine.TowerManager.towers.forEach((tower) => {
tower.slottedGems.forEach((gem) => {
gems.push(gem.definition.name);
});
});
return ( return (
this.damageDealt * 2 + this.damageDealt * 2 +
this.hp * 10 + this.hp * 10 +
(this.goldEarned - this.goldSpent) * 3 + (this.goldEarned - this.goldSpent) * 3 +
this.wavesSurvived * 100 + this.wavesSurvived * 100 +
gems.length * 100 + uniqueGems.length * 100
1000
); );
} }
// Because it's a game object, must have this.
public update() {} public update() {}
} }

View File

@ -7,7 +7,6 @@ import Creep, { CreepEffects } from './Creep';
import Projectile, { calculateAngleToPoint, TrapProjectile, VisualLightning } from './Projectile'; import Projectile, { calculateAngleToPoint, TrapProjectile, VisualLightning } from './Projectile';
import { distance, Tower } from './Tower'; import { distance, Tower } from './Tower';
import * as PIXI from 'pixi.js'; import * as PIXI from 'pixi.js';
import { TowerBehaviours } from './TowerManager';
/** /**
* Checks the projectiles of the tower and updates or removes them based on their state. * Checks the projectiles of the tower and updates or removes them based on their state.
@ -63,19 +62,17 @@ export function computeGemImprovements(tower: Tower) {
gemPierceUp += improvements.pierceUp; gemPierceUp += improvements.pierceUp;
let resistances = gem.currentGemResistanceModifications(); let resistances = gem.currentGemResistanceModifications();
tower.totalGemResistanceModifications.physical += Number(resistances.physical.toFixed(2)); tower.totalGemResistanceModifications.physical += resistances.physical;
tower.totalGemResistanceModifications.ice += Number(resistances.ice.toFixed(2)); tower.totalGemResistanceModifications.ice += resistances.ice;
tower.totalGemResistanceModifications.fire += Number(resistances.fire.toFixed(2)); tower.totalGemResistanceModifications.fire += resistances.fire;
tower.totalGemResistanceModifications.divine += Number(resistances.divine.toFixed(2)); tower.totalGemResistanceModifications.divine += resistances.divine;
tower.totalGemResistanceModifications.frostfire += Number(resistances.frostfire.toFixed(2)); tower.totalGemResistanceModifications.frostfire += resistances.frostfire;
}); });
tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage; tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage;
tower.computedCooldown = tower.definition.stats.cooldown - gemAttackSpeedUp; tower.computedCooldown = tower.definition.stats.cooldown - gemAttackSpeedUp;
tower.computedRange = tower.definition.stats.range + gemRangeUp; tower.computedRange = tower.definition.stats.range + gemRangeUp;
if (tower.behaviour != TowerBehaviours.TrapperTowerBehaviour) tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp;
tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp;
else tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp * 10;
tower.computedPierce = tower.definition.stats.pierce + gemPierceUp; tower.computedPierce = tower.definition.stats.pierce + gemPierceUp;
// Buff tower // Buff tower
@ -87,21 +84,14 @@ export function computeGemImprovements(tower: Tower) {
tower.computedTimeToLive += Number((buffedBy.computedTimeToLive / 5).toFixed(1)); tower.computedTimeToLive += Number((buffedBy.computedTimeToLive / 5).toFixed(1));
tower.computedPierce += Number((buffedBy.computedPierce / 4).toFixed(1)); tower.computedPierce += Number((buffedBy.computedPierce / 4).toFixed(1));
tower.totalGemResistanceModifications.physical += Number( tower.totalGemResistanceModifications.physical +=
(buffedBy.totalGemResistanceModifications.physical / 2).toFixed(2) (buffedBy.totalGemResistanceModifications.physical * 100) / 2 / 100;
); tower.totalGemResistanceModifications.ice += (buffedBy.totalGemResistanceModifications.ice * 100) / 2 / 100;
tower.totalGemResistanceModifications.ice += Number( tower.totalGemResistanceModifications.fire += (buffedBy.totalGemResistanceModifications.fire * 100) / 2 / 100;
(buffedBy.totalGemResistanceModifications.ice / 2).toFixed(2) tower.totalGemResistanceModifications.divine +=
); (buffedBy.totalGemResistanceModifications.divine * 100) / 2 / 100;
tower.totalGemResistanceModifications.fire += Number( tower.totalGemResistanceModifications.frostfire +=
(buffedBy.totalGemResistanceModifications.fire / 2).toFixed(2) (buffedBy.totalGemResistanceModifications.frostfire * 100) / 2 / 100;
);
tower.totalGemResistanceModifications.divine += Number(
(buffedBy.totalGemResistanceModifications.divine / 2).toFixed(2)
);
tower.totalGemResistanceModifications.frostfire += Number(
(buffedBy.totalGemResistanceModifications.frostfire / 2).toFixed(2)
);
} }
} }

View File

@ -28,7 +28,7 @@ export default class TowerManager {
height: 64, height: 64,
alpha: 0.8, alpha: 0.8,
}); });
public towers: Tower[] = []; private towers: Tower[] = [];
constructor() { constructor() {
// TODO: Unsubscribe from events once the scene is destroyed // TODO: Unsubscribe from events once the scene is destroyed
Engine.TowerManager = this; Engine.TowerManager = this;

View File

@ -119,9 +119,7 @@ export default class EndGameDialog extends ModalDialogBase {
override close(button?: string): void { override close(button?: string): void {
if (button === EndGameDialogButtons.Confirm) { if (button === EndGameDialogButtons.Confirm) {
if (this.playerNameTextInput.getText().length == 0) { if (this.playerNameTextInput.getText().length == 0) {
MessageBox.show('Please enter your name.\n(Just start typing, input bar is automatically selected.)', [ MessageBox.show('Please enter your name.', ['OK']);
'OK',
]);
} else { } else {
this.highScore.addScore({ this.highScore.addScore({
playerName: this.playerNameTextInput.getText(), playerName: this.playerNameTextInput.getText(),

View File

@ -19,15 +19,16 @@ export default class GamePausedDialog extends ModalDialogBase {
protected override createContent(): PIXI.Container { protected override createContent(): PIXI.Container {
const container = new PIXI.Container(); const container = new PIXI.Container();
this.btnContinue = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Continue', ButtonTexture.Button01); this.btnMainMenu = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Main Menu', ButtonTexture.Button01);
this.btnContinue.onClick = this.onContinueClick.bind(this); this.btnMainMenu.onClick = this.onMainMenuClick.bind(this);
container.addChild(this.btnContinue.container); container.addChild(this.btnMainMenu.container);
this.btnRetry = new Button(new PIXI.Rectangle(0, 70, 300, 60), 'Retry', ButtonTexture.Button01); this.btnRetry = new Button(new PIXI.Rectangle(0, 70, 300, 60), 'Retry', ButtonTexture.Button01);
this.btnRetry.onClick = this.onRetryClick.bind(this); this.btnRetry.onClick = this.onRetryClick.bind(this);
container.addChild(this.btnRetry.container); container.addChild(this.btnRetry.container);
this.btnMainMenu = new Button(new PIXI.Rectangle(0, 140, 300, 60), 'Main Menu', ButtonTexture.Button01);
this.btnMainMenu.onClick = this.onMainMenuClick.bind(this); this.btnContinue = new Button(new PIXI.Rectangle(0, 140, 300, 60), 'Continue', ButtonTexture.Button01);
container.addChild(this.btnMainMenu.container); this.btnContinue.onClick = this.onContinueClick.bind(this);
container.addChild(this.btnContinue.container);
return container; return container;
} }

View File

@ -20,7 +20,7 @@ export class VisualGemSlot extends GuiObject {
let gtexture; let gtexture;
this.i = index; this.i = index;
this.container.x = 10; this.container.x = 10;
this.container.y = index * (Engine.GridCellSize + 6) + 350; this.container.y = index * (Engine.GridCellSize + 6) + 300;
this.background = new PIXI.Sprite({ this.background = new PIXI.Sprite({
texture: GameAssets.Frame01Texture, texture: GameAssets.Frame01Texture,
}); });
@ -115,7 +115,6 @@ export default class TowerPanel extends GuiObject {
public divineResDamage: PIXI.Text; public divineResDamage: PIXI.Text;
public physicalResDamage: PIXI.Text; public physicalResDamage: PIXI.Text;
private sellButton: Button; private sellButton: Button;
private description: PIXI.Text;
constructor(bounds: PIXI.Rectangle) { constructor(bounds: PIXI.Rectangle) {
super(false); super(false);
@ -283,23 +282,6 @@ export default class TowerPanel extends GuiObject {
}), }),
}); });
this.container.addChild(this.physicalResDamage); this.container.addChild(this.physicalResDamage);
this.description = new PIXI.Text({
x: 10,
y: 270,
zIndex: 5,
style: {
fontSize: 18,
wordWrap: true,
wordWrapWidth: 245,
fill: 0x00ff00,
fontStyle: 'italic',
stroke: {
color: 0x000000,
width: 2,
},
},
});
this.container.addChild(this.description);
this.sellButton = new Button( this.sellButton = new Button(
new PIXI.Rectangle(5, this.towerPanel.height - 70, this.towerPanel.width - 115, 60), new PIXI.Rectangle(5, this.towerPanel.height - 70, this.towerPanel.width - 115, 60),
'Sell', 'Sell',
@ -366,7 +348,6 @@ export default class TowerPanel extends GuiObject {
this.frostFireResDamage.text = `+${tower.totalGemResistanceModifications.frostfire * 100}% FrostFire damage`; this.frostFireResDamage.text = `+${tower.totalGemResistanceModifications.frostfire * 100}% FrostFire damage`;
this.divineResDamage.text = `+${tower.totalGemResistanceModifications.divine * 100}% Divine damage`; this.divineResDamage.text = `+${tower.totalGemResistanceModifications.divine * 100}% Divine damage`;
this.physicalResDamage.text = `+${tower.totalGemResistanceModifications.physical * 100}% Physical damage`; this.physicalResDamage.text = `+${tower.totalGemResistanceModifications.physical * 100}% Physical damage`;
this.description.text = `"${tower.definition.description}"`;
this.sellButton.setCaption('Sell for ' + tower.definition.stats.cost + ' gold'); this.sellButton.setCaption('Sell for ' + tower.definition.stats.cost + ' gold');
this.sellButton.onClick = () => { this.sellButton.onClick = () => {
tower.Sell(); tower.Sell();

View File

@ -48,23 +48,6 @@ import DebrisManager from './classes/game/DebrisManager';
window.addEventListener('resize', resize); window.addEventListener('resize', resize);
resize(); resize();
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (isMobile) {
let ttxt = new PIXI.Text({
text: 'Bastion: The Watchers Lament is currently unsupported on mobile.\nPlease play it on your computer instead.',
style: new PIXI.TextStyle({
fill: 0x333333,
fontSize: 50,
textBaseline: 'middle',
}),
});
ttxt.x = Engine.app.canvas.width / 2;
ttxt.y = Engine.app.canvas.height / 2 + 50;
ttxt.anchor.set(0.5, 0.5);
Engine.app.stage.addChild(ttxt);
return;
}
await Assets.LoadAssets(); await Assets.LoadAssets();
GameUIConstants.init(); GameUIConstants.init();
KeyboardManager.init(); KeyboardManager.init();

View File

@ -85,8 +85,6 @@ export class GameScene extends Scene {
this.isWaveManagerFinished = true; this.isWaveManagerFinished = true;
}); });
this.events.on(CreepEvents.Died, (playerAward, creepThatDied) => { this.events.on(CreepEvents.Died, (playerAward, creepThatDied) => {
this.MissionStats.damageDealt += playerAward;
this.MissionStats.creepsKilled++;
this.MissionStats.earnGold(playerAward); this.MissionStats.earnGold(playerAward);
}); });
this.towerPanel = new TowerPanel(GameUIConstants.SidebarRect); this.towerPanel = new TowerPanel(GameUIConstants.SidebarRect);
@ -123,7 +121,7 @@ export class GameScene extends Scene {
this.changeRoundButton.buttonIcon.texture = GameAssets.FastForwardIconTexture; this.changeRoundButton.buttonIcon.texture = GameAssets.FastForwardIconTexture;
this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`); this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`);
}; };
this.MissionStats = new MissionStats(100, 75); this.MissionStats = new MissionStats(100, 250);
this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => { this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => {
if (gem == null) { if (gem == null) {
if (!this.MissionStats.checkIfPlayerHasAnyGems()) if (!this.MissionStats.checkIfPlayerHasAnyGems())
@ -187,13 +185,13 @@ export class GameScene extends Scene {
this.isWaveManagerFinished = false; this.isWaveManagerFinished = false;
this.setRoundMode(RoundMode.Purchase); this.setRoundMode(RoundMode.Purchase);
this.changeRoundButton.buttonIcon.texture = GameAssets.PlayIconTexture; this.changeRoundButton.buttonIcon.texture = GameAssets.PlayIconTexture;
this.MissionStats.wavesSurvived++;
Engine.NotificationManager.Notify( Engine.NotificationManager.Notify(
`Round ${this.currentRound + 1}/${this.mission.rounds.length} completed.`, `Round ${this.currentRound + 1}/${this.mission.rounds.length} completed.`,
'info' 'info'
); );
if (this.currentRound + 1 == this.mission.rounds.length) { if (this.currentRound + 1 == this.mission.rounds.length) {
Engine.NotificationManager.Notify(`Mission victory!!`, 'reward'); Engine.NotificationManager.Notify(`Mission victory!!`, 'reward');
this.changeRoundButton.buttonIcon.texture = GameAssets.HomeIconTexture;
this.playerWon = true; this.playerWon = true;
} else { } else {
this.OfferPlayerGems(); this.OfferPlayerGems();
@ -251,6 +249,7 @@ export class GameScene extends Scene {
}, },
}, },
}); });
// offerText.x -= offerText.width;
Engine.GameMaster.currentScene.stage.addChildAt(offerText, 0); Engine.GameMaster.currentScene.stage.addChildAt(offerText, 0);
gemsToOffer.forEach((gType, index) => { gemsToOffer.forEach((gType, index) => {
let _Gem = new Gem(gType, true); let _Gem = new Gem(gType, true);

View File

@ -14,7 +14,7 @@ export class MainScene extends Scene {
this.addMainBackground(); this.addMainBackground();
const NewGameButton = { const NewGameButton = {
caption: 'Play', caption: 'New Game',
rect: new PIXI.Rectangle(Engine.app.canvas.width / 2 - 300 / 2, 400 + 0 * 70, 300, 60), rect: new PIXI.Rectangle(Engine.app.canvas.width / 2 - 300 / 2, 400 + 0 * 70, 300, 60),
texture: ButtonTexture.Button01, texture: ButtonTexture.Button01,