diff --git a/docs/todos.md b/docs/todos.md index e0941e0..3590a01 100644 --- a/docs/todos.md +++ b/docs/todos.md @@ -12,8 +12,8 @@ List of things to implement following the "release" of the minimum viable produc ## Towers - [x] Extend projectile into seperate defintion + json file -- [ ] Make tower react with slotted gems -- [ ] Alter damage based on attunement from slotted gems +- [x] Make tower react with slotted gems +- [x] Alter damage based on attunement from slotted gems - [x] Tower info on click - [x] Animate projectiles - [x] Better mouseover tracking when placing tower and showing radius @@ -21,7 +21,7 @@ List of things to implement following the "release" of the minimum viable produc ## Gems - [x] Create Gem definitions -- [ ] Make gems affect towers +- [x] Make gems affect towers ## Other diff --git a/public/assets/gui/banner_01.png b/public/assets/gui/banner_01.png index 63258b3..002858e 100755 Binary files a/public/assets/gui/banner_01.png and b/public/assets/gui/banner_01.png differ diff --git a/public/assets/gui/banner_02.png b/public/assets/gui/banner_02.png deleted file mode 100755 index dae9558..0000000 Binary files a/public/assets/gui/banner_02.png and /dev/null differ diff --git a/public/assets/gui/banner_03.png b/public/assets/gui/banner_03.png new file mode 100755 index 0000000..63258b3 Binary files /dev/null and b/public/assets/gui/banner_03.png differ diff --git a/public/assets/gui/icons/cross.png b/public/assets/gui/icons/cross.png index deda6cb..e0bebb3 100755 Binary files a/public/assets/gui/icons/cross.png and b/public/assets/gui/icons/cross.png differ diff --git a/public/assets/json/Gems.json b/public/assets/json/Gems.json index 880ee75..aab44ba 100644 --- a/public/assets/json/Gems.json +++ b/public/assets/json/Gems.json @@ -7,20 +7,23 @@ "textures": [], "cantCombineWith": [], "specialCombine": [], + "initialGemValue": 10, "genericImprovements": [ { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0.5, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 0 }, { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0.5, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 10 } ], "gemResistanceModifications": [ @@ -48,20 +51,23 @@ "textures": [], "cantCombineWith": [], "specialCombine": [], + "initialGemValue": 10, "genericImprovements": [ { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0.5, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 0 }, { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0.5, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 10 } ], "gemResistanceModifications": [ @@ -89,27 +95,31 @@ "textures": [], "cantCombineWith": [], "specialCombine": [], + "initialGemValue": 10, "genericImprovements": [ { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0.5, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 0 }, { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 10 }, { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 10 } ], "gemResistanceModifications": [ @@ -144,20 +154,23 @@ "textures": [], "cantCombineWith": [], "specialCombine": [], + "initialGemValue": 10, "genericImprovements": [ { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0.5, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 0 }, { "damageUp": 2, "attackSpeedUp": 100, "rangeUp": 0.5, "timeToLiveUp": 0, - "pierceUp": 1 + "pierceUp": 1, + "gemValueUp": 10 } ], "gemResistanceModifications": [ diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index 528aada..ee2edbe 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -23,7 +23,7 @@ export default class GameAssets { public static WaveTexture: PIXI.Texture; public static SwordsTexture: PIXI.Texture; public static TitleTexture: PIXI.Texture; - public static GemFrame: PIXI.Texture; + public static BannerGemsmith: PIXI.Texture; public static PlayIconTexture: PIXI.Texture; public static PauseIconTexture: PIXI.Texture; @@ -95,7 +95,7 @@ export default class GameAssets { 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)), - this.Load('./assets/gui/gem_frame.png').then((texture) => (this.GemFrame = texture)), + this.Load('./assets/gui/banner_01.png').then((texture) => (this.BannerGemsmith = texture)), this.Load('./assets/gui/heart.png').then((texture) => (this.HealthTexture = texture)), this.Load('./assets/gui/money.png').then((texture) => (this.GoldTexture = texture)), this.Load('./assets/gui/wave.png').then((texture) => (this.WaveTexture = texture)), diff --git a/src/classes/Definitions.ts b/src/classes/Definitions.ts index 4bee02e..3026d7e 100644 --- a/src/classes/Definitions.ts +++ b/src/classes/Definitions.ts @@ -78,6 +78,7 @@ export type GemDefinition = { textures: PIXI.Texture[]; cantCombineWith: GemType[]; specialCombine: GemType[]; + initialGemValue: number; genericImprovements: GenericGemImprovement[]; gemResistanceModifications: CreepResistancesDefinition[]; }; @@ -88,6 +89,7 @@ export type GenericGemImprovement = { rangeUp: number; timeToLiveUp: number; pierceUp: number; + gemValueUp: number; }; export type PathDefinition = [[row: number, column: number]]; diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts index f3742c1..b699dbc 100644 --- a/src/classes/game/Gem.ts +++ b/src/classes/game/Gem.ts @@ -25,6 +25,7 @@ export default class Gem { rangeUp: 0, timeToLiveUp: 0, pierceUp: 0, + gemValueUp: 0, }; for (let i = 0; i < this.level; i++) { const item = this.definition.genericImprovements[i]; @@ -33,10 +34,19 @@ export default class Gem { totalGemImprovement.rangeUp += item.rangeUp; totalGemImprovement.timeToLiveUp += item.timeToLiveUp; totalGemImprovement.pierceUp += item.pierceUp; + totalGemImprovement.gemValueUp += item.gemValueUp; } return totalGemImprovement; } public currentGemResistanceModifications() { return this.definition.gemResistanceModifications[this.level - 1]; } + public isMaxLevel() { + return this.level == this.definition.totalLevels; + } + public levelUp(howMuch) { + if (!howMuch) howMuch = 1; + this.level += howMuch; + this.texture = this.definition.textures[this.level - 1]; + } } diff --git a/src/classes/gui/GemTab.ts b/src/classes/gui/GemTab.ts index 04b0833..c2c063f 100644 --- a/src/classes/gui/GemTab.ts +++ b/src/classes/gui/GemTab.ts @@ -32,7 +32,7 @@ export default class GemTab extends GuiObject { this.gemTabSprite.x = 0; this.gemTabSprite.y = 0; this.gemTabSprite.width = this.bounds.width; - this.gemTabSprite.height = this.bounds.height - 255; + this.gemTabSprite.height = this.bounds.height; this.container.addChild(this.gemTabSprite); Engine.app.canvas.addEventListener('pointermove', () => { this.pointerMoveEvent(); @@ -76,10 +76,22 @@ export default class GemTab extends GuiObject { } } public pointerMoveEvent() { - if (!this.isDragAndDroppingGem || !Engine.GameScene.towerPanel.isShown || !this.dragAndDroppingGem) return; + if (!this.isDragAndDroppingGem || !this.dragAndDroppingGem) return; this.dragAndDroppingGem.container.x = Engine.MouseX - 32; this.dragAndDroppingGem.container.y = Engine.MouseY - 32; } + private isOverlappingGemsmith(me: VisualGemSlot, other: VisualGemSlot, otherParent: PIXI.Container) { + let ddbb = me.copyContainerToBB(); + let vb = other.copyContainerToBB(); + let x = otherParent.x + vb.x + Engine.GameScene.sidebar.container.x; + let y = otherParent.y + vb.y + Engine.GameScene.sidebar.container.y; + + let vgbb = new PIXI.Rectangle(x, y, vb.width, vb.height); + // console.log(ddbb, vgbb, ddbb.getBounds().intersects(vgbb)); + if (ddbb.getBounds().intersects(vgbb)) { + if (other && other.gem == null) return true; + } + } public RebuildInventoryVisual() { this.vGems.forEach((vGem) => vGem.destroy()); this.vGems = []; @@ -112,7 +124,6 @@ export default class GemTab extends GuiObject { this.selectingGemTowerObject.SlotGem(takenGem, this.selectingGemSlotIndex); this.RebuildInventoryVisual(); } else { - if (!Engine.GameScene.towerPanel.isShown) return; // initialise drag and drop this.isDragAndDroppingGem = true; this.dragAndDroppingGem = vGem; @@ -124,6 +135,63 @@ export default class GemTab extends GuiObject { vGem.container.onpointerup = () => { if (this.isSelectingGem) return; let overlapping = null; + let wantToSell = this.isOverlappingGemsmith( + this.dragAndDroppingGem, + Engine.GameScene.sidebar.gemsmith.sellVGem, + Engine.GameScene.sidebar.gemsmith.container + ); + if (wantToSell) { + let sellFor = + this.dragAndDroppingGem.gem.definition.initialGemValue + + this.dragAndDroppingGem.gem.currentGemImprovement().gemValueUp; + Engine.GameScene.MissionStats.earnGold(Math.ceil(sellFor * 0.8)); + Engine.NotificationManager.Notify( + `Sold Lv. ${this.dragAndDroppingGem.gem.level} ${ + this.dragAndDroppingGem.gem.definition.name + } for ${Math.ceil(sellFor * 0.8)} gold.`, + 'info' + ); + Engine.GameScene.MissionStats.takeGem(this.dragAndDroppingGem.gem); + this.isDragAndDroppingGem = false; + this.dragAndDroppingGem = null; + this.RebuildInventoryVisual(); + return; + } + let wantToUpgrade = this.isOverlappingGemsmith( + this.dragAndDroppingGem, + Engine.GameScene.sidebar.gemsmith.upgradeVGem, + Engine.GameScene.sidebar.gemsmith.container + ); + if (wantToUpgrade) { + if (this.dragAndDroppingGem.gem.isMaxLevel()) + Engine.NotificationManager.Notify('Gem is max level.', 'warn'); + else { + let cost = + this.dragAndDroppingGem.gem.definition.genericImprovements[ + this.dragAndDroppingGem.gem.level + ].gemValueUp; + if (Engine.GameScene.MissionStats.hasEnoughGold(cost)) { + Engine.GameScene.MissionStats.spendGold(cost); + this.dragAndDroppingGem.gem.levelUp(1); + Engine.NotificationManager.Notify( + `Spent ${cost} gold to upgrade ${this.dragAndDroppingGem.gem.definition.name} Lv. ${ + this.dragAndDroppingGem.gem.level - 1 + } -> Lv. ${this.dragAndDroppingGem.gem.level}!`, + 'warn' + ); + } else { + Engine.NotificationManager.Notify( + "You don't have enough, you need " + cost + ' gold to upgrade this gem.', + 'warn' + ); + } + } + this.isDragAndDroppingGem = false; + this.dragAndDroppingGem = null; + this.RebuildInventoryVisual(); + + return; + } Engine.GameScene.towerPanel.vGems.forEach((internVG) => { if (overlapping || !this.dragAndDroppingGem) return; let ddbb = this.dragAndDroppingGem.copyContainerToBB(); @@ -132,7 +200,7 @@ export default class GemTab extends GuiObject { let y = Engine.GameScene.towerPanel.container.y + vb.y; let vgbb = new PIXI.Rectangle(x, y, vb.width, vb.height); - console.log(ddbb, vgbb, ddbb.getBounds().intersects(vgbb)); + // console.log(ddbb, vgbb, ddbb.getBounds().intersects(vgbb)); if (ddbb.getBounds().intersects(vgbb)) { if (internVG && internVG.gem == null) overlapping = internVG; } diff --git a/src/classes/gui/Gemsmith.ts b/src/classes/gui/Gemsmith.ts new file mode 100644 index 0000000..eb509de --- /dev/null +++ b/src/classes/gui/Gemsmith.ts @@ -0,0 +1,60 @@ +import * as PIXI from 'pixi.js'; +import GuiObject from '../GuiObject'; +import GameAssets from '../Assets'; +import { VisualGemSlot } from './TowerPanel'; + +export default class Gemsmith extends GuiObject { + private bounds: PIXI.Rectangle; + public sellVGem: VisualGemSlot; + public upgradeVGem: VisualGemSlot; + + // ! NOTE: Gemsmith logic is contained within GemTab.ts + constructor(bounds: PIXI.Rectangle) { + super(false); + this.bounds = bounds; + this.container.x = this.bounds.x; + this.container.y = this.bounds.y; + let background = new PIXI.Sprite({ + x: 0, + y: 0, + width: this.bounds.width, + height: this.bounds.height, + texture: GameAssets.BannerGemsmith, + }); + this.container.addChild(background); + let sellLabel = new PIXI.Text({ + x: 40, + y: this.bounds.height / 6.5, + text: 'Sell gem', + style: new PIXI.TextStyle({ + fill: 0xffdb00, // orange color + fontSize: 18, + stroke: { + color: 0x000000, + width: 2, + }, + }), + }); + this.container.addChild(sellLabel); + let upgradeLabel = new PIXI.Text({ + x: 155, + y: this.bounds.height / 6.5, + text: 'Upgrade gem', + style: new PIXI.TextStyle({ + fill: 0x2df937, + fontSize: 18, + stroke: { + color: 0x000000, + width: 2, + }, + }), + }); + this.container.addChild(upgradeLabel); + this.sellVGem = new VisualGemSlot(0, this.container, null, 'SELL'); + this.sellVGem.container.x = 45; + this.sellVGem.container.y = this.bounds.height / 4; + this.upgradeVGem = new VisualGemSlot(0, this.container, null, 'UPGRADE'); + this.upgradeVGem.container.x = 180; + this.upgradeVGem.container.y = this.bounds.height / 4; + } +} diff --git a/src/classes/gui/Sidebar.ts b/src/classes/gui/Sidebar.ts index 8389279..a3a34fc 100644 --- a/src/classes/gui/Sidebar.ts +++ b/src/classes/gui/Sidebar.ts @@ -3,10 +3,12 @@ import GuiObject from '../GuiObject'; import GameAssets from '../Assets'; import TowerTab from './TowerTab'; import GemTab from './GemTab'; +import Gemsmith from './Gemsmith'; export default class Sidebar extends GuiObject { public towerTab: TowerTab; public gemTab: GemTab; + public gemsmith: Gemsmith; private bounds: PIXI.Rectangle; private sidebarSprite: PIXI.NineSliceSprite; @@ -33,8 +35,17 @@ export default class Sidebar extends GuiObject { this.towerTab = new TowerTab(towerTabRect); this.container.addChild(this.towerTab.container); - const gemTabRect = new PIXI.Rectangle(60, 180, this.bounds.width - 65, this.bounds.height - 280); + const gemTabRect = new PIXI.Rectangle(60, 180, this.bounds.width - 65, this.bounds.height - 280 - 255); this.gemTab = new GemTab(gemTabRect); this.container.addChild(this.gemTab.container); + + const gemSmithRect = new PIXI.Rectangle( + 60, + 185 + gemTabRect.height, + this.bounds.width - 65, + this.bounds.height - 840 + ); + this.gemsmith = new Gemsmith(gemSmithRect); + this.container.addChild(this.gemsmith.container); } } diff --git a/src/classes/gui/Tooltip.ts b/src/classes/gui/Tooltip.ts index b6767f3..7678ba2 100644 --- a/src/classes/gui/Tooltip.ts +++ b/src/classes/gui/Tooltip.ts @@ -179,7 +179,9 @@ export default class Tooltip extends GuiObject { this.gemDescriptionText.alpha = 1; this.titleText.text = `Lv. ${gem.level} ` + gem.definition.name; - this.gemDescriptionText.text = gem.definition.description; + this.gemDescriptionText.text = + `Valued at ${gem.definition.initialGemValue + gem.currentGemImprovement().gemValueUp} gold. ` + + gem.definition.description; } public Show(x, y) { this.container.alpha = 1; diff --git a/src/classes/gui/TowerPanel.ts b/src/classes/gui/TowerPanel.ts index f75b2e7..c090ebf 100644 --- a/src/classes/gui/TowerPanel.ts +++ b/src/classes/gui/TowerPanel.ts @@ -15,7 +15,7 @@ export class VisualGemSlot extends GuiObject { private frame: PIXI.Sprite; public i: number = 0; public gem: Gem = null; - constructor(index: number, parent: PIXI.Container, gem: Gem | null) { + constructor(index: number, parent: PIXI.Container, gem: Gem | null, extra?) { super(true); let gtexture; this.i = index; @@ -24,7 +24,11 @@ export class VisualGemSlot extends GuiObject { this.background = new PIXI.Sprite({ texture: GameAssets.Frame01Texture, }); - if (gem == null) { + if (gem == null && !extra) { + gtexture = GameAssets.PlusIconTexture; + } else if (extra == 'SELL') { + gtexture = GameAssets.GoldTexture; + } else if (extra == 'UPGRADE') { gtexture = GameAssets.PlusIconTexture; } else { gtexture = gem.texture; @@ -36,12 +40,24 @@ export class VisualGemSlot extends GuiObject { }); this.background.width = Engine.GridCellSize; this.background.height = Engine.GridCellSize; - if (gem == null) { + if (gem == null && !extra) { this.iconSprite.x = Engine.GridCellSize / 2; this.iconSprite.y = Engine.GridCellSize / 2; this.iconSprite.width = Engine.GridCellSize / 2; this.iconSprite.height = Engine.GridCellSize / 2; this.iconSprite.anchor.set(0.5, 0.5); + } else if (extra == 'SELL') { + this.iconSprite.x = 4; + this.iconSprite.y = 4; + this.iconSprite.width = Engine.GridCellSize - 8; + this.iconSprite.height = Engine.GridCellSize - 8; + } else if (extra == 'UPGRADE') { + this.iconSprite.x = Engine.GridCellSize / 2; + this.iconSprite.y = Engine.GridCellSize / 2; + this.iconSprite.width = Engine.GridCellSize / 2; + this.iconSprite.height = Engine.GridCellSize / 2; + this.iconSprite.tint = 0x2df937; + this.iconSprite.anchor.set(0.5, 0.5); } else { this.iconSprite.x = 4; this.iconSprite.y = 4;