Compare commits

..

11 Commits

Author SHA1 Message Date
64ac63b5ef
bunch more features 2025-03-08 21:01:52 +01:00
185faa42e2 more balance 2025-03-08 21:01:26 +01:00
a3e8be3c18 balanced gems 2025-03-07 01:08:17 +01:00
aec0821dbb statistics 2025-03-06 22:09:59 +01:00
bdeb67e32e
many optimizations, nearing final release 2025-02-27 15:43:43 +01:00
547cccd071 more optimizations 2025-02-27 15:43:13 +01:00
4943c6ba7f spritesheets + more balanced 2025-02-26 15:13:48 +01:00
dd5da90521
Merge pull request #21 from koneko/spritesheet
creeps load as spritesheets now
2025-02-26 11:57:20 +01:00
ca8bf01c78
successfully implement all mechanics 2025-02-22 15:12:58 +01:00
8267ade858
implemented more towers 2025-02-17 23:39:36 +01:00
5b18dd601a
improvement 2025-02-10 20:06:18 +01:00
80 changed files with 487 additions and 331 deletions

View File

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

View File

@ -4,8 +4,19 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<link href="https://fonts.googleapis.com/css?family=Aclonica" rel="stylesheet" />
<title>Bastion: The Watcher's Lament</title>
<title>Bastion: The Watchers Lament</title>
<meta property="og:title" content="Bastion: The Watchers Lament" />
<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>
<body>
<script type="module" src="/src/main.ts"></script>

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

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.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

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.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

View File

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

View File

@ -1,14 +1,14 @@
[
{
"name": "Fire Gem",
"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.",
"description": "Forged from molten lava, the Fire Gem imbues your tower's attacks and add extra fire damage. Well researched and common.",
"color": "red",
"type": "Fire",
"totalLevels": 9,
"textures": [],
"cantCombineWith": [],
"specialCombine": [],
"initialGemValue": 50,
"initialGemValue": 75,
"genericImprovements": [
{
"damageUp": 1,
@ -16,14 +16,6 @@
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 50
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
},
{
@ -32,7 +24,15 @@
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
"gemValueUp": 150
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 2,
"timeToLiveUp": 0,
"pierceUp": 0,
"gemValueUp": 250
},
{
"damageUp": 2,
@ -40,7 +40,31 @@
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
"gemValueUp": 400
},
{
"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,
@ -48,42 +72,25 @@
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
"gemValueUp": 1000
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"attackSpeedUp": 250,
"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
},
{
"damageUp": 2,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 100
"gemValueUp": 1200
}
],
"gemResistanceModifications": [
{
"physical": 0,
"divine": 0,
"fire": 0.05,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
@ -94,7 +101,14 @@
{
"physical": 0,
"divine": 0,
"fire": 0.25,
"fire": 0.15,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0.2,
"ice": 0,
"frostfire": 0
},
@ -115,14 +129,7 @@
{
"physical": 0,
"divine": 0,
"fire": 0.3,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0.3,
"fire": 0.35,
"ice": 0,
"frostfire": 0
},
@ -139,90 +146,83 @@
"fire": 0.45,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0.5,
"ice": 0,
"frostfire": 0
}
]
},
{
"name": "Yeti Gem",
"description": "Yeti gem description. Something something, write this while drunk or something.",
"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.",
"color": "#32e4fc",
"type": "Yeti",
"totalLevels": 8,
"textures": [],
"cantCombineWith": [],
"specialCombine": [],
"initialGemValue": 10,
"initialGemValue": 80,
"genericImprovements": [
{
"damageUp": 2,
"attackSpeedUp": 10,
"damageUp": 1,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 100
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"gemValueUp": 150
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"attackSpeedUp": 0,
"rangeUp": 2,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 250
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"gemValueUp": 400
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 650
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"gemValueUp": 750
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 900
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"attackSpeedUp": 100,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"gemValueUp": 1000
}
],
"gemResistanceModifications": [
@ -230,14 +230,7 @@
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.1,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.2,
"ice": 0.05,
"frostfire": 0
},
{
@ -251,14 +244,7 @@
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.2,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.1,
"ice": 0.15,
"frostfire": 0
},
{
@ -272,84 +258,98 @@
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.1,
"frostfire": 0
"ice": 0.25,
"frostfire": 0.1
},
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.2,
"frostfire": 0
"ice": 0.3,
"frostfire": 0.1
},
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.35,
"frostfire": 0.2
},
{
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.4,
"frostfire": 0.25
}
]
},
{
"name": "Titalium Gem",
"description": "Titalium gem description. Something something zombie creeps working for you something something.",
"description": "Created from distilled creep eyes, the Titalium gem is known for boosting tower range, at the sacrifice for other stats.",
"color": "pink",
"type": "Titalium",
"totalLevels": 7,
"textures": [],
"cantCombineWith": [],
"specialCombine": [],
"initialGemValue": 10,
"initialGemValue": 100,
"genericImprovements": [
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"damageUp": 1,
"attackSpeedUp": 0,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 200
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"attackSpeedUp": 0,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"gemValueUp": 350
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"attackSpeedUp": 0,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 2,
"gemValueUp": 450
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"attackSpeedUp": 200,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"gemValueUp": 600
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"attackSpeedUp": 0,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 850
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"attackSpeedUp": 0,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"pierceUp": 0,
"gemValueUp": 900
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"attackSpeedUp": 0,
"rangeUp": 2,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 900
}
],
"gemResistanceModifications": [
@ -357,191 +357,190 @@
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"physical": 0.05,
"divine": 0,
"fire": 0,
"ice": 0.2,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"physical": 0.1,
"divine": 0,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"physical": 0.1,
"divine": 0,
"fire": 0,
"ice": 0.2,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"physical": 0.15,
"divine": 0,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"physical": 0.2,
"divine": 0,
"fire": 0,
"ice": 0.2,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"physical": 0.25,
"divine": 0,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0.3,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0
}
]
},
{
"name": "Soulforge Gem",
"description": "Soulforge gem description, have to write later.",
"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.",
"color": "gray",
"type": "Soulforge",
"totalLevels": 2,
"totalLevels": 7,
"textures": [],
"cantCombineWith": [],
"specialCombine": [],
"initialGemValue": 10,
"initialGemValue": 150,
"genericImprovements": [
{
"damageUp": 2,
"attackSpeedUp": 10,
"damageUp": 0,
"attackSpeedUp": 100,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 200
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"damageUp": 0,
"attackSpeedUp": 100,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"gemValueUp": 350
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"damageUp": 0,
"attackSpeedUp": 150,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"pierceUp": 0,
"gemValueUp": 450
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"damageUp": 0,
"attackSpeedUp": 100,
"rangeUp": 1,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"pierceUp": 0,
"gemValueUp": 600
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"damageUp": 0,
"attackSpeedUp": 250,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
"gemValueUp": 850
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"damageUp": 0,
"attackSpeedUp": 400,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"pierceUp": 0,
"gemValueUp": 900
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"damageUp": 0,
"attackSpeedUp": 500,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 0
},
{
"damageUp": 2,
"attackSpeedUp": 10,
"rangeUp": 0,
"timeToLiveUp": 0,
"pierceUp": 1,
"gemValueUp": 10
"pierceUp": 0,
"gemValueUp": 900
}
],
"gemResistanceModifications": [
{
"physical": 0,
"divine": 0,
"divine": 0.2,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"divine": 0.3,
"fire": 0,
"ice": 0.2,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"divine": 0.4,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"divine": 0.5,
"fire": 0,
"ice": 0.2,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"divine": 0.6,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"divine": 0.7,
"fire": 0,
"ice": 0.2,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"divine": 0.75,
"fire": 0,
"ice": 0.1,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"divine": 0.8,
"fire": 0,
"ice": 0.2,
"ice": 0,
"frostfire": 0
}
]
},
{
"name": "Golden Gem",
"description": "An inherently useless gem, but high monetary value. Perhaps someone would pay a lot for this.",
"description": "An inherently useless gem, providing no tower buffs, but high in monetary value. Highly sought after by all rich people.",
"color": "gold",
"type": "Gold",
"totalLevels": 1,
@ -571,21 +570,21 @@
},
{
"name": "Artifact",
"description": "A wildly rare gem, packed with power. No monetary value, yet your towers oddly resonate stronger with it.",
"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.",
"color": "blue",
"type": "Artifact",
"totalLevels": 1,
"textures": [],
"cantCombineWith": [],
"specialCombine": [],
"initialGemValue": 0,
"initialGemValue": 1000,
"genericImprovements": [
{
"damageUp": 10,
"attackSpeedUp": 100,
"rangeUp": 2,
"timeToLiveUp": 200,
"pierceUp": 20,
"pierceUp": 3,
"gemValueUp": 0
}
],

View File

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

View File

@ -62,15 +62,30 @@
{
"firstCreepSpawnTick": 500,
"spawnIntervalTicks": 1000,
"creeps": [0, 0, 0]
"creeps": [0, 0, 0, 0, 0]
},
{
"firstCreepSpawnTick": 4000,
"spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0]
"creeps": [0, 0, 0, 0, 0]
}
],
"offeredGems": [0, 1]
"offeredGems": [0, 1, 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,
"spawnIntervalTicks": 500,
"creeps": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 2]
"creeps": [0]
}
],
"offeredGems": [0, 0, 0, 0]

View File

@ -10,17 +10,13 @@ export default class GameAssets {
public static Frame03Texture: PIXI.Texture;
public static Frame04Texture: PIXI.Texture;
public static Frame05Texture: PIXI.Texture;
public static FrameInventory: PIXI.Texture;
public static FrameBackground: PIXI.Texture;
public static FrameTowerTab: PIXI.Texture;
public static VioletBackground: PIXI.Texture;
public static RedBackground: PIXI.Texture;
public static GreenBackground: PIXI.Texture;
public static BlueBackground: PIXI.Texture;
public static YellowBackground: PIXI.Texture;
public static Button01Texture: PIXI.Texture;
public static Button02Texture: PIXI.Texture;
public static ButtonSmallTexture: PIXI.Texture;
public static HealthTexture: PIXI.Texture;
public static GoldTexture: PIXI.Texture;
public static WaveTexture: PIXI.Texture;
@ -40,8 +36,6 @@ export default class GameAssets {
public static PauseIconTexture: PIXI.Texture;
public static ExclamationIconTexture: PIXI.Texture;
public static FastForwardIconTexture: PIXI.Texture;
public static HomeIconTexture: PIXI.Texture;
public static HammerIconTexture: PIXI.Texture;
public static XIconTexture: PIXI.Texture;
public static PlusIconTexture: PIXI.Texture;
public static GemAmountIcons: PIXI.Texture[] = [];
@ -53,8 +47,10 @@ export default class GameAssets {
public static Gems: GemDefinition[];
private static text;
private static counter = 0;
private static async Load(src) {
this.text.text = 'Loading asset: ' + src;
this.text.text = `Loading asset: ${src} (${this.counter}/102)`;
this.counter++;
return await PIXI.Assets.load({
src: src,
});
@ -91,19 +87,14 @@ export default class GameAssets {
Engine.app.stage.addChild(this.text);
await Promise.all([
this.Load('./aclonica.woff2'),
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_small.png').then((texture) => (this.ButtonSmallTexture = 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_03.png').then((texture) => (this.Frame03Texture = 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_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/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_green.png').then((texture) => (this.GreenBackground = texture)),
this.Load('./assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = texture)),
@ -125,10 +116,7 @@ export default class GameAssets {
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/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/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/plus.png').then((texture) => (this.PlusIconTexture = texture)),
this.LoadMissions(),
@ -148,9 +136,27 @@ export default class GameAssets {
for (let idx = 0; idx < gems.length; 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++) {
const texture = await this.Load(`./assets/gems/${gem.type}/${i}.png`);
gem.textures[i - 1] = texture;
const spritesheet = new PIXI.Spritesheet(texture, {
frames: {
[`${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++) {
@ -164,8 +170,8 @@ export default class GameAssets {
this.Creeps = creeps;
for (let idx = 0; idx < this.Creeps.length; 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++) {
const texture = await this.Load(`./assets/creeps/${creep.sprite}/spritesheet.png`);
const spritesheet = new PIXI.Spritesheet(texture, {
frames: {
[`${creep.sprite}_${i}.png`]: {
@ -185,7 +191,6 @@ export default class GameAssets {
});
await spritesheet.parse();
creep.textures[i] = spritesheet.textures[`${creep.sprite}_${i}.png`];
// creep.textures[i] = texture;
}
}
}
@ -208,6 +213,8 @@ export default class GameAssets {
for (let idx = 0; idx < this.Towers.length; idx++) {
const tower = this.Towers[idx];
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`);
tower.projectileTextures[i] = projTexture;
}
@ -221,7 +228,7 @@ export default class GameAssets {
private static async LoadMission(missionUrl: string) {
const res = await fetch(missionUrl);
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.MissionBackgrounds.push(await this.Load(mission.mapImage.url));
}

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import Creep, { CreepEffects } from './Creep';
import Projectile, { calculateAngleToPoint, TrapProjectile, VisualLightning } from './Projectile';
import { distance, Tower } from './Tower';
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.
@ -62,17 +63,19 @@ export function computeGemImprovements(tower: Tower) {
gemPierceUp += improvements.pierceUp;
let resistances = gem.currentGemResistanceModifications();
tower.totalGemResistanceModifications.physical += resistances.physical;
tower.totalGemResistanceModifications.ice += resistances.ice;
tower.totalGemResistanceModifications.fire += resistances.fire;
tower.totalGemResistanceModifications.divine += resistances.divine;
tower.totalGemResistanceModifications.frostfire += resistances.frostfire;
tower.totalGemResistanceModifications.physical += Number(resistances.physical.toFixed(2));
tower.totalGemResistanceModifications.ice += Number(resistances.ice.toFixed(2));
tower.totalGemResistanceModifications.fire += Number(resistances.fire.toFixed(2));
tower.totalGemResistanceModifications.divine += Number(resistances.divine.toFixed(2));
tower.totalGemResistanceModifications.frostfire += Number(resistances.frostfire.toFixed(2));
});
tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage;
tower.computedCooldown = tower.definition.stats.cooldown - gemAttackSpeedUp;
tower.computedRange = tower.definition.stats.range + gemRangeUp;
tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp;
if (tower.behaviour != TowerBehaviours.TrapperTowerBehaviour)
tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp;
else tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp * 10;
tower.computedPierce = tower.definition.stats.pierce + gemPierceUp;
// Buff tower
@ -84,14 +87,21 @@ export function computeGemImprovements(tower: Tower) {
tower.computedTimeToLive += Number((buffedBy.computedTimeToLive / 5).toFixed(1));
tower.computedPierce += Number((buffedBy.computedPierce / 4).toFixed(1));
tower.totalGemResistanceModifications.physical +=
(buffedBy.totalGemResistanceModifications.physical * 100) / 2 / 100;
tower.totalGemResistanceModifications.ice += (buffedBy.totalGemResistanceModifications.ice * 100) / 2 / 100;
tower.totalGemResistanceModifications.fire += (buffedBy.totalGemResistanceModifications.fire * 100) / 2 / 100;
tower.totalGemResistanceModifications.divine +=
(buffedBy.totalGemResistanceModifications.divine * 100) / 2 / 100;
tower.totalGemResistanceModifications.frostfire +=
(buffedBy.totalGemResistanceModifications.frostfire * 100) / 2 / 100;
tower.totalGemResistanceModifications.physical += Number(
(buffedBy.totalGemResistanceModifications.physical / 2).toFixed(2)
);
tower.totalGemResistanceModifications.ice += Number(
(buffedBy.totalGemResistanceModifications.ice / 2).toFixed(2)
);
tower.totalGemResistanceModifications.fire += Number(
(buffedBy.totalGemResistanceModifications.fire / 2).toFixed(2)
);
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,
alpha: 0.8,
});
private towers: Tower[] = [];
public towers: Tower[] = [];
constructor() {
// TODO: Unsubscribe from events once the scene is destroyed
Engine.TowerManager = this;

View File

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

View File

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

View File

@ -20,7 +20,7 @@ export class VisualGemSlot extends GuiObject {
let gtexture;
this.i = index;
this.container.x = 10;
this.container.y = index * (Engine.GridCellSize + 6) + 300;
this.container.y = index * (Engine.GridCellSize + 6) + 350;
this.background = new PIXI.Sprite({
texture: GameAssets.Frame01Texture,
});
@ -115,6 +115,7 @@ export default class TowerPanel extends GuiObject {
public divineResDamage: PIXI.Text;
public physicalResDamage: PIXI.Text;
private sellButton: Button;
private description: PIXI.Text;
constructor(bounds: PIXI.Rectangle) {
super(false);
@ -282,6 +283,23 @@ export default class TowerPanel extends GuiObject {
}),
});
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(
new PIXI.Rectangle(5, this.towerPanel.height - 70, this.towerPanel.width - 115, 60),
'Sell',
@ -348,6 +366,7 @@ export default class TowerPanel extends GuiObject {
this.frostFireResDamage.text = `+${tower.totalGemResistanceModifications.frostfire * 100}% FrostFire damage`;
this.divineResDamage.text = `+${tower.totalGemResistanceModifications.divine * 100}% Divine 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.onClick = () => {
tower.Sell();

View File

@ -48,6 +48,23 @@ import DebrisManager from './classes/game/DebrisManager';
window.addEventListener('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();
GameUIConstants.init();
KeyboardManager.init();

View File

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

View File

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