diff --git a/data/fh/buildings.json b/data/fh/buildings.json index e10abd819..80e3bc768 100644 --- a/data/fh/buildings.json +++ b/data/fh/buildings.json @@ -1629,8 +1629,11 @@ "hide": 6, "gold": 10 }, - "upgrades": [], - "manualUpgrades": 1, + "upgrades": [ + { + "manual": 1 + } + ], "repair": [ 3, 4 @@ -2149,8 +2152,17 @@ "hide": 0, "gold": 0 }, - "upgrades": [], - "manualUpgrades": 1, + "upgrades": [ + { + "manual": 1 + }, + { + "manual": 1 + }, + { + "manual": 1 + } + ], "repair": [ 2, 2, diff --git a/data/fh/label-spoiler/en.json b/data/fh/label-spoiler/en.json index bb620b575..53e577a4b 100644 --- a/data/fh/label-spoiler/en.json +++ b/data/fh/label-spoiler/en.json @@ -1360,11 +1360,62 @@ "wealth": { ".": "Wealth", "automation": "Fully automated (applied in scenario summary)", + "level": "(incl. +{0} Wealth bonus)", "reward": "Wealth Favor", "text": "Each character will gain +1 gold per coin on their loot cards." } } }, + "pets": { + "fh-01": { + ".": "Piranha Pig", + "1": "All characters immediately perform:" + }, + "fh-02": { + ".": "Hound", + "1": "All characters add +1%game.action.attack% to all their melee attacks targeting enemies adjacent to any of the character's allies this round." + }, + "fh-03": { + ".": "Spitting Drake", + "1": "All characters add %game.condition.poison% to all their ranged attacks this round." + }, + "fh-04": { + ".": "Rending Drake", + "1": "All characters add %game.condition.wound% to all their melee attacks this round." + }, + "fh-05": { + ".": "Black Imp", + "1": "In initiative order, all characters immediately perform:" + }, + "fh-06": { + ".": "Forest Imp", + "1": "In initiative order, all characters immediately perform:" + }, + "fh-07": { + ".": "Snow Imp", + "1": "In initiative order, all characters immediately perform:" + }, + "fh-08": { + ".": "Ooze", + "1": "Create one 1-,2-, or 3-hex obstacle anywhere on the revealed map." + }, + "fh-09": { + ".": "Ruined Machine", + "1": "All characters immediately perform:" + }, + "fh-10": { + ".": "Lightning Eel", + "1": "All characters suffers %game.damage:2% to %game.card.recover% one of their level 1 or X discarded cards." + }, + "fh-11": { + ".": "HE-RO-IC-S", + "1": "In initiative order, all characters can individually choose to immediately perform:" + }, + "fh-12": { + ".": "Brummix", + "1": "During card selection, reveal the top card of each active monster set's ability deck for the round before each character selects their cards." + } + }, "custom": { "fh": { "astral": { diff --git a/data/fh/label/en.json b/data/fh/label/en.json index c8d71c5c1..67248019f 100644 --- a/data/fh/label/en.json +++ b/data/fh/label/en.json @@ -487,7 +487,11 @@ "garden": { ".": "Garden", "1": "Plant herbs, rotate this card 180°, then stop - Gain 1 herb from each planted plot, then rotate this card 180° then stop", - "2": "Gain 1 collective herb from each planted plot, then plant herbs" + "2": "Gain 1 collective herb from each planted plot, then plant herbs", + "plots": { + ".": "Manage Garden (%wip%)", + "interaction": "Plant/Harvest herbs" + } }, "hall-of-revelry": { ".": "Hall of Revelry", @@ -557,7 +561,18 @@ "2": "Bring one pet into each scenario.
Capacity: 8", "3": "Bring two pets into each scenario.
Capacity: 8", "4": "Bring two pets into each scenario.
Capacity: 12", - "5": "Characters cannot use pets" + "5": "Characters cannot use pets", + "pets": { + ".": "Pets", + "add": "Add Pet", + "catch": "Catch Pet", + "confirmAdd": "Click again to add Pet to Stables", + "edit": "Edit Pets", + "empty": "Currently no Pets in Stables", + "interaction": "Manage Pets", + "name": "Name", + "showAll": "Show all" + } }, "tavern": { ".": "Tavern", diff --git a/data/fh/monster/black-imp.json b/data/fh/monster/black-imp.json index 011426b5a..b8531209b 100644 --- a/data/fh/monster/black-imp.json +++ b/data/fh/monster/black-imp.json @@ -2,7 +2,7 @@ "name": "black-imp", "edition": "fh", "deck": "imp", - "catching": true, + "pet": "05", "count": 10, "baseStat": { "type": "normal", diff --git a/data/fh/monster/forest-imp.json b/data/fh/monster/forest-imp.json index e8fe79365..1d8ccbd70 100644 --- a/data/fh/monster/forest-imp.json +++ b/data/fh/monster/forest-imp.json @@ -3,7 +3,7 @@ "edition": "fh", "deck": "imp", "flying": true, - "catching": true, + "pet": "06", "count": 10, "baseStat": { "type": "normal" diff --git a/data/fh/monster/hound.json b/data/fh/monster/hound.json index 9fc0f9208..8767bf6fb 100644 --- a/data/fh/monster/hound.json +++ b/data/fh/monster/hound.json @@ -1,7 +1,7 @@ { "name": "hound", "edition": "fh", - "catching": true, + "pet": "02", "count": 6, "baseStat": { "type": "normal" diff --git a/data/fh/monster/lightning-eel.json b/data/fh/monster/lightning-eel.json index fd0942fe8..a04486f1d 100644 --- a/data/fh/monster/lightning-eel.json +++ b/data/fh/monster/lightning-eel.json @@ -1,7 +1,7 @@ { "name": "lightning-eel", "edition": "fh", - "catching": true, + "pet": "10", "count": 10, "baseStat": { "type": "normal", diff --git a/data/fh/monster/ooze.json b/data/fh/monster/ooze.json index 8e800c2af..874e6b7ca 100644 --- a/data/fh/monster/ooze.json +++ b/data/fh/monster/ooze.json @@ -1,7 +1,7 @@ { "name": "ooze", "edition": "fh", - "catching": true, + "pet": "08", "count": 10, "baseStat": { "type": "normal" diff --git a/data/fh/monster/piranha-pig.json b/data/fh/monster/piranha-pig.json index 4e92aba19..226437a34 100644 --- a/data/fh/monster/piranha-pig.json +++ b/data/fh/monster/piranha-pig.json @@ -1,7 +1,7 @@ { "name": "piranha-pig", "edition": "fh", - "catching": true, + "pet": "01", "count": 10, "baseStat": { "type": "normal" diff --git a/data/fh/monster/rending-drake.json b/data/fh/monster/rending-drake.json index 607e8d841..7b4db5ef5 100644 --- a/data/fh/monster/rending-drake.json +++ b/data/fh/monster/rending-drake.json @@ -1,7 +1,7 @@ { "name": "rending-drake", "edition": "fh", - "catching": true, + "pet": "04", "count": 6, "baseStat": { "type": "normal" diff --git a/data/fh/monster/ruined-machine.json b/data/fh/monster/ruined-machine.json index 4fb65a372..dc18344bf 100644 --- a/data/fh/monster/ruined-machine.json +++ b/data/fh/monster/ruined-machine.json @@ -1,7 +1,7 @@ { "name": "ruined-machine", "edition": "fh", - "catching": true, + "pet": "09", "count": 10, "baseStat": { "type": "normal", diff --git a/data/fh/monster/snow-imp-scenario-65.json b/data/fh/monster/snow-imp-scenario-65.json index bad57d24f..696b629b4 100644 --- a/data/fh/monster/snow-imp-scenario-65.json +++ b/data/fh/monster/snow-imp-scenario-65.json @@ -3,7 +3,7 @@ "edition": "fh", "deck": "imp", "flying": true, - "catching": true, + "pet": "07", "hidden": true, "count": 10, "standeeShare": "snow-imp", diff --git a/data/fh/monster/snow-imp.json b/data/fh/monster/snow-imp.json index c879928ce..c447040bf 100644 --- a/data/fh/monster/snow-imp.json +++ b/data/fh/monster/snow-imp.json @@ -3,7 +3,7 @@ "edition": "fh", "deck": "imp", "flying": true, - "catching": true, + "pet": "07", "count": 10, "baseStat": { "type": "normal" diff --git a/data/fh/monster/spitting-drake.json b/data/fh/monster/spitting-drake.json index 35ab3d9c8..ff66e5e4f 100644 --- a/data/fh/monster/spitting-drake.json +++ b/data/fh/monster/spitting-drake.json @@ -2,7 +2,7 @@ "name": "spitting-drake", "edition": "fh", "flying": true, - "catching": true, + "pet": "03", "count": 6, "baseStat": { "type": "normal" diff --git a/data/fh/pets.json b/data/fh/pets.json new file mode 100644 index 000000000..e2fee4e63 --- /dev/null +++ b/data/fh/pets.json @@ -0,0 +1,188 @@ +[ + { + "id": "01", + "cardId": 361, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-01.1%", + "small": true, + "subActions": [ + { + "type": "loot", + "value": 1 + } + ] + } + }, + { + "id": "02", + "cardId": 362, + "lost": true, + "round": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-02.1%", + "small": true + } + }, + { + "id": "03", + "cardId": 363, + "lost": true, + "round": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-03.1%", + "small": true + } + }, + { + "id": "04", + "cardId": 364, + "lost": true, + "round": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-04.1%", + "small": true + } + }, + { + "id": "05", + "cardId": 365, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-05.1%", + "small": true, + "subActions": [ + { + "type": "attack", + "value": 2 + } + ] + } + }, + { + "id": "06", + "cardId": 366, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-06.1%", + "small": true, + "subActions": [ + { + "type": "heal", + "value": 3, + "subActions": [ + { + "type": "specialTarget", + "value": "self" + } + ] + } + ] + } + }, + { + "id": "07", + "cardId": 367, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-07.1%", + "small": true, + "subActions": [ + { + "type": "move", + "value": 2 + } + ] + } + }, + { + "id": "08", + "cardId": 368, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-08.1%", + "small": true + } + }, + { + "id": "09", + "cardId": 369, + "lost": true, + "round": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-09.1%", + "small": true, + "subActions": [ + { + "type": "shield", + "value": 1 + } + ] + } + }, + { + "id": "10", + "cardId": 370, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-10.1%", + "small": true + } + }, + { + "id": "11", + "cardId": 371, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-11.1%", + "small": true, + "subActions": [ + { + "type": "concatenation", + "value": ",", + "subActions": [ + { + "type": "attack", + "value": 1 + }, + { + "type": "attack", + "value": 1 + }, + { + "type": "heal", + "value": 1, + "subActions": [ + { + "type": "specialTarget", + "value": "self" + } + ] + } + ] + } + ] + } + }, + { + "id": "12", + "cardId": 372, + "lost": true, + "action": { + "type": "custom", + "value": "%data.pets.fh-12.1%", + "small": true + } + } +] \ No newline at end of file diff --git a/data/fh/sections/040-1.json b/data/fh/sections/040-1.json new file mode 100644 index 000000000..473877fa5 --- /dev/null +++ b/data/fh/sections/040-1.json @@ -0,0 +1,10 @@ +{ + "index": "40.1", + "name": "Brummix", + "edition": "fh", + "conclusion": true, + "named": true, + "rewards": { + "pet": "12" + } +} \ No newline at end of file diff --git a/data/fh/sections/135-4.json b/data/fh/sections/135-4.json new file mode 100644 index 000000000..a37ca401d --- /dev/null +++ b/data/fh/sections/135-4.json @@ -0,0 +1,10 @@ +{ + "index": "135.4", + "name": "HE-RO-IC-S", + "edition": "fh", + "conclusion": true, + "named": true, + "rewards": { + "pet": "11" + } +} \ No newline at end of file diff --git a/package.json b/package.json index c0e2c406a..25056d758 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gloomhavensecretariat", - "version": "0.103.1", + "version": "0.104.0", "license": "AGPL3", "description": "Gloomhaven Secretariat is a Gloomhaven/Frosthaven Companion app.", "homepage": "https://gloomhaven-secretariat.de", @@ -85,17 +85,18 @@ }, "private": true, "dependencies": { - "@angular/animations": "^18.2.11", + "@angular/animations": "^18.2.12", "@angular/cdk": "^18.2.12", - "@angular/common": "^18.2.11", - "@angular/compiler": "^18.2.11", - "@angular/core": "^18.2.11", - "@angular/forms": "^18.2.11", - "@angular/platform-browser": "^18.2.11", - "@angular/platform-browser-dynamic": "^18.2.11", - "@angular/router": "^18.2.11", - "@angular/service-worker": "^18.2.11", + "@angular/common": "^18.2.12", + "@angular/compiler": "^18.2.12", + "@angular/core": "^18.2.12", + "@angular/forms": "^18.2.12", + "@angular/platform-browser": "^18.2.12", + "@angular/platform-browser-dynamic": "^18.2.12", + "@angular/router": "^18.2.12", + "@angular/service-worker": "^18.2.12", "autocompleter": "^9.3.2", + "browserslist": "^4.24.2", "leaflet": "^1.9.4", "mermaid": "^10.9.1", "ng-in-viewport": "^16.1.0", @@ -105,9 +106,9 @@ "zone.js": "~0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.11", - "@angular/cli": "^18.2.11", - "@angular/compiler-cli": "^18.2.11", + "@angular-devkit/build-angular": "^18.2.12", + "@angular/cli": "^18.2.12", + "@angular/compiler-cli": "^18.2.12", "@types/d3": "^7.4.3", "@types/dom-screen-wake-lock": "1.0.3", "@types/dompurify": "^3.0.5", @@ -117,7 +118,7 @@ "electron": "^33.2.0", "electron-builder": "^25.1.8", "http-server": "^14.1.1", - "husky": "^9.1.6", + "husky": "^9.1.7", "jasmine-core": "~5.4.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", @@ -127,4 +128,4 @@ "nodemon": "^3.1.7", "typescript": "~5.5.4" } -} \ No newline at end of file +} diff --git a/scripts/build-data.js b/scripts/build-data.js index 2694c07c0..b8204ae02 100644 --- a/scripts/build-data.js +++ b/scripts/build-data.js @@ -74,6 +74,7 @@ for (edition_path of edition_dirs) { edition_data['challenges'] = load_file(edition_path, 'challenges.json', []); edition_data['trials'] = load_file(edition_path, 'trials.json', []); edition_data['favors'] = load_file(edition_path, 'favors.json', []); + edition_data['pets'] = load_file(edition_path, 'pets.json', []); if (edition_data['campaign']) { edition_data['campaign']['buildings'] = load_file(edition_path, 'buildings.json', undefined); diff --git a/scripts/sort/sorter/monster.mjs b/scripts/sort/sorter/monster.mjs index 9e1893d6c..6970108a5 100644 --- a/scripts/sort/sorter/monster.mjs +++ b/scripts/sort/sorter/monster.mjs @@ -25,5 +25,5 @@ export const sortMonster = function (monster) { }); } - return sortObjectKeys(monster, 'name', 'thumbnail', 'thumbnailUrl', 'noThumbnail', 'noArtwork', 'edition', 'deck', 'boss', 'bb', 'flying', 'immortal', 'catching', 'hidden', 'count', 'randomCount', 'standeeCount', 'standeeShare', 'standeeShareEdition', 'baseStat', 'stats'); + return sortObjectKeys(monster, 'name', 'thumbnail', 'thumbnailUrl', 'noThumbnail', 'noArtwork', 'edition', 'deck', 'boss', 'bb', 'flying', 'immortal', 'pet', 'hidden', 'count', 'randomCount', 'standeeCount', 'standeeShare', 'standeeShareEdition', 'baseStat', 'stats'); } \ No newline at end of file diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index df12dbd6d..a8b86d728 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -83,6 +83,9 @@ import { MonsterStatsComponent } from './ui/figures/monster/stats/stats'; import { MonsterStatsDialogComponent } from './ui/figures/monster/stats/stats-dialog'; import { ObjectiveContainerComponent } from './ui/figures/objective-container/objective-container'; import { PartyBuildingsComponent } from './ui/figures/party/buildings/buildings'; +import { GardenComponent } from './ui/figures/party/buildings/garden/garden'; +import { PetCardComponent } from './ui/figures/party/buildings/stables/pet-card/pet-card'; +import { StablesComponent } from './ui/figures/party/buildings/stables/stables'; import { BuildingUpgradeDialog } from './ui/figures/party/buildings/upgrade-dialog/upgrade-dialog'; import { PartySheetDialogComponent } from './ui/figures/party/party-sheet-dialog'; import { ScenarioRequirementsComponent, ScenarioRequirementsDialogComponent } from './ui/figures/party/requirements/requirements'; @@ -243,6 +246,7 @@ describe('AppComponent', () => { FigureErrorsDialogComponent, FooterComponent, GameClockDialogComponent, + GardenComponent, GhsCeilPipe, GhsDurationLabelPipe, GhsFloorPipe, @@ -298,6 +302,7 @@ describe('AppComponent', () => { PartySheetDialogComponent, PartyWeekDialogComponent, PerkLabelComponent, + PetCardComponent, PointerInputDirective, ScenarioChartDialogComponent, ScenarioChartPopupDialog, @@ -320,6 +325,7 @@ describe('AppComponent', () => { SettingMenuTitleComponent, SettingsDebugMenuComponent, SettingsMenuComponent, + StablesComponent, StandeeComponent, StatisticsDialogComponent, StatsListComponent, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dbdbabb5c..45200abc4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -84,6 +84,9 @@ import { MonsterStatsComponent } from './ui/figures/monster/stats/stats'; import { MonsterStatsDialogComponent } from './ui/figures/monster/stats/stats-dialog'; import { ObjectiveContainerComponent } from './ui/figures/objective-container/objective-container'; import { PartyBuildingsComponent } from './ui/figures/party/buildings/buildings'; +import { GardenComponent } from './ui/figures/party/buildings/garden/garden'; +import { PetCardComponent } from './ui/figures/party/buildings/stables/pet-card/pet-card'; +import { StablesComponent } from './ui/figures/party/buildings/stables/stables'; import { BuildingUpgradeDialog } from './ui/figures/party/buildings/upgrade-dialog/upgrade-dialog'; import { PartySheetDialogComponent } from './ui/figures/party/party-sheet-dialog'; import { ScenarioRequirementsComponent, ScenarioRequirementsDialogComponent } from './ui/figures/party/requirements/requirements'; @@ -245,6 +248,7 @@ import { TreasuresToolComponent } from './ui/tools/treasures/treasures-tool'; FigureErrorsDialogComponent, FooterComponent, GameClockDialogComponent, + GardenComponent, GhsCeilPipe, GhsDurationLabelPipe, GhsFloorPipe, @@ -300,6 +304,7 @@ import { TreasuresToolComponent } from './ui/tools/treasures/treasures-tool'; PartySheetDialogComponent, PartyWeekDialogComponent, PerkLabelComponent, + PetCardComponent, PointerInputDirective, ScenarioChartDialogComponent, ScenarioChartPopupDialog, @@ -322,6 +327,7 @@ import { TreasuresToolComponent } from './ui/tools/treasures/treasures-tool'; SettingMenuTitleComponent, SettingsDebugMenuComponent, SettingsMenuComponent, + StablesComponent, StandeeComponent, StatisticsDialogComponent, StatsListComponent, diff --git a/src/app/game/businesslogic/BuildingsManager.ts b/src/app/game/businesslogic/BuildingsManager.ts index f88987f4c..7da6c4506 100644 --- a/src/app/game/businesslogic/BuildingsManager.ts +++ b/src/app/game/businesslogic/BuildingsManager.ts @@ -2,14 +2,27 @@ import { Game } from "../model/Game"; import { BuildingData, BuildingRewards } from "../model/data/BuildingData"; import { ScenarioData } from "../model/data/ScenarioData"; import { gameManager } from "./GameManager"; +import { settingsManager } from "./SettingsManager"; export class BuildingsManager { game: Game; + petsAvailable: boolean = false; + petsEnabled: boolean = false; + gardenAvailable: boolean = false; + gardenEnabled: boolean = false; + constructor(game: Game) { this.game = game; } + update() { + this.petsAvailable = gameManager.fhRules() && gameManager.game.party.buildings.find((value) => value.name == 'stables' && value.level) != undefined; + this.petsEnabled = this.petsAvailable && settingsManager.settings.fhPets; + this.gardenAvailable = gameManager.fhRules() && gameManager.game.party.buildings.find((value) => value.name == 'garden' && value.level) != undefined; + this.gardenEnabled = this.gardenAvailable && settingsManager.settings.fhGarden; + } + applyRewards(rewards: BuildingRewards) { if (rewards.defense) { this.game.party.defense += rewards.defense; diff --git a/src/app/game/businesslogic/GameManager.ts b/src/app/game/businesslogic/GameManager.ts index 8190ddd9d..d39e61705 100644 --- a/src/app/game/businesslogic/GameManager.ts +++ b/src/app/game/businesslogic/GameManager.ts @@ -110,6 +110,7 @@ export class GameManager { this.scenarioRulesManager.applyScenarioRulesAlways(); } this.roundManager.firstRound = this.game.round == 0 && this.game.roundResets.length == 0 && this.game.roundResetsHidden.length == 0; + this.buildingsManager.update(); this.challengesManager.update(); this.trialsManager.update(); } diff --git a/src/app/game/businesslogic/RoundManager.ts b/src/app/game/businesslogic/RoundManager.ts index d63a4ee62..f1695c1fa 100644 --- a/src/app/game/businesslogic/RoundManager.ts +++ b/src/app/game/businesslogic/RoundManager.ts @@ -613,6 +613,12 @@ export class RoundManager { this.game.party.townGuardDeck = townGuardDeck.toModel(); } + if (this.game.party.pets) { + this.game.party.pets.forEach((value) => { + value.lost = false; + }) + } + gameManager.trialsManager.applyTrialCards(); gameManager.stateManager.standeeDialogCanceled = false; diff --git a/src/app/game/businesslogic/ScenarioManager.ts b/src/app/game/businesslogic/ScenarioManager.ts index d26228457..5400c8d72 100644 --- a/src/app/game/businesslogic/ScenarioManager.ts +++ b/src/app/game/businesslogic/ScenarioManager.ts @@ -14,6 +14,7 @@ import { MonsterStandeeData, RoomData } from "../model/data/RoomData"; import { ScenarioData, ScenarioRewards } from "../model/data/ScenarioData"; import { gameManager } from "./GameManager"; import { settingsManager } from "./SettingsManager"; +import { PetIdentifier } from "../model/data/PetCard"; export class ScenarioManager { @@ -253,6 +254,12 @@ export class ScenarioManager { } }) } + + if (rewards.pet) { + if (gameManager.game.party.pets.find((value) => value.edition == scenario.edition && value.name == rewards.pet) == undefined) { + gameManager.game.party.pets.push(new PetIdentifier(rewards.pet, scenario.edition)); + } + } } if (settingsManager.settings.automaticUnlocking && rewards.unlockCharacter && this.game.unlockedCharacters.indexOf(rewards.unlockCharacter) == -1) { @@ -555,7 +562,7 @@ export class ScenarioManager { if (figure instanceof Monster) { figure.entities.forEach((entity) => { if (entities.indexOf(entity) != -1) { - gameManager.entityManager.addCondition(entity,figure, new Condition(ConditionName.muddle)); + gameManager.entityManager.addCondition(entity, figure, new Condition(ConditionName.muddle)); } }) } diff --git a/src/app/game/businesslogic/SettingsManager.ts b/src/app/game/businesslogic/SettingsManager.ts index 99bec6d58..aff630c50 100644 --- a/src/app/game/businesslogic/SettingsManager.ts +++ b/src/app/game/businesslogic/SettingsManager.ts @@ -369,6 +369,7 @@ export class SettingsManager { value.challenges = value.challenges || []; value.trials = value.trials || []; value.favors = value.favors || []; + value.pets = value.pets || []; value.personalQuests = value.personalQuests || []; value.label = value.label || {}; value.labelSpoiler = value.labelSpoiler || {}; @@ -452,6 +453,16 @@ export class SettingsManager { return favor; }) + value.pets.map((pet, index) => { + if (!pet.edition) { + pet.edition = value.edition; + } + if (!pet.id) { + pet.id = (index < 9 ? '0' : '') + (index + 1); + } + return pet; + }) + gameManager.editionData.push(value); gameManager.editionData.sort((a, b) => { return this.settings.editionDataUrls.indexOf(a.url) - this.settings.editionDataUrls.indexOf(b.url); diff --git a/src/app/game/businesslogic/StateManager.ts b/src/app/game/businesslogic/StateManager.ts index d81905ccc..173c52654 100644 --- a/src/app/game/businesslogic/StateManager.ts +++ b/src/app/game/businesslogic/StateManager.ts @@ -526,9 +526,9 @@ export class StateManager { }) } - before(...info: string[]) { + before(...info: (string | number | boolean)[]) { window.document.body.classList.add('working'); - this.addToUndo(info || []); + this.addToUndo(info && info.map((value) => '' + value) || []); } async after(timeout: number = 1, autoBackup: boolean = false, revisionChange: number = 1, type: string = "game", revision: number = 0, undolength: number = 1) { diff --git a/src/app/game/model/Building.ts b/src/app/game/model/Building.ts new file mode 100644 index 000000000..388dd2ef8 --- /dev/null +++ b/src/app/game/model/Building.ts @@ -0,0 +1,22 @@ +import { LootType } from "./data/Loot"; + +export class BuildingModel { + name: string; + level: number; + state: "normal" | "damaged" | "wrecked"; + + constructor(name: string = "", level: number = 1, state: "normal" | "damaged" | "wrecked" = "normal") { + this.name = name; + this.level = level; + this.state = state; + } +} + + +export class GardenModel { + + flipped: boolean = false; + automated: boolean = true; + plots: LootType[] = []; + +} \ No newline at end of file diff --git a/src/app/game/model/Game.ts b/src/app/game/model/Game.ts index 2be398528..589031720 100644 --- a/src/app/game/model/Game.ts +++ b/src/app/game/model/Game.ts @@ -247,6 +247,7 @@ export class Game { this.party.players = this.party.players || []; this.party.casualScenarios = this.party.casualScenarios || []; + this.party.pets = this.party.pets || []; this.parties = [this.party]; if (model.parties) { diff --git a/src/app/game/model/Party.ts b/src/app/game/model/Party.ts index 3a2cc0f6a..ad3350376 100644 --- a/src/app/game/model/Party.ts +++ b/src/app/game/model/Party.ts @@ -1,10 +1,11 @@ import { GameAttackModifierDeckModel } from "./data/AttackModifier"; import { GameCharacterModel } from "./Character"; -import { BuildingModel } from "./data/BuildingData"; +import { BuildingModel, GardenModel } from "./Building"; import { CountIdentifier, Identifier } from "./data/Identifier"; import { Loot, LootType } from "./data/Loot"; import { GameScenarioModel } from "./Scenario"; import { ConditionName } from "./data/Condition"; +import { PetIdentifier } from "./data/PetCard"; export class Party { @@ -57,11 +58,13 @@ export class Party { campaignStickers: string[] = []; townGuardDeck: GameAttackModifierDeckModel | undefined; buildings: BuildingModel[] = []; + pets: PetIdentifier[] = []; lootDeckEnhancements: Loot[] = []; lootDeckFixed: LootType[] = []; lootDeckSections: string[] = []; trials: number = -1; + garden: GardenModel | undefined; } \ No newline at end of file diff --git a/src/app/game/model/Settings.ts b/src/app/game/model/Settings.ts index 50c1da98b..ad2787d7b 100644 --- a/src/app/game/model/Settings.ts +++ b/src/app/game/model/Settings.ts @@ -109,7 +109,9 @@ export class Settings { feedbackErrorsIgnore: string[] = []; fhChallenges: boolean = false; fhChallengesApply: boolean = true; + fhGarden: boolean = true; fhGhItems: boolean = false; + fhPets: boolean = true; fhSecondEdition: boolean = false; fhStyle: boolean = false; fhTrials: boolean = false; diff --git a/src/app/game/model/data/BuildingData.ts b/src/app/game/model/data/BuildingData.ts index ed0236d59..8ae994cfd 100644 --- a/src/app/game/model/data/BuildingData.ts +++ b/src/app/game/model/data/BuildingData.ts @@ -5,9 +5,8 @@ import { WorldMapCoordinates } from "./WorldMap"; export class BuildingData implements Editional { id: string = ""; name: string = ""; - costs: BuildingCosts = { "prosperity": 0, "lumber": 0, "metal": 0, "hide": 0, "gold": 0 }; + costs: BuildingCosts = { "prosperity": 0, "lumber": 0, "metal": 0, "hide": 0, "gold": 0, "manual": 0 }; upgrades: BuildingCosts[] = []; - manualUpgrades: number = 0; repair: number[] | undefined = undefined; rebuild: BuildingCosts[] = []; effectNormal: string[] = []; @@ -35,22 +34,10 @@ export class BuildingRewards { errata: string = ""; } -export type BuildingCostType = "prosperity" | "lumber" | "metal" | "hide" | "gold"; +export type BuildingCostType = "prosperity" | "lumber" | "metal" | "hide" | "gold" | "manual"; export type BuildingCosts = Record; -export class BuildingModel { - name: string; - level: number; - state: "normal" | "damaged" | "wrecked"; - - constructor(name: string = "", level: number = 1, state: "normal" | "damaged" | "wrecked" = "normal") { - this.name = name; - this.level = level; - this.state = state; - } -} - export class SelectResourceResult { characters: Character[]; characterSpent: BuildingCosts[]; diff --git a/src/app/game/model/data/EditionData.ts b/src/app/game/model/data/EditionData.ts index 7d8dcfbf0..54543302e 100644 --- a/src/app/game/model/data/EditionData.ts +++ b/src/app/game/model/data/EditionData.ts @@ -9,6 +9,7 @@ import { ItemData } from "./ItemData"; import { MonsterData } from "./MonsterData"; import { Perk } from "./Perks"; import { PersonalQuest } from "./PersonalQuest"; +import { PetCard } from "./PetCard"; import { ScenarioData } from "./ScenarioData"; import { Favor, TrialCard } from "./Trials"; @@ -32,6 +33,7 @@ export class EditionData implements Editional { challenges: ChallengeCard[] = []; trials: TrialCard[] = []; favors: Favor[] = []; + pets: PetCard[] = []; worldMap: { width: number, height: number } | undefined; extendWorldMap: string | undefined; label: any = {}; diff --git a/src/app/game/model/data/Loot.ts b/src/app/game/model/data/Loot.ts index 506b3d292..94aaf538a 100644 --- a/src/app/game/model/data/Loot.ts +++ b/src/app/game/model/data/Loot.ts @@ -22,7 +22,11 @@ export enum LootType { special2 = "special2" } -export const resourceLootTypes: LootType[] = [LootType.lumber, LootType.metal, LootType.hide, LootType.arrowvine, LootType.axenut, LootType.corpsecap, LootType.flamefruit, LootType.rockroot, LootType.snowthistle]; +export const materialResourceLootTypes: LootType[] = [LootType.lumber, LootType.metal, LootType.hide]; + +export const herbResourceLootTypes: LootType[] = [LootType.arrowvine, LootType.axenut, LootType.corpsecap, LootType.flamefruit, LootType.rockroot, LootType.snowthistle]; + +export const resourceLootTypes: LootType[] = [...materialResourceLootTypes, ...herbResourceLootTypes]; export const enhancableLootTypes: LootType[] = [...resourceLootTypes, LootType.money]; diff --git a/src/app/game/model/data/MonsterData.ts b/src/app/game/model/data/MonsterData.ts index 25ca83de2..dc82b1c9b 100644 --- a/src/app/game/model/data/MonsterData.ts +++ b/src/app/game/model/data/MonsterData.ts @@ -26,7 +26,7 @@ export class MonsterData implements Editional, Spoilable { noThumbnail: boolean = false; noArtwork: boolean = false; - catching: boolean = false; + pet: string = ""; // from Editional edition: string = ""; @@ -60,7 +60,7 @@ export class MonsterData implements Editional, Spoilable { this.thumbnailUrl = monsterData.thumbnailUrl; this.noThumbnail = monsterData.noThumbnail; this.noArtwork = monsterData.noArtwork; - this.catching = monsterData.catching; + this.pet = monsterData.pet; this.edition = monsterData.edition; this.spoiler = monsterData.spoiler; this.errors = monsterData.errors || []; diff --git a/src/app/game/model/data/PetCard.ts b/src/app/game/model/data/PetCard.ts new file mode 100644 index 000000000..464db67b5 --- /dev/null +++ b/src/app/game/model/data/PetCard.ts @@ -0,0 +1,28 @@ +import { Action } from "./Action"; +import { Editional } from "./Editional"; +import { Identifier } from "./Identifier"; + +export class PetCard implements Editional { + + id: string = ""; + cardId: number = 0; + edition: string = ""; + lost: boolean = false; + round: boolean = false; + action: Action | undefined; + +} + +export class PetIdentifier extends Identifier { + + petname: string; + active: boolean; + lost: boolean; + + constructor(name: string, edition: string, petname: string = "", active: boolean = false, lost: boolean = false) { + super(name, edition); + this.petname = petname; + this.active = active; + this.lost = lost; + } +} \ No newline at end of file diff --git a/src/app/game/model/data/ScenarioData.ts b/src/app/game/model/data/ScenarioData.ts index 0f0aa6888..fb5369792 100644 --- a/src/app/game/model/data/ScenarioData.ts +++ b/src/app/game/model/data/ScenarioData.ts @@ -151,6 +151,7 @@ export class ScenarioRewards { ignoredBonus: string[] = []; overlaySticker: WorldMapOverlay | undefined = undefined; overlayCampaignSticker: WorldMapOverlay | undefined = undefined; + pet: string | undefined = undefined; hints: ScenarioRewardHints | undefined = undefined; } @@ -192,6 +193,7 @@ export class ScenarioRewardHints { chooseUnlockCharacter: string[] = []; overlaySticker: string = ""; overlayCampaignSticker: string = ""; + pet: string = ""; } export class ScenarioFinish { diff --git a/src/app/ui/figures/actions/action.html b/src/app/ui/figures/actions/action.html index fe49c1e44..3156b4db1 100644 --- a/src/app/ui/figures/actions/action.html +++ b/src/app/ui/figures/actions/action.html @@ -1,5 +1,5 @@ + [ngClass]="{'highlight' : highlight, 'inline' : inline, 'right' : right, 'text-black' : textBlack || right, 'fh' : fhStyle, 'aoe' : hasAOE}"> @@ -16,7 +16,8 @@ - + {{values[1]}} @@ -24,7 +25,7 @@ , , , @@ -102,7 +103,7 @@ + [ngClass]="{'text-white' : !textBlack && !right, 'small' : action.small}"> @@ -115,7 +116,7 @@ , + [ngClass]="{'small' : action.small,'text-white' : !textBlack && !right}">
@@ -146,12 +147,12 @@ [ghs-tooltip]="'monster.toggleAction'" [delay]="1" [disabled]="!isInteractiveApplicableAction" [ngClass]="{'action-interactive' : isInteractiveApplicableAction, 'action-interactive-highlight': highlightAction()}"> + [ngClass]="{'small' : action.small,'text-white' : !textBlack && !right}">
@@ -164,13 +165,13 @@ [ghs-tooltip]="'monster.toggleAction'" [delay]="1" [disabled]="!isInteractiveApplicableAction" [ngClass]="{'action-interactive' : isInteractiveApplicableAction, 'action-interactive-highlight': highlightAction()}"> + [ngClass]="{'small' : action.small,'text-white' : !textBlack && !right}" + [ghs-label-args]="[ monster ? '%data.monster.' + monster.name + '%' : '', action.value]">
@@ -180,12 +181,12 @@ + [ngClass]="{'small' : action.small,'text-white' : !textBlack && !right}">
@@ -195,12 +196,12 @@ + [ngClass]="{'small' : action.small,'text-white' : !textBlack && !right}">
@@ -208,15 +209,15 @@ - @@ -225,15 +226,15 @@ - + @@ -243,12 +244,12 @@ + [ngClass]="{'small' : action.small,'text-white' : !textBlack && !right}"> @@ -261,7 +262,7 @@ , {{action.value}}: + [inline]="inline" [right]="right" [textBlack]="textBlack" [hexSize]="hexSize" + [interactiveAbilities]="interactiveAbilities" [interactiveActions]="interactiveActions" + (interactiveActionsChange)="onInteractiveActionsChange($event)" [relative]="relative || forceRelative" + [statsCalculation]="statsCalculation" [index]="actionIndex" [style]="style"> @@ -479,7 +480,7 @@ *ngFor="let subAction of (statsCalculation && additionalSubActions.length > 0 ? additionalSubActions : subActions) | slice:(hasAOE ? 1 : 0); let index = index;"> , @@ -504,10 +505,11 @@
+ [textBlack]="textBlack" [hexSize]="hexSize" [interactiveAbilities]="interactiveAbilities" + [interactiveActions]="interactiveActions" (interactiveActionsChange)="onInteractiveActionsChange($event)" + [relative]="relative || forceRelative" [statsCalculation]="statsCalculation" + [index]="(actionIndex ? actionIndex + '-' : '') + getOrigIndex(subAction)" [monsterType]="monsterType" + [style]="style">
diff --git a/src/app/ui/figures/actions/action.scss b/src/app/ui/figures/actions/action.scss index 7247fd0a9..e39b0939c 100644 --- a/src/app/ui/figures/actions/action.scss +++ b/src/app/ui/figures/actions/action.scss @@ -889,8 +889,6 @@ } &.right { - color: var(--ghs-color-black); - filter: none; justify-content: flex-end; text-align: right; @@ -927,11 +925,38 @@ align-items: flex-end; align-self: flex-end; } + } + } - .ghs-svg { - filter: none; - } + &.text-black { + color: var(--ghs-color-black); + filter: none !important; + + .ghs-svg, + .placeholder-action .icon { + filter: none !important; + } + + .action-default, + .action-custom, + .action-grant, + .action-trigger, + .action-specialTarget, + .action-condition { + filter: none !important; } + + .type, + .value, + .condition-value, + .custom-value, + .grant-value, + .special-target-value, + .monster-type, + .summon-value { + filter: none !important; + } + } .subactions, @@ -1095,6 +1120,20 @@ } } + &.text-black { + .subactions, + .condition-subactions, + .monster-type-subactions, + .subaction-custom .custom-subactions, + .subaction-grant .grant-subactions, + .subaction-specialTarget .special-target-subactions { + + &::before { + opacity: 0.5; + } + } + } + .action-concatenation { .action-concatenation-item { margin: 0; diff --git a/src/app/ui/figures/actions/action.ts b/src/app/ui/figures/actions/action.ts index 74c9541d7..d5152c946 100644 --- a/src/app/ui/figures/actions/action.ts +++ b/src/app/ui/figures/actions/action.ts @@ -27,6 +27,7 @@ export class ActionComponent implements OnInit, OnDestroy { @Input('action') origAction!: Action | undefined; @Input() relative: boolean = false; @Input() inline: boolean = false; + @Input() textBlack: boolean = false; @Input() right: boolean = false; @Input() highlight: boolean = false; @Input() interactiveAbilities: boolean = false; diff --git a/src/app/ui/figures/actions/actions.html b/src/app/ui/figures/actions/actions.html index eb512ae80..745a6261c 100644 --- a/src/app/ui/figures/actions/actions.html +++ b/src/app/ui/figures/actions/actions.html @@ -2,8 +2,10 @@
diff --git a/src/app/ui/figures/actions/actions.ts b/src/app/ui/figures/actions/actions.ts index ba719a1d9..49304d4b4 100644 --- a/src/app/ui/figures/actions/actions.ts +++ b/src/app/ui/figures/actions/actions.ts @@ -21,6 +21,7 @@ export class ActionsComponent implements OnInit, OnDestroy { @Input() actions!: Action[]; @Input() relative: boolean = false; @Input() inline: boolean = false; + @Input() textBlack: boolean = false; @Input() right: boolean = false; @Input() statsCalculation: boolean = false; @Input() interactiveAbilities: boolean = false; diff --git a/src/app/ui/figures/actions/summon/action-summon.html b/src/app/ui/figures/actions/summon/action-summon.html index 27e40b793..cb2981fea 100644 --- a/src/app/ui/figures/actions/summon/action-summon.html +++ b/src/app/ui/figures/actions/summon/action-summon.html @@ -8,11 +8,11 @@ [ngClass]="{'action-interactive' : isInteractiveApplicableAction(), 'action-interactive-highlight': highlightAction()}"> (     diff --git a/src/app/ui/figures/actions/summon/action-summon.ts b/src/app/ui/figures/actions/summon/action-summon.ts index 78feb3c0b..652d6c85c 100644 --- a/src/app/ui/figures/actions/summon/action-summon.ts +++ b/src/app/ui/figures/actions/summon/action-summon.ts @@ -25,6 +25,7 @@ export class ActionSummonComponent implements OnChanges, OnDestroy { @Input() monsterType: MonsterType | undefined; @Input() objective: ObjectiveContainer | undefined; @Input() action!: Action; + @Input() textBlack: boolean = false; @Input() right: boolean = false; @Input('spawn') isSpawn: boolean = false; @Input() additional: boolean = false; diff --git a/src/app/ui/figures/character/character.ts b/src/app/ui/figures/character/character.ts index a66d36a47..c42069e00 100644 --- a/src/app/ui/figures/character/character.ts +++ b/src/app/ui/figures/character/character.ts @@ -288,12 +288,12 @@ export class CharacterComponent implements OnInit, OnDestroy { dragTokenEnd(value: number) { if (this.token != 0) { if (this.character.primaryToken < 0) { - gameManager.stateManager.before("setCharacterToken", gameManager.characterManager.characterName(this.character), '' + (this.character.token + this.token)); + gameManager.stateManager.before("setCharacterToken", gameManager.characterManager.characterName(this.character), (this.character.token + this.token)); this.character.token += this.token; this.token = 0; gameManager.stateManager.after(); } else { - gameManager.stateManager.before("setCharacterTokenValue", gameManager.characterManager.characterName(this.character), '%data.characterToken.' + this.character.name + '.' + this.character.tokens[this.character.primaryToken] + '%', '' + (this.character.token + this.token)); + gameManager.stateManager.before("setCharacterTokenValue", gameManager.characterManager.characterName(this.character), '%data.characterToken.' + this.character.name + '.' + this.character.tokens[this.character.primaryToken] + '%', (this.character.token + this.token)); this.character.tokenValues[this.character.primaryToken] += this.token; if (this.character.tags.find((tag) => tag === 'time_tokens') && this.character.primaryToken == 0 && this.character.tokenValues[0] > 5) { diff --git a/src/app/ui/figures/character/dialogs/summondialog.html b/src/app/ui/figures/character/dialogs/summondialog.html index 8c08b0e4f..f6f7d11bb 100644 --- a/src/app/ui/figures/character/dialogs/summondialog.html +++ b/src/app/ui/figures/character/dialogs/summondialog.html @@ -35,7 +35,7 @@ diff --git a/src/app/ui/figures/character/dialogs/summondialog.ts b/src/app/ui/figures/character/dialogs/summondialog.ts index 7ece0d6af..06732a8b8 100644 --- a/src/app/ui/figures/character/dialogs/summondialog.ts +++ b/src/app/ui/figures/character/dialogs/summondialog.ts @@ -97,7 +97,7 @@ export class CharacterSummonDialog { } addCustomSummon() { - gameManager.stateManager.before("addCustomSummon", gameManager.characterManager.characterName(this.character), '' + this.summonNumber, this.summonColor); + gameManager.stateManager.before("addCustomSummon", gameManager.characterManager.characterName(this.character), this.summonNumber, this.summonColor); let summon: Summon = new Summon(uuidv4(), this.summonName, "", this.character.level, this.summonNumber, this.summonColor); summon.state = SummonState.new; gameManager.characterManager.addSummon(this.character, summon); diff --git a/src/app/ui/figures/character/event-effects/event-effects.ts b/src/app/ui/figures/character/event-effects/event-effects.ts index 227a7daf1..acfff5c97 100644 --- a/src/app/ui/figures/character/event-effects/event-effects.ts +++ b/src/app/ui/figures/character/event-effects/event-effects.ts @@ -297,7 +297,7 @@ export class EventEffectsDialog implements OnInit, OnDestroy { next: (result: unknown) => { if (result) { const itemData = result as ItemData; - gameManager.stateManager.before("eventEffect.drawRandomItem" + (blueprint ? 'Blueprint' : ''), '' + itemData.id, itemData.edition, itemData.name); + gameManager.stateManager.before("eventEffect.drawRandomItem" + (blueprint ? 'Blueprint' : ''), itemData.id, itemData.edition, itemData.name); gameManager.game.party.unlockedItems.push(new CountIdentifier('' + itemData.id, itemData.edition)); gameManager.stateManager.after(); } @@ -320,7 +320,7 @@ export class EventEffectsDialog implements OnInit, OnDestroy { const scenarioData = result as ScenarioData; if (section) { const unlocks = scenarioData.unlocks ? scenarioData.unlocks.map((unlock) => '%data.scenarioNumber:' + unlock + '%').join(', ') : ''; - gameManager.stateManager.before("eventEffect.drawRandomScenarioSection", '' + scenarioData.index, scenarioData.edition, scenarioData.name, unlocks); + gameManager.stateManager.before("eventEffect.drawRandomScenarioSection", scenarioData.index, scenarioData.edition, scenarioData.name, unlocks); gameManager.game.party.conclusions.push(new GameScenarioModel('' + scenarioData.index, scenarioData.edition, scenarioData.group)); if (scenarioData.unlocks) { scenarioData.unlocks.forEach((unlock) => { @@ -329,7 +329,7 @@ export class EventEffectsDialog implements OnInit, OnDestroy { } gameManager.stateManager.after(); } else { - gameManager.stateManager.before("eventEffect.drawRandomScenario", '' + scenarioData.index, scenarioData.edition, scenarioData.name); + gameManager.stateManager.before("eventEffect.drawRandomScenario", scenarioData.index, scenarioData.edition, scenarioData.name); gameManager.game.party.manualScenarios.push(new GameScenarioModel(scenarioData.index, scenarioData.edition, scenarioData.group)); gameManager.stateManager.after(); } diff --git a/src/app/ui/figures/character/sheet/character-sheet.html b/src/app/ui/figures/character/sheet/character-sheet.html index bc30b52a1..a6e0fbc02 100644 --- a/src/app/ui/figures/character/sheet/character-sheet.html +++ b/src/app/ui/figures/character/sheet/character-sheet.html @@ -70,7 +70,7 @@ + [ghs-label-args]="[character.progress.donations]">
+ + + + + + + + +
figure instanceof Character && figure.progress.equippedItems.find((item) => item.edition == 'fh' && item.name == '247' && (!item.tags || item.tags.indexOf(ItemFlags.consumed) == -1))) == undefined; + } } this.dialogRef.closed.subscribe({ @@ -631,9 +640,9 @@ export class EntityMenuDialogComponent { } } - dead() { - if (this.data.figure instanceof Monster && this.data.entity instanceof MonsterEntity) { - gameManager.stateManager.before("entityDead", "data.monster." + this.data.figure.name, "monster." + this.data.entity.type, "" + this.data.entity.number); + dead(catching: boolean = false, force: boolean = false) { + if (this.data.figure instanceof Monster && this.data.entity instanceof MonsterEntity && (!catching || this.catching && (!this.catchingDisabled || force))) { + gameManager.stateManager.before(catching ? 'buildings.stables.pets.catch' : "entityDead", "data.monster." + this.data.figure.name, "monster." + this.data.entity.type, "" + this.data.entity.number); this.data.entity.dead = true; if (this.data.figure.entities.every((monsterEntity) => monsterEntity.dead)) { @@ -642,12 +651,25 @@ export class EntityMenuDialogComponent { } } + if (catching && gameManager.game.party.pets.find((value) => this.data.figure instanceof Monster && value.edition == this.data.figure.edition && value.name == this.data.figure.pet) == undefined) { + gameManager.game.party.pets.push(new PetIdentifier(this.data.figure.pet, this.data.figure.edition)); + const character = gameManager.game.figures.find((figure) => figure instanceof Character && figure.progress.equippedItems.find((item) => item.edition == 'fh' && item.name == '247' && (!item.tags || item.tags.indexOf(ItemFlags.consumed) == -1))) as Character; + if (character) { + const item = character.progress.equippedItems.find((item) => item.edition == 'fh' && item.name == '247' && (!item.tags || item.tags.indexOf(ItemFlags.consumed) == -1)); + if (item) { + item.tags = item.tags || []; + item.tags.push(ItemFlags.consumed); + } + } + } + setTimeout(() => { if (this.data.figure instanceof Monster && this.data.entity instanceof MonsterEntity) { gameManager.monsterManager.removeMonsterEntity(this.data.figure, this.data.entity); gameManager.stateManager.after(); } }, !settingsManager.settings.animations ? 0 : 1500); + ghsDialogClosingHelper(this.dialogRef, true) } else if (this.data.figure instanceof Character && this.data.entity instanceof Summon) { gameManager.stateManager.before("summonDead", gameManager.characterManager.characterName(this.data.figure), "data.summon." + this.data.entity.name); this.data.entity.dead = true; @@ -657,6 +679,7 @@ export class EntityMenuDialogComponent { gameManager.stateManager.after(); } }, !settingsManager.settings.animations ? 0 : 1500); + ghsDialogClosingHelper(this.dialogRef, true) } else if (this.data.figure instanceof ObjectiveContainer && this.data.entity instanceof ObjectiveEntity) { let name = this.data.figure.name; if (!name) { @@ -680,8 +703,8 @@ export class EntityMenuDialogComponent { gameManager.stateManager.after(); } }, !settingsManager.settings.animations || !this.data.figure.entities.some((entity) => gameManager.entityManager.isAlive(entity)) ? 0 : 1500); + ghsDialogClosingHelper(this.dialogRef, true) } - ghsDialogClosingHelper(this.dialogRef, true) } changeAttack(value: number) { @@ -931,7 +954,7 @@ export class EntityMenuDialogComponent { if (token < 0) { token = 0; } - gameManager.stateManager.before("setCharacterToken", gameManager.characterManager.characterName(this.data.entity), '' + token); + gameManager.stateManager.before("setCharacterToken", gameManager.characterManager.characterName(this.data.entity), token); this.data.entity.token = token; this.characterToken = 0; gameManager.stateManager.after(); @@ -943,7 +966,7 @@ export class EntityMenuDialogComponent { if (tokenValue < 0) { tokenValue = 0; } - gameManager.stateManager.before("setCharacterTokenValue", gameManager.characterManager.characterName(this.data.entity), '%data.characterToken.' + this.data.figure.name + '.' + this.data.entity.tokens[index] + '%', '' + tokenValue); + gameManager.stateManager.before("setCharacterTokenValue", gameManager.characterManager.characterName(this.data.entity), '%data.characterToken.' + this.data.figure.name + '.' + this.data.entity.tokens[index] + '%', tokenValue); this.data.entity.tokenValues[index] = tokenValue; this.characterTokenValues[index] = 0; gameManager.stateManager.after(); @@ -1227,7 +1250,7 @@ export class EntityMenuDialogComponent { } if (this.health != 0) { - gameManager.stateManager.before("changeObjectiveEntityHP", this.data.figure.title || this.data.figure.name || this.data.figure.escort ? 'escort' : 'objective', '' + this.data.entity.number, ghsValueSign(this.health)); + gameManager.stateManager.before("changeObjectiveEntityHP", this.data.figure.title || this.data.figure.name || this.data.figure.escort ? 'escort' : 'objective', this.data.entity.number, ghsValueSign(this.health)); gameManager.entityManager.changeHealth(this.data.entity, this.data.figure, this.health); if (this.data.entity.health <= 0) { gameManager.objectiveManager.removeObjectiveEntity(this.data.figure, this.data.entity); @@ -1238,7 +1261,7 @@ export class EntityMenuDialogComponent { const newId = this.data.entity.number + this.id; if (newId != this.data.entity.number) { - gameManager.stateManager.before("changeObjectiveEntityNumber", this.data.figure.title || this.data.figure.name || this.data.figure.escort ? 'escort' : 'objective', '' + this.data.entity.number, "" + newId); + gameManager.stateManager.before("changeObjectiveEntityNumber", this.data.figure.title || this.data.figure.name || this.data.figure.escort ? 'escort' : 'objective', this.data.entity.number, "" + newId); this.data.entity.number = newId; gameManager.stateManager.after(); } @@ -1251,7 +1274,7 @@ export class EntityMenuDialogComponent { const newMarker = OBJECTIV_MARKERS[ghsModulo(this.marker + OBJECTIV_MARKERS.indexOf(this.data.entity.marker), OBJECTIV_MARKERS.length)]; if (newMarker != this.data.entity.marker) { - gameManager.stateManager.before("changeObjectiveEntityMarker", this.data.figure.title || this.data.figure.name || this.data.figure.escort ? 'escort' : 'objective', '' + this.data.entity.number, newMarker); + gameManager.stateManager.before("changeObjectiveEntityMarker", this.data.figure.title || this.data.figure.name || this.data.figure.escort ? 'escort' : 'objective', this.data.entity.number, newMarker); this.data.entity.marker = newMarker; gameManager.stateManager.after(); } @@ -1307,7 +1330,7 @@ export class EntityMenuDialogComponent { if (settingsManager.settings.characterShieldRetaliate && (this.data.entity instanceof Character || this.data.entity instanceof ObjectiveEntity) || settingsManager.settings.standeeShieldRetaliate && (this.data.entity instanceof Summon || this.data.entity instanceof MonsterEntity)) { if (this.entityShield.value) { if (!this.data.entity.shield || this.entityShield.value != this.data.entity.shield.value) { - gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, "setEntityShield"), '' + this.entityShield.value); + gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, "setEntityShield"), this.entityShield.value); this.data.entity.shield = this.entityShield; gameManager.stateManager.after(); } @@ -1320,7 +1343,7 @@ export class EntityMenuDialogComponent { if (this.entityShieldPersistent.value) { if (!this.data.entity.shieldPersistent || this.entityShieldPersistent.value != this.data.entity.shieldPersistent.value) { - gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, "setEntityShieldPersistent"), '' + this.entityShield.value); + gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, "setEntityShieldPersistent"), this.entityShield.value); this.data.entity.shieldPersistent = this.entityShieldPersistent; gameManager.stateManager.after(); } @@ -1373,14 +1396,14 @@ export class EntityMenuDialogComponent { closeAMs() { if (this.bless != 0) { - gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.bless < 0 ? "removeCondition" + (this.bless < -1 ? 's' : '') : "addCondition" + (this.bless > 1 ? 's' : '')), AttackModifierType.bless, '' + (this.bless > 0 ? this.bless : this.bless * -1)); + gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.bless < 0 ? "removeCondition" + (this.bless < -1 ? 's' : '') : "addCondition" + (this.bless > 1 ? 's' : '')), AttackModifierType.bless, (this.bless > 0 ? this.bless : this.bless * -1)); this.changeAttackModifier(AttackModifierType.bless, this.bless); gameManager.stateManager.after(); this.bless = 0; } if (this.curse != 0) { - gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.curse < 0 ? "removeCondition" + (this.curse < -1 ? 's' : '') : "addCondition" + (this.curse > 1 ? 's' : '')), AttackModifierType.curse, '' + (this.curse > 0 ? this.curse : this.curse * -1)); + gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.curse < 0 ? "removeCondition" + (this.curse < -1 ? 's' : '') : "addCondition" + (this.curse > 1 ? 's' : '')), AttackModifierType.curse, (this.curse > 0 ? this.curse : this.curse * -1)); this.changeAttackModifier(AttackModifierType.curse, this.curse); gameManager.stateManager.after(); this.curse = 0; @@ -1388,7 +1411,7 @@ export class EntityMenuDialogComponent { if (this.empower != 0) { if (this.empowerChar || this.empower < 0) { - gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.empower < 0 ? "removeCondition" + (this.empower < -1 ? 's' : '') : "addCondition" + (this.empower > 1 ? 's' : '')), AttackModifierType.empower, '' + (this.empower > 0 ? this.empower : this.empower * -1)); + gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.empower < 0 ? "removeCondition" + (this.empower < -1 ? 's' : '') : "addCondition" + (this.empower > 1 ? 's' : '')), AttackModifierType.empower, (this.empower > 0 ? this.empower : this.empower * -1)); if (this.empowerChar && this.empower > 0) { const additional = gameManager.attackModifierManager.getAdditional(this.empowerChar, AttackModifierType.empower); for (let i = 0; i < Math.min(this.empower, additional.length); i++) { @@ -1409,7 +1432,7 @@ export class EntityMenuDialogComponent { if (this.enfeeble != 0) { if (this.enfeebleChar || this.enfeeble < 0) { - gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.enfeeble < 0 ? "removeCondition" + (this.enfeeble < -1 ? 's' : '') : "addCondition" + (this.enfeeble > 1 ? 's' : '')), AttackModifierType.enfeeble, '' + (this.enfeeble > 0 ? this.enfeeble : this.enfeeble * -1)); + gameManager.stateManager.before(...gameManager.entityManager.undoInfos(this.data.entity, this.data.figure, this.enfeeble < 0 ? "removeCondition" + (this.enfeeble < -1 ? 's' : '') : "addCondition" + (this.enfeeble > 1 ? 's' : '')), AttackModifierType.enfeeble, (this.enfeeble > 0 ? this.enfeeble : this.enfeeble * -1)); if (this.enfeebleChar && this.enfeeble > 0) { const additional = gameManager.attackModifierManager.getAdditional(this.enfeebleChar, AttackModifierType.enfeeble); for (let i = 0; i < Math.min(this.enfeeble, additional.length); i++) { diff --git a/src/app/ui/figures/items/brew/brew.html b/src/app/ui/figures/items/brew/brew.html index a4f806d13..d246a75ba 100644 --- a/src/app/ui/figures/items/brew/brew.html +++ b/src/app/ui/figures/items/brew/brew.html @@ -76,9 +76,9 @@ = + [ghs-label]="'game.items.brewing.item'" [ghs-label-args]="[item.id, item.name]"> + [ghs-label]="'game.items.brewing.item'" [ghs-label-args]="[brewed.id, brewed.name]"> @@ -87,7 +87,7 @@
+ [ghs-label-args]="[brewed.id, brewed.name, gameManager.characterManager.characterName(character,true) , otherCharacter ? gameManager.characterManager.characterName(otherCharacter,true): '']">
diff --git a/src/app/ui/figures/items/brew/brew.ts b/src/app/ui/figures/items/brew/brew.ts index 38e60f5b4..3fc4467e4 100644 --- a/src/app/ui/figures/items/brew/brew.ts +++ b/src/app/ui/figures/items/brew/brew.ts @@ -141,7 +141,7 @@ export class ItemsBrewDialog implements OnInit, OnDestroy { brewInternal(character: Character, itemData: ItemData) { this.otherCharacter = character != this.character ? character : undefined - gameManager.stateManager.before(!this.otherCharacter ? 'brewPotion' : 'brewPotionOther', this.character.name, '' + itemData.id, itemData.name, character.name); + gameManager.stateManager.before(!this.otherCharacter ? 'brewPotion' : 'brewPotionOther', this.character.name, itemData.id, itemData.name, character.name); this.herbs.forEach((herb) => { if (this.fhSupportSpent[herb]) { gameManager.game.party.loot[herb] = (gameManager.game.party.loot[herb] || 0) - (this.fhSupportSpent[herb] || 0); diff --git a/src/app/ui/figures/items/character/item-character.ts b/src/app/ui/figures/items/character/item-character.ts index c848b9787..28ff89747 100644 --- a/src/app/ui/figures/items/character/item-character.ts +++ b/src/app/ui/figures/items/character/item-character.ts @@ -38,7 +38,7 @@ export class CharacterItemComponent { toggleEquippedItem(force: boolean = false) { const owned = this.character.progress.items.find((identifier) => identifier.name == '' + this.item.id && identifier.edition == this.item.edition) != undefined; if ((this.setup || force) && (owned || !this.bbBlocked() || force)) { - gameManager.stateManager.before(this.equipped() ? 'unequipItem' : 'equipItem', gameManager.characterManager.characterName(this.character), '' + this.item.id, this.item.edition) + gameManager.stateManager.before(this.equipped() ? 'unequipItem' : 'equipItem', gameManager.characterManager.characterName(this.character), this.item.id, this.item.edition) gameManager.itemManager.toggleEquippedItem(this.item, this.character, force); if (gameManager.bbRules()) { if (!this.equipped() && owned) { @@ -72,7 +72,7 @@ export class CharacterItemComponent { const equipped = this.equipped(); if (equipped) { equipped.tags = equipped.tags || []; - gameManager.stateManager.before((equipped.tags.indexOf(flag) == -1 ? 'characterItemApply.' : 'characterItemUnapply.') + flag, gameManager.characterManager.characterName(this.character), '' + this.item.id, this.item.edition, this.item.name) + gameManager.stateManager.before((equipped.tags.indexOf(flag) == -1 ? 'characterItemApply.' : 'characterItemUnapply.') + flag, gameManager.characterManager.characterName(this.character), this.item.id, this.item.edition, this.item.name) if (equipped.tags.indexOf(flag) == -1) { if (!force && gameManager.challengesManager.apply && gameManager.challengesManager.isActive(1507, 'fh') && flag == ItemFlags.spent) { equipped.tags.push(ItemFlags.consumed); @@ -104,7 +104,7 @@ export class CharacterItemComponent { if (equipped) { equipped.tags = equipped.tags || []; const count = this.countFlag(flag); - gameManager.stateManager.before((count <= index ? 'characterItemApply.' : 'characterItemUnapply.') + flag, gameManager.characterManager.characterName(this.character), '' + this.item.id, this.item.edition, this.item.name); + gameManager.stateManager.before((count <= index ? 'characterItemApply.' : 'characterItemUnapply.') + flag, gameManager.characterManager.characterName(this.character), this.item.id, this.item.edition, this.item.name); if (count <= index) { for (let i = count; i <= index; i++) { equipped.tags.push(flag); diff --git a/src/app/ui/figures/items/dialog/items-dialog.html b/src/app/ui/figures/items/dialog/items-dialog.html index 103793ec9..73dccf72a 100644 --- a/src/app/ui/figures/items/dialog/items-dialog.html +++ b/src/app/ui/figures/items/dialog/items-dialog.html @@ -110,14 +110,14 @@ [ngClass]="{'disabled' : !selected || !gameManager.itemManager.canBuy(selected, character) && !gameManager.itemManager.canCraft(selected, character)}" (click)="selected ? (selected.cost ? buyItem(selected): craftItem(selected)) : false" [ghs-label]="'game.items.' + (selected ? (selected.cost ? 'buy' : 'craft') : 'choose')" - [ghs-label-args]="selected ? ['%data.items.' + selected.edition + '-' + selected.id + '%', '' + (selected.cost + gameManager.itemManager.pricerModifier())] : []"> + [ghs-label-args]="selected ? ['%data.items.' + selected.edition + '-' + selected.id + '%',(selected.cost + gameManager.itemManager.pricerModifier())] : []"> + [ghs-label-args]="['%data.items.' + selected.edition + '-' + selected.id + '%', gameManager.itemManager.itemSellValue(selected)]">
diff --git a/src/app/ui/figures/items/dialog/items-dialog.ts b/src/app/ui/figures/items/dialog/items-dialog.ts index 4330e8a4d..6b6e703f0 100644 --- a/src/app/ui/figures/items/dialog/items-dialog.ts +++ b/src/app/ui/figures/items/dialog/items-dialog.ts @@ -243,7 +243,7 @@ export class ItemsDialogComponent implements OnInit, OnDestroy { if (this.unlocks.indexOf(item) != -1 && revealed) { gameManager.game.party.unlockedItems = gameManager.game.party.unlockedItems || []; if (!this.unlocked(item)) { - gameManager.stateManager.before("addUnlockedItem", item.edition, '' + item.id, item.name); + gameManager.stateManager.before("addUnlockedItem", item.edition, item.id, item.name); gameManager.game.party.unlockedItems.push(new CountIdentifier('' + item.id, item.edition)); gameManager.stateManager.after(); this.updateEditionItems(); @@ -253,7 +253,7 @@ export class ItemsDialogComponent implements OnInit, OnDestroy { removeUnlocked(itemData: ItemData) { if (this.unlocked(itemData)) { - gameManager.stateManager.before("removeUnlockedItem", itemData.edition, '' + itemData.id, itemData.name); + gameManager.stateManager.before("removeUnlockedItem", itemData.edition, itemData.id, itemData.name); gameManager.game.party.unlockedItems = gameManager.game.party.unlockedItems || []; gameManager.game.party.unlockedItems = gameManager.game.party.unlockedItems.filter((identifier) => identifier.name != '' + itemData.id || identifier.edition != itemData.edition); gameManager.stateManager.after(); diff --git a/src/app/ui/figures/items/items.ts b/src/app/ui/figures/items/items.ts index e262ee158..ecee7f516 100644 --- a/src/app/ui/figures/items/items.ts +++ b/src/app/ui/figures/items/items.ts @@ -306,7 +306,7 @@ export class CharacterItemsComponent implements OnInit, OnDestroy { toggleEquippedItem(itemData: ItemData, force: boolean = false) { const disabled = gameManager.game.state != GameState.draw || gameManager.game.round > 0; if ((!disabled || force) && this.character.progress.items.find((identifier) => identifier.name == '' + itemData.id && identifier.edition == itemData.edition) != undefined) { - gameManager.stateManager.before(gameManager.itemManager.isEquipped(itemData, this.character) ? 'unequipItem' : 'equipItem', gameManager.characterManager.characterName(this.character), '' + itemData.id, itemData.edition) + gameManager.stateManager.before(gameManager.itemManager.isEquipped(itemData, this.character) ? 'unequipItem' : 'equipItem', gameManager.characterManager.characterName(this.character), itemData.id, itemData.edition) gameManager.itemManager.toggleEquippedItem(itemData, this.character, force) gameManager.stateManager.after(); } diff --git a/src/app/ui/figures/loot/loot-card.ts b/src/app/ui/figures/loot/loot-card.ts index a06598a2f..63f2a9445 100644 --- a/src/app/ui/figures/loot/loot-card.ts +++ b/src/app/ui/figures/loot/loot-card.ts @@ -142,7 +142,7 @@ export class LootComponent implements OnInit, OnChanges { if (this.loot.type == LootType.random_item && name) { randomItemIdentifier = charBefore.progress.equippedItems.find((value) => value.marker == "loot-random-item"); } else { - gameManager.stateManager.before("removeLootCard", gameManager.characterManager.characterName(charBefore), "game.loot." + this.loot.type, gameManager.lootManager.getValue(this.loot) + ''); + gameManager.stateManager.before("removeLootCard", gameManager.characterManager.characterName(charBefore), "game.loot." + this.loot.type, gameManager.lootManager.getValue(this.loot)); charBefore.lootCards = charBefore.lootCards.filter((index) => index != this.index); if (this.loot.type == LootType.money || this.loot.type == LootType.special1 || this.loot.type == LootType.special2) { charBefore.loot -= gameManager.lootManager.getValue(this.loot); @@ -163,7 +163,7 @@ export class LootComponent implements OnInit, OnChanges { const character = gameManager.game.figures.find((figure) => figure instanceof Character && figure.name == name); if (character instanceof Character) { if (this.loot.type != LootType.random_item) { - gameManager.stateManager.before("addLootCard", gameManager.characterManager.characterName(character), "game.loot." + this.loot.type, gameManager.lootManager.getValue(this.loot) + ''); + gameManager.stateManager.before("addLootCard", gameManager.characterManager.characterName(character), "game.loot." + this.loot.type, gameManager.lootManager.getValue(this.loot)); gameManager.lootManager.applyLoot(this.loot, character, this.index); gameManager.stateManager.after(); } else { @@ -179,7 +179,7 @@ export class LootComponent implements OnInit, OnChanges { next: (result) => { if (result) { const item = result as ItemData; - gameManager.stateManager.before("lootRandomItem", '' + item.id, item.edition, item.name, gameManager.characterManager.characterName(character)); + gameManager.stateManager.before("lootRandomItem", item.id, item.edition, item.name, gameManager.characterManager.characterName(character)); let itemIdentifier: Identifier = new Identifier('' + item.id, item.edition); gameManager.itemManager.addItemCount(item); if (character.lootCards.indexOf(this.index) == -1) { diff --git a/src/app/ui/figures/loot/loot-deck.ts b/src/app/ui/figures/loot/loot-deck.ts index c68ba4f3f..91f4df858 100644 --- a/src/app/ui/figures/loot/loot-deck.ts +++ b/src/app/ui/figures/loot/loot-deck.ts @@ -184,7 +184,7 @@ export class LootDeckComponent implements OnInit, OnDestroy, OnChanges { if (name) { const character = gameManager.game.figures.find((figure) => figure instanceof Character && figure.name == name); if (character instanceof Character) { - gameManager.stateManager.before(loot.type == LootType.random_item ? "lootRandomItem" : "addResource", gameManager.characterManager.characterName(character), "game.loot." + loot.type, this.lootManager.getValue(loot) + ''); + gameManager.stateManager.before(loot.type == LootType.random_item ? "lootRandomItem" : "addResource", gameManager.characterManager.characterName(character), "game.loot." + loot.type, this.lootManager.getValue(loot)); const result = gameManager.lootManager.applyLoot(loot, character, currentIndex); gameManager.stateManager.after(); if (result) { @@ -195,7 +195,7 @@ export class LootDeckComponent implements OnInit, OnDestroy, OnChanges { next: (result) => { if (result) { const item = result as ItemData; - gameManager.stateManager.before("lootRandomItem", '' + item.id, item.edition, item.name, gameManager.characterManager.characterName(character)); + gameManager.stateManager.before("lootRandomItem", item.id, item.edition, item.name, gameManager.characterManager.characterName(character)); let itemIdentifier: Identifier = new Identifier('' + item.id, item.edition); gameManager.itemManager.addItemCount(item); if (character.lootCards.indexOf(currentIndex) == -1) { @@ -243,7 +243,7 @@ export class LootDeckComponent implements OnInit, OnDestroy, OnChanges { next: (result) => { if (result) { const item = result as ItemData; - gameManager.stateManager.before("lootRandomItem", '' + item.id, item.edition, item.name, gameManager.characterManager.characterName(activeCharacter)); + gameManager.stateManager.before("lootRandomItem", item.id, item.edition, item.name, gameManager.characterManager.characterName(activeCharacter)); let itemIdentifier: Identifier = new Identifier('' + item.id, item.edition); gameManager.itemManager.addItemCount(item); if (activeCharacter.lootCards.indexOf(this.current) == -1) { diff --git a/src/app/ui/figures/monster/dialogs/numberpicker-dialog.html b/src/app/ui/figures/monster/dialogs/numberpicker-dialog.html index efd6bd464..506443eb3 100644 --- a/src/app/ui/figures/monster/dialogs/numberpicker-dialog.html +++ b/src/app/ui/figures/monster/dialogs/numberpicker-dialog.html @@ -13,9 +13,9 @@ + [ghs-label-args]="[entity.type, entity.number]"> + [ghs-label-args]="[type, entitiesLeft()]">
diff --git a/src/app/ui/figures/monster/stats/stats.html b/src/app/ui/figures/monster/stats/stats.html index 7716d12d3..8643434f5 100644 --- a/src/app/ui/figures/monster/stats/stats.html +++ b/src/app/ui/figures/monster/stats/stats.html @@ -23,10 +23,12 @@ [] - +
-
{ this.update(); } }); + } + + update() { if (!settingsManager.settings.statAnimations) { this.highlightActions = []; + } else { + this.highlightActions = [ActionType.shield, ActionType.retaliate]; } this.edition = gameManager.getEdition(this.monster); - this.catching = this.monster.catching && gameManager.game.party.buildings.find((buildingModel) => buildingModel.name == 'stables' && buildingModel.level > 0 && buildingModel.state != 'wrecked') != undefined; + this.catching = this.monster.pet != undefined && gameManager.buildingsManager.petsAvailable; + this.catched = this.catching && gameManager.buildingsManager.petsEnabled && gameManager.game.party.pets.find((value) => value.edition == this.monster.edition && value.name == this.monster.pet) != undefined; + this.setStats(); this.flying = this.monster.flying && (!this.monster.statEffect || this.monster.statEffect.flying != 'disabled') || this.monster.statEffect != undefined && this.monster.statEffect.flying == true; - gameManager.uiChange.subscribe({ - next: () => { - if (!settingsManager.settings.statAnimations) { - this.highlightActions = []; - } else { - this.highlightActions = [ActionType.shield, ActionType.retaliate]; - } - this.edition = gameManager.getEdition(this.monster); - this.catching = this.monster.catching && gameManager.game.party.buildings.find((buildingModel) => buildingModel.name == 'stables' && buildingModel.level > 0 && buildingModel.state != 'wrecked') != undefined; - this.setStats(); - this.flying = this.monster.flying && (!this.monster.statEffect || this.monster.statEffect.flying != 'disabled') || this.monster.statEffect != undefined && this.monster.statEffect.flying == true; - } - }) } setStats() { @@ -89,7 +85,7 @@ export class MonsterStatsComponent implements OnInit { setLevel(value: number) { if (value != this.monster.level) { - gameManager.stateManager.before("setLevel", "data.monster." + this.monster.name, '' + value); + gameManager.stateManager.before("setLevel", "data.monster." + this.monster.name, value); gameManager.monsterManager.setLevel(this.monster, value); gameManager.monsterManager.setLevel(this.monsterCopy, value); this.setStats(); diff --git a/src/app/ui/figures/objective-container/objective-container.ts b/src/app/ui/figures/objective-container/objective-container.ts index feb270cec..d50fe6646 100644 --- a/src/app/ui/figures/objective-container/objective-container.ts +++ b/src/app/ui/figures/objective-container/objective-container.ts @@ -182,7 +182,7 @@ export class ObjectiveContainerComponent implements OnInit, OnDestroy { dragHpEnd(value: number) { if (this.health != 0 && this.entity) { - gameManager.stateManager.before("changeObjectiveEntityHP", gameManager.objectiveManager.objectiveName(this.objective), ghsValueSign(this.health), '' + this.entity.number); + gameManager.stateManager.before("changeObjectiveEntityHP", gameManager.objectiveManager.objectiveName(this.objective), ghsValueSign(this.health), this.entity.number); gameManager.entityManager.changeHealth(this.entity, this.objective, this.health); if (this.entity.health <= 0 && this.entity.maxHealth > 0) { gameManager.objectiveManager.removeObjective(this.objective) @@ -240,7 +240,7 @@ export class ObjectiveContainerComponent implements OnInit, OnDestroy { } } - gameManager.stateManager.before('addObjective.entity', '' + (number + 1), name); + gameManager.stateManager.before('addObjective.entity', (number + 1), name); gameManager.objectiveManager.addObjectiveEntity(this.objective, number); gameManager.stateManager.after(); } diff --git a/src/app/ui/figures/party/buildings/buildings.html b/src/app/ui/figures/party/buildings/buildings.html index 869dbf932..3080241b0 100644 --- a/src/app/ui/figures/party/buildings/buildings.html +++ b/src/app/ui/figures/party/buildings/buildings.html @@ -6,7 +6,8 @@ [ghs-label-attribute]="'placeholder'"> -
+
@@ -72,6 +73,22 @@ + + + + + + + + + + @@ -88,7 +105,7 @@ + *ngIf="building.model.state == 'normal' && building.model.level < building.data.upgrades.length + 1 && !building.data.upgrades[building.model.level - 1].manual"> {{building.data.upgrades[building.model.level - 1].prosperity|| '-'}} @@ -120,7 +137,7 @@ span { + z-index: 2; } .name { @@ -216,6 +232,15 @@ } } } + + &.stables, + &.garden { + + .icon, + .text { + cursor: pointer; + } + } } } diff --git a/src/app/ui/figures/party/buildings/buildings.ts b/src/app/ui/figures/party/buildings/buildings.ts index cce5f0db5..af7026ff0 100644 --- a/src/app/ui/figures/party/buildings/buildings.ts +++ b/src/app/ui/figures/party/buildings/buildings.ts @@ -2,14 +2,17 @@ import { Dialog } from "@angular/cdk/dialog"; import { Component, Input, OnInit } from "@angular/core"; import { gameManager, GameManager } from "src/app/game/businesslogic/GameManager"; import { SettingsManager, settingsManager } from "src/app/game/businesslogic/SettingsManager"; +import { BuildingModel } from "src/app/game/model/Building"; import { Character } from "src/app/game/model/Character"; import { Party } from "src/app/game/model/Party"; import { Scenario } from "src/app/game/model/Scenario"; -import { BuildingCosts, BuildingData, BuildingModel, SelectResourceResult } from "src/app/game/model/data/BuildingData"; +import { BuildingCosts, BuildingData, SelectResourceResult } from "src/app/game/model/data/BuildingData"; import { LootType } from "src/app/game/model/data/Loot"; import { ScenarioData } from "src/app/game/model/data/ScenarioData"; import { ScenarioConclusionComponent } from "src/app/ui/footer/scenario/scenario-conclusion/scenario-conclusion"; import { ScenarioSummaryComponent } from "src/app/ui/footer/scenario/summary/scenario-summary"; +import { GardenComponent } from "./garden/garden"; +import { StablesComponent } from "./stables/stables"; import { BuildingUpgradeDialog } from "./upgrade-dialog/upgrade-dialog"; export type Building = { model: BuildingModel, data: BuildingData }; @@ -106,10 +109,6 @@ export class PartyBuildingsComponent implements OnInit { } upgradeable(building: Building): boolean { - if (building.data.manualUpgrades > 0 && building.model.level >= building.data.manualUpgrades) { - return true; - } - let costs: BuildingCosts = building.model.level ? building.data.upgrades[building.model.level - 1] : building.data.costs; if (building.model.level && !building.data.repair) { return false; @@ -191,7 +190,7 @@ export class PartyBuildingsComponent implements OnInit { } upgrade(building: Building, force: boolean = false) { - if (building.model.level < building.data.upgrades.length + 1 || building.model.level < building.data.manualUpgrades + 1) { + if (building.model.level < building.data.upgrades.length + 1) { if (this.upgradeable(building) || force) { const costs = building.model.level ? building.data.upgrades[building.model.level - 1] : building.data.costs; this.dialog.open(BuildingUpgradeDialog, { @@ -206,7 +205,7 @@ export class PartyBuildingsComponent implements OnInit { next: (result) => { if (force && result == true || result instanceof SelectResourceResult) { setTimeout(() => { - gameManager.stateManager.before(building.model.level ? "upgradeBuilding" : "buildBuilding", building.data.id, building.model.name, '' + (building.model.level + 1)); + gameManager.stateManager.before(building.model.level ? "upgradeBuilding" : "buildBuilding", building.data.id, building.model.name, (building.model.level + 1)); if (!force && result instanceof SelectResourceResult) { gameManager.lootManager.applySelectResources(result); } @@ -378,7 +377,7 @@ export class PartyBuildingsComponent implements OnInit { gameManager.stateManager.after(); } else if (!gameManager.buildingsManager.initialBuilding(building.data) && !gameManager.buildingsManager.availableBuilding(building.data) || building.model.level > 1) { - gameManager.stateManager.before("downgradeBuilding", building.data.id, building.model.name, '' + (building.model.level - 1)); + gameManager.stateManager.before("downgradeBuilding", building.data.id, building.model.name, (building.model.level - 1)); building.model.level--; if (building.model.level == 0) { building.model.state = 'normal'; @@ -398,4 +397,15 @@ export class PartyBuildingsComponent implements OnInit { } } + openStables() { + this.dialog.open(StablesComponent, { + panelClass: ['dialog'] + }) + } + + openGarden() { + this.dialog.open(GardenComponent, { + panelClass: ['dialog'] + }) + } } \ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/garden/garden.html b/src/app/ui/figures/party/buildings/garden/garden.html new file mode 100644 index 000000000..c30427d52 --- /dev/null +++ b/src/app/ui/figures/party/buildings/garden/garden.html @@ -0,0 +1,21 @@ +
+ + +
+
+
+
+
\ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/garden/garden.scss b/src/app/ui/figures/party/buildings/garden/garden.scss new file mode 100644 index 000000000..35efcc2bc --- /dev/null +++ b/src/app/ui/figures/party/buildings/garden/garden.scss @@ -0,0 +1,184 @@ +.garden-container { + display: flex; + flex-direction: column; + align-items: center; + color: var(--ghs-color-white); + + + .menu { + display: flex; + flex-direction: column; + + .title { + font-family: var(--ghs-font-title); + font-size: calc(var(--ghs-unit) * 3 * var(--ghs-dialog-factor)); + } + + .herbs { + display: flex; + justify-content: center; + align-items: center; + + .herb { + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + margin: calc(var(--ghs-unit) * 0.5 * var(--ghs-dialog-factor)); + + .ghs-svg { + width: calc(var(--ghs-unit) * 4 * var(--ghs-dialog-factor)); + height: auto; + filter: var(--ghs-filter-gray); + } + + .value { + font-size: calc(var(--ghs-unit) * 2 * var(--ghs-dialog-factor)); + color: var(--ghs-color-gray); + } + + &.active { + .ghs-svg { + filter: var(--ghs-filter-white); + } + + .value { + color: var(--ghs-color-white); + } + } + + &:hover { + .ghs-svg { + filter: var(--ghs-filter-white); + } + + .value { + color: var(--ghs-color-white); + } + } + + &.disabled { + cursor: initial; + + .ghs-svg { + filter: var(--ghs-filter-darkgray); + } + + .value { + color: var(--ghs-color-darkgray); + } + + &:hover { + .ghs-svg { + filter: var(--ghs-filter-darkgray); + } + + .value { + color: var(--ghs-color-darkgray); + } + } + } + } + } + } + + .garden { + position: relative; + display: block; + width: calc(var(--ghs-unit) * 31 * var(--ghs-dialog-factor)); + height: calc(var(--ghs-unit) * 27.1 * var(--ghs-dialog-factor)); + margin-top: calc(var(--ghs-unit) * 1 * var(--ghs-dialog-factor)); + background-repeat: no-repeat; + background-size: contain; + + &.level-1 { + background-image: url('~src/assets/images/world-map/fh/buildings/fh-24-garden-1.png'); + } + + &.level-2 { + background-image: url('~src/assets/images/world-map/fh/buildings/fh-24-garden-2.png'); + } + + &.level-3 { + background-image: url('~src/assets/images/world-map/fh/buildings/fh-24-garden-3.png'); + } + + &.level-4 { + background-image: url('~src/assets/images/world-map/fh/buildings/fh-24-garden-4.png'); + } + + .plot { + position: absolute; + display: block; + width: calc(var(--ghs-unit) * 5.7 * var(--ghs-dialog-factor)); + height: calc(var(--ghs-unit) * 9.9 * var(--ghs-dialog-factor)); + background-repeat: no-repeat; + background-size: 0; + transform: rotate(-45deg); + cursor: pointer; + + &:hover { + background-size: contain; + filter: brightness(1.2); + } + + &.disabled { + cursor: initial; + + &:hover { + filter: brightness(0.7) grayscale(0.3); + } + } + + &.arrowvine, + &.plant-arrowvine:hover { + background-image: url('~src/assets/images/world-map/fh/overlays/fh-24-garden-arrowvine.png'); + } + + &.axenut, + &.plant-axenut:hover { + background-image: url('~src/assets/images/world-map/fh/overlays/fh-24-garden-axenut.png'); + } + + &.corpsecap, + &.plant-corpsecap:hover { + background-image: url('~src/assets/images/world-map/fh/overlays/fh-24-garden-corpsecap.png'); + } + + &.flamefruit, + &.plant-flamefruit:hover { + background-image: url('~src/assets/images/world-map/fh/overlays/fh-24-garden-flamefruit.png'); + } + + &.rockroot, + &.plant-rockroot:hover { + background-image: url('~src/assets/images/world-map/fh/overlays/fh-24-garden-rockroot.png'); + } + + &.snowthistle, + &.plant-snowthistle:hover { + background-image: url('~src/assets/images/world-map/fh/overlays/fh-24-garden-snowthistle.png'); + } + + &.harvest { + background-size: contain; + } + + &.plot-1 { + top: calc(var(--ghs-unit) * 14.3 * var(--ghs-dialog-factor)); + left: calc(var(--ghs-unit) * 12.5 * var(--ghs-dialog-factor)); + } + + &.plot-2 { + top: calc(var(--ghs-unit) * 7.2 * var(--ghs-dialog-factor)); + left: calc(var(--ghs-unit) * 5.5 * var(--ghs-dialog-factor)); + } + + &.plot-3 { + top: calc(var(--ghs-unit) * 3.6 * var(--ghs-dialog-factor)); + left: calc(var(--ghs-unit) * 10.5 * var(--ghs-dialog-factor)); + } + } + } + +} \ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/garden/garden.ts b/src/app/ui/figures/party/buildings/garden/garden.ts new file mode 100644 index 000000000..8a4066711 --- /dev/null +++ b/src/app/ui/figures/party/buildings/garden/garden.ts @@ -0,0 +1,93 @@ +import { Component } from "@angular/core"; +import { gameManager } from "src/app/game/businesslogic/GameManager"; +import { GardenModel } from "src/app/game/model/Building"; +import { Character } from "src/app/game/model/Character"; +import { herbResourceLootTypes, LootType } from "src/app/game/model/data/Loot"; + +@Component({ + + selector: 'ghs-garden', + templateUrl: 'garden.html', + styleUrls: ['./garden.scss'], +}) +export class GardenComponent { + + level: number = 0; + slots: number = 0; + garden!: GardenModel; + herbs: LootType[] = herbResourceLootTypes; + activeHerb: LootType | undefined; + disabled: boolean = false; + resources: Partial> = {}; + + constructor() { + const garden = gameManager.game.party.buildings.find((value) => value.name == 'garden' && value.level); + if (garden) { + this.level = garden.level; + switch (this.level) { + case 1: + this.slots = 1; + break; + case 2: + case 3: + this.slots = 2; + break; + case 4: + this.slots = 3; + break; + } + } + this.garden = gameManager.game.party.garden || new GardenModel(); + this.disabled = gameManager.game.scenario != undefined; + + this.herbs.forEach((type) => { + this.resources[type] = (gameManager.game.party.loot[type] || 0); + gameManager.game.figures.forEach((figure) => { + if (figure instanceof Character) { + this.resources[type] = (this.resources[type] || 0) + (figure.progress.loot[type] || 0); + } + }) + + if (!this.activeHerb && this.resources[type]) { + this.activeHerb = type; + } + }) + } + + setActive(herb: LootType, force: boolean = false) { + if (this.activeHerb == herb || this.resources[herb] || force) { + if (this.activeHerb == herb) { + this.activeHerb = undefined; + } else { + this.activeHerb = herb; + } + } + } + + plantHerb(slot: number, force: boolean = false) { + if (this.activeHerb && (!this.disabled || force) && slot >= 0 && slot < this.slots && this.herbs.indexOf(this.activeHerb) != -1 && this.garden.plots[slot] != this.activeHerb && (this.level > 2 || !this.garden.flipped)) { + gameManager.stateManager.before('buildings.garden.plant', this.activeHerb, slot); + this.garden.plots = this.garden.plots || []; + this.garden.plots[slot] = this.activeHerb; + gameManager.game.party.garden = Object.assign(new GardenModel(), this.garden); + gameManager.stateManager.after(); + } + } + + flipGarden(force: boolean = false) { + if ((!this.disabled || force) && this.level && this.level < 3) { + gameManager.stateManager.before('buildings.garden.' + (this.garden.flipped ? 'flipPlant' : 'flipHarvest')); + this.garden.flipped = !this.garden.flipped; + gameManager.game.party.garden = Object.assign(new GardenModel(), this.garden); + gameManager.stateManager.after(); + } + } + + toggleAutomation() { + gameManager.stateManager.before('buildings.garden.' + (this.garden.automated ? 'automationOff' : 'automationOn')); + this.garden.automated = !this.garden.automated; + gameManager.game.party.garden = Object.assign(new GardenModel(), this.garden); + gameManager.stateManager.after(); + } + +} \ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.html b/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.html new file mode 100644 index 000000000..2723417e3 --- /dev/null +++ b/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.html @@ -0,0 +1,36 @@ +
+ +
+
+ +
+ {{petCard.id}} +
+
+ +
+ +
+
+ : {{name}}
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ 0{{petCard.cardId}} +
+
+
+
+
\ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.scss b/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.scss new file mode 100644 index 000000000..1eb7b7487 --- /dev/null +++ b/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.scss @@ -0,0 +1,160 @@ +.pet-container { + position: relative; + width: 100%; + padding-bottom: 147%; +} + +.pet { + position: absolute; + width: 100%; + height: 100%; + + .front { + background-image: url('~src/assets/images/fh/pets/pets-card-front.png'); + background-size: 100% 100%; + background-position: center center; + background-repeat: no-repeat; + border-radius: 0.5em; + background-clip: padding-box; + + .artwork { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + mask-image: url('~src/assets/images/fh/pets/pets-card-front-mask.png'); + mask-size: 100% 100%; + mask-position: center center; + mask-repeat: no-repeat; + } + + .title { + position: absolute; + top: 4%; + left: 0; + width: 100%; + height: 10%; + display: flex; + color: var(--ghs-color-white); + justify-content: center; + align-items: center; + font-family: ghs-title-fh; + font-size: 1.3em; + filter: var(--ghs-filter-outline-thin); + } + + .name-container { + position: absolute; + top: 14%; + left: 5%; + width: 95%; + height: 8%; + color: var(--ghs-color-white); + font-size: 0.8em; + font-family: ghs-normal-fh; + filter: var(--ghs-filter-outline-thin); + display: flex; + align-items: center; + + .name { + font-family: ghs-extra-fh; + font-size: 1.05em; + margin-left: 3%; + flex-grow: 1; + } + } + + .action { + position: absolute; + top: 74%; + left: 5%; + width: 75%; + height: 26%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + font-family: ghs-normal-fh; + font-size: 1.25em; + line-height: 0.8em; + } + + + .round, + .lost { + position: absolute; + top: 72%; + left: 80%; + width: 20%; + height: 13%; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + font-size: 1.8em; + + &.lost { + top: 83%; + } + } + + .card-id { + position: absolute; + bottom: 0; + left: 78%; + width: 15%; + height: 6%; + display: flex; + justify-content: flex-end; + align-items: center; + font-family: ghs-normal; + font-size: 0.65em; + } + + } + + .back { + background-image: url('~src/assets/images/fh/pets/pets-card-back.png'); + background-size: 100% 100%; + background-position: center center; + background-repeat: no-repeat; + border-radius: 0.5em; + background-clip: padding-box; + + .id { + position: absolute; + top: 0; + left: 40%; + width: 20%; + height: 7%; + display: flex; + justify-content: center; + align-items: center; + font-family: ghs-title-fh; + font-size: 1.2em; + } + } + + + &.confirm { + .revealed { + position: relative; + top: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + color: var(--ghs-color-white); + font-size: 1.5em; + filter: drop-shadow(calc(var(--ghs-unit) * 0.2) calc(var(--ghs-unit) * 0.2) calc(var(--ghs-unit) * 0.2) var(--ghs-color-black)); + + .text { + text-align: center; + } + } + } +} \ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.ts b/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.ts new file mode 100644 index 000000000..7272d060b --- /dev/null +++ b/src/app/ui/figures/party/buildings/stables/pet-card/pet-card.ts @@ -0,0 +1,37 @@ +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core"; +import { GameManager, gameManager } from "src/app/game/businesslogic/GameManager"; +import { SettingsManager, settingsManager } from "src/app/game/businesslogic/SettingsManager"; +import { PetCard } from "src/app/game/model/data/PetCard"; + +@Component({ + selector: 'ghs-pet-card', + templateUrl: './pet-card.html', + styleUrls: ['./pet-card.scss'] +}) +export class PetCardComponent implements OnInit, AfterViewInit { + + @ViewChild('container') containerElement!: ElementRef; + @Input() petCard!: PetCard | undefined; + @Input() flipped: boolean = false; + @Input() reveal: boolean = false; + @Input() name: string = ""; + @Output() revealed = new EventEmitter(); + + settingsManager: SettingsManager = settingsManager; + gameManager: GameManager = gameManager; + fontsize: string = "1em"; + + ngOnInit(): void { + gameManager.uiChange.subscribe({ + next: () => { + this.fontsize = (this.containerElement.nativeElement.offsetWidth * 0.072) + 'px';; + } + }) + } + + ngAfterViewInit(): void { + setTimeout(() => { + this.fontsize = (this.containerElement.nativeElement.offsetWidth * 0.072) + 'px'; + }, 1); + } +} \ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/stables/stables.html b/src/app/ui/figures/party/buildings/stables/stables.html new file mode 100644 index 000000000..0fb205cdf --- /dev/null +++ b/src/app/ui/figures/party/buildings/stables/stables.html @@ -0,0 +1,40 @@ +
+ + + +
+
+ +
+ + + + + + + + +
+
+
\ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/stables/stables.scss b/src/app/ui/figures/party/buildings/stables/stables.scss new file mode 100644 index 000000000..9a11c0065 --- /dev/null +++ b/src/app/ui/figures/party/buildings/stables/stables.scss @@ -0,0 +1,140 @@ +.stables-container { + display: flex; + flex-direction: column; + color: var(--ghs-color-white); + + .menu { + display: flex; + flex-wrap: wrap; + font-family: var(--ghs-font-title); + font-size: calc(var(--ghs-unit) * 3 * var(--ghs-dialog-factor)); + justify-content: center; + align-items: center; + z-index: 1; + + .capacity { + margin: calc(var(--ghs-unit) * 0.5 * var(--ghs-dialog-factor)); + + .used.full { + color: var(--ghs-color-yellow); + } + + .used.warning { + color: var(--ghs-color-red); + } + } + + label { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + color: var(--ghs-color-white); + margin: calc(var(--ghs-unit) * 0.5 * var(--ghs-dialog-factor)); + } + } + + .pet-list { + position: relative; + display: flex; + flex-wrap: wrap; + justify-content: center; + margin: calc(var(--ghs-unit) * 2 * var(--ghs-dialog-factor)); + min-height: calc(var(--ghs-unit) * 40.5 * var(--ghs-dialog-factor)); + + .empty { + display: flex; + justify-content: center; + align-items: center; + font-family: var(--ghs-font-text); + font-size: calc(var(--ghs-unit) * 3.5 * var(--ghs-dialog-factor)); + color: var(--ghs-color-yellow); + } + + .pet { + position: relative; + margin: calc(var(--ghs-unit) * 0.5 * var(--ghs-dialog-factor)); + width: calc(var(--ghs-unit) * 27 * var(--ghs-dialog-factor)); + height: calc(var(--ghs-unit) * 40.5 * var(--ghs-dialog-factor)); + + ghs-pet-card { + position: absolute; + top: 0; + left: 0; + width: 100%; + border: calc(var(--ghs-unit) * 0.35 * var(--ghs-dialog-factor)) solid transparent; + border-radius: calc(var(--ghs-unit) * 1 * var(--ghs-dialog-factor)); + overflow: hidden; + + &.unavailable { + cursor: initial; + border-color: var(--ghs-color-darkgray); + filter: grayscale(0.7); + } + + &.disabled { + cursor: not-allowed; + opacity: 0.5; + } + + &.edit { + cursor: initial; + } + + &.selectable { + cursor: pointer; + } + + &.selected { + border-color: var(--ghs-color-green); + opacity: 1; + } + } + + input.name { + position: absolute; + top: calc(var(--ghs-unit) * 6.2 * var(--ghs-dialog-factor)); + left: calc(var(--ghs-unit) * 5.35 * var(--ghs-dialog-factor)); + width: calc(var(--ghs-unit) * 21 * var(--ghs-dialog-factor)); + height: calc(var(--ghs-unit) * 2.3 * var(--ghs-dialog-factor)); + color: var(--ghs-color-white); + font-family: ghs-extra-fh; + filter: var(--ghs-filter-outline-thin); + font-size: calc(var(--ghs-unit) * 1.6 * var(--ghs-dialog-factor)); + } + + .button-remove { + z-index: 1; + position: absolute; + display: flex; + cursor: pointer; + top: calc(var(--ghs-unit) * 1 * var(--ghs-dialog-factor)); + right: calc(var(--ghs-unit) * 1 * var(--ghs-dialog-factor)); + justify-content: center; + align-items: center; + width: calc(var(--ghs-unit) * 2.5 * var(--ghs-dialog-factor)); + height: calc(var(--ghs-unit) * 2.5 * var(--ghs-dialog-factor)); + color: var(--ghs-filter-white) var(--ghs-filter-shadow); + + .ghs-svg { + filter: var(--ghs-filter-white) var(--ghs-filter-shadow); + } + + &:hover .ghs-svg { + filter: var(--ghs-filter-red) var(--ghs-filter-shadow); + } + } + + .button-lost { + z-index: 1; + position: absolute; + display: block; + cursor: pointer; + bottom: calc(var(--ghs-unit) * 3.5 * var(--ghs-dialog-factor)); + right: calc(var(--ghs-unit) * 1.2 * var(--ghs-dialog-factor)); + width: calc(var(--ghs-unit) * 3.5 * var(--ghs-dialog-factor)); + height: calc(var(--ghs-unit) * 3.5 * var(--ghs-dialog-factor)); + } + } + } +} \ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/stables/stables.ts b/src/app/ui/figures/party/buildings/stables/stables.ts new file mode 100644 index 000000000..0e53fe047 --- /dev/null +++ b/src/app/ui/figures/party/buildings/stables/stables.ts @@ -0,0 +1,160 @@ +import { Component, OnInit } from "@angular/core"; +import { gameManager } from "src/app/game/businesslogic/GameManager"; +import { PetCard, PetIdentifier } from "src/app/game/model/data/PetCard"; + +@Component({ + + selector: 'ghs-stables', + templateUrl: 'stables.html', + styleUrls: ['./stables.scss'], +}) +export class StablesComponent implements OnInit { + + pets: { card: PetCard | undefined, model: PetIdentifier | undefined }[] = []; + + edit: boolean = false; + select: boolean = false; + running: boolean = false; + showAll: boolean = false; + + capacity: number = 0; + used: number = 0; + active: number = 0; + + ngOnInit() { + this.update(); + gameManager.uiChange.subscribe({ + next: () => { + this.updateState(); + } + }) + } + + update() { + this.select = gameManager.game.scenario != undefined && gameManager.roundManager.firstRound; + this.running = gameManager.game.scenario != undefined && !this.select; + this.used = 0; + this.pets = []; + const stables = gameManager.game.party.buildings.find((model) => model.name == 'stables' && model.level); + + if (stables) { + this.active = stables.level < 3 ? 1 : 2; + this.capacity = 4 + Math.floor(stables.level / 2) * 4; + } + + if (this.showAll) { + const editionData = gameManager.editionData.find((editionData) => editionData.edition == gameManager.currentEdition()); + if (editionData && editionData.pets) { + editionData.pets.forEach((petCard) => { + const model = gameManager.game.party.pets.find((value) => value.edition == petCard.edition && value.name == petCard.id); + if (model) { + this.used++; + } + this.pets.push({ card: petCard, model: model }); + }) + } + } + + gameManager.game.party.pets.forEach((value) => { + if (this.pets.find((pet) => pet.model && pet.model.edition && value.edition && pet.model.name == value.name) == undefined) { + const editionData = gameManager.editionData.find((editionData) => editionData.edition == value.edition); + if (editionData && editionData.pets) { + const petCard = editionData.pets.find((petCard) => petCard.edition == value.edition && petCard.id == value.name); + this.pets.push({ card: petCard, model: value }); + } else { + this.pets.push({ card: undefined, model: value }); + } + this.used++; + } + }) + + this.pets.sort((a, b) => { + if (a.model && !b.model) { + return -1; + } else if (!a.model && b.model) { + return 1; + } else if (a.model && b.model && a.model.active != b.model.active) { + return a.model.active ? -1 : 1; + } else if (a.card && b.card) { + if (a.card.id < b.card.id) { + return -1; + } else { + return 1; + } + } else if (a.model && b.model) { + if (a.model.name < b.model.name) { + return -1; + } else { + return 1; + } + } + return 0; + }) + } + + updateState() { + gameManager.game.party.pets.forEach((value) => { + const pet = this.pets.find((pet) => pet.model && pet.model.edition && value.edition && pet.model.name == value.name); + if (pet && pet.model) { + pet.model.active = value.active; + pet.model.lost = value.lost; + pet.model.petname = value.petname; + } + }) + } + + addPet(pet: { card: PetCard | undefined, model: PetIdentifier | undefined }) { + if (pet.card && gameManager.game.party.pets.find((value) => pet.card && value.edition == pet.card.edition && value.name == pet.card.id) == undefined) { + const model = new PetIdentifier(pet.card.id, pet.card.edition); + gameManager.stateManager.before('buildings.stables.pets.add', model.edition, model.name); + gameManager.game.party.pets.push(model); + pet.model = model; + this.used++; + gameManager.stateManager.after(); + } + } + + updatePet(event: any, model: PetIdentifier | undefined) { + if (model && event.target) { + const name = event.target.value; + gameManager.stateManager.before(name ? 'buildings.stables.pets.setName' : 'buildings.stables.pets.unsetName', model.edition, model.name, name); + model.petname = name; + gameManager.stateManager.after(); + } + } + + removePet(model: PetIdentifier | undefined) { + if (model) { + gameManager.stateManager.before('buildings.stables.pets.remove', model.edition, model.name); + gameManager.game.party.pets = gameManager.game.party.pets.filter((value) => value.edition != model.edition || value.name != model.name); + this.update(); + gameManager.stateManager.after(); + } + } + + toggleLost(model: PetIdentifier | undefined, force: boolean = false) { + if (model && (!model.lost || force)) { + gameManager.stateManager.before(model.lost ? 'buildings.stables.pets.restore' : 'buildings.stables.pets.play', model.edition, model.name); + model.lost = !model.lost; + gameManager.stateManager.after(); + } + } + + + toggleActive(model: PetIdentifier | undefined, force: boolean = false) { + if (model && !this.edit && (this.select || force)) { + gameManager.stateManager.before(model.active ? 'buildings.stables.pets.setInactive' : 'buildings.stables.pets.setActive', model.edition, model.name, model.petname); + model.active = !model.active; + const stables = gameManager.game.party.buildings.find((model) => model.name == 'stables' && model.level); + while (!force && stables && gameManager.game.party.pets.filter((value) => value.active).length > (stables.level < 3 ? 1 : 2)) { + const other = gameManager.game.party.pets.find((value) => value.active && value != model); + if (other) { + other.active = false; + } + } + + gameManager.stateManager.after(); + } + } + +} \ No newline at end of file diff --git a/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.html b/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.html index ff38df5ba..8b3782129 100644 --- a/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.html +++ b/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.html @@ -29,7 +29,7 @@
- + diff --git a/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.ts b/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.ts index 1e3b264d1..2fec73ef3 100644 --- a/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.ts +++ b/src/app/ui/figures/party/buildings/upgrade-dialog/upgrade-dialog.ts @@ -32,8 +32,8 @@ export class BuildingUpgradeDialog implements OnInit { force: boolean; characters: Character[] = []; characterSpent: BuildingCosts[] = []; - fhSupportSpent: BuildingCosts = { "gold": 0, "hide": 0, "lumber": 0, "metal": 0, "prosperity": 0 }; - spent: BuildingCosts = { "gold": 0, "hide": 0, "lumber": 0, "metal": 0, "prosperity": 0 }; + fhSupportSpent: BuildingCosts = { "gold": 0, "hide": 0, "lumber": 0, "metal": 0, "prosperity": 0, "manual": 0 }; + spent: BuildingCosts = { "gold": 0, "hide": 0, "lumber": 0, "metal": 0, "prosperity": 0, "manual": 0 }; rewards: BuildingRewards | undefined; rewardsOnly: boolean; discount: boolean; @@ -47,7 +47,7 @@ export class BuildingUpgradeDialog implements OnInit { this.rewardsOnly = this.action == 'rewards'; this.discount = gameManager.game.party.buildings.find((buildingModel) => buildingModel.name == "carpenter" && buildingModel.level > 0 && buildingModel.state != 'wrecked') != undefined && !this.repair; if (!this.repair && this.building) { - this.costs = data.costs || { gold: 0, hide: 0, lumber: 0, metal: 0, prosperity: 0 }; + this.costs = data.costs || { gold: 0, hide: 0, lumber: 0, metal: 0, prosperity: 0, manual: 0 }; this.costs.gold = this.costs.gold || 0; this.costs.hide = this.costs.hide || 0; this.costs.lumber = this.costs.lumber || 0; @@ -93,11 +93,11 @@ export class BuildingUpgradeDialog implements OnInit { } } else { - this.costs = { "gold": data.costs && data.costs.gold || 0, "hide": this.repair, "lumber": this.repair, "metal": this.repair, "prosperity": 0 } + this.costs = { "gold": data.costs && data.costs.gold || 0, "hide": this.repair, "lumber": this.repair, "metal": this.repair, "prosperity": 0, "manual": 0 } } this.characters = gameManager.game.figures.filter((figure) => figure instanceof Character).map((figure) => figure as Character); this.characters.forEach((character, index) => { - this.characterSpent[index] = { "gold": 0, "hide": 0, "lumber": 0, "metal": 0, "prosperity": 0 }; + this.characterSpent[index] = { "gold": 0, "hide": 0, "lumber": 0, "metal": 0, "prosperity": 0, "manual": 0 }; }) } diff --git a/src/app/ui/figures/party/party-sheet-dialog.ts b/src/app/ui/figures/party/party-sheet-dialog.ts index 4db9d05f1..82565eb89 100644 --- a/src/app/ui/figures/party/party-sheet-dialog.ts +++ b/src/app/ui/figures/party/party-sheet-dialog.ts @@ -225,13 +225,13 @@ export class PartySheetDialogComponent implements OnInit, OnDestroy { } changePlayer(event: any, index: number) { - gameManager.stateManager.before("setPlayer", event.target.value, '' + (index + 1)); + gameManager.stateManager.before("setPlayer", event.target.value, (index + 1)); this.party.players[index] = event.target.value; gameManager.stateManager.after(); } removePlayer(index: number) { - gameManager.stateManager.before("removePlayer", this.party.players[index], '' + (index + 1)); + gameManager.stateManager.before("removePlayer", this.party.players[index], (index + 1)); this.party.players.splice(index, 1); gameManager.stateManager.after(); } @@ -800,7 +800,7 @@ export class PartySheetDialogComponent implements OnInit, OnDestroy { removeItem(item: ItemData) { const identifier = this.party.unlockedItems.find((identifier) => identifier.name == '' + item.id && identifier.edition == item.edition); if (identifier) { - gameManager.stateManager.before("removeUnlockedItem", item.edition, '' + item.id, item.name); + gameManager.stateManager.before("removeUnlockedItem", item.edition, item.id, item.name); this.party.unlockedItems.splice(this.party.unlockedItems.indexOf(identifier), 1); gameManager.stateManager.after(); this.update(); @@ -816,7 +816,7 @@ export class PartySheetDialogComponent implements OnInit, OnDestroy { } incItemCount(item: ItemData, itemIdentifier: CountIdentifier) { - gameManager.stateManager.before("updateUnlockedItemCount", item.edition, '' + item.id, item.name); + gameManager.stateManager.before("updateUnlockedItemCount", item.edition, item.id, item.name); if (itemIdentifier.count < 0) { itemIdentifier.count = 1; } else { @@ -975,7 +975,7 @@ export class PartySheetDialogComponent implements OnInit, OnDestroy { } removeConclusion(section: string, edition: string) { - gameManager.stateManager.before("removeConclusion", gameManager.game.party.name, section + ''); + gameManager.stateManager.before("removeConclusion", gameManager.game.party.name, section); gameManager.game.party.conclusions = gameManager.game.party.conclusions.filter((conclusion) => conclusion.edition != edition || conclusion.index != section); // TODO: remove week gameManager.stateManager.after(); @@ -1067,7 +1067,7 @@ export class PartySheetDialogComponent implements OnInit, OnDestroy { if (!isNaN(+event.target.value)) { const value = +event.target.value - this.moraleDefense; if (this.party.defense != value) { - gameManager.stateManager.before("setPartyTotalDefense", this.party.name, '' + value); + gameManager.stateManager.before("setPartyTotalDefense", this.party.name, value); this.party.defense = value; gameManager.stateManager.after(); } diff --git a/src/app/ui/figures/party/requirements/requirements.html b/src/app/ui/figures/party/requirements/requirements.html index 4af380a0f..b3102b071 100644 --- a/src/app/ui/figures/party/requirements/requirements.html +++ b/src/app/ui/figures/party/requirements/requirements.html @@ -11,7 +11,7 @@
+ [ghs-label-args]="[achievement.name, (achievement.required - achievement.count) , achievement.required]">
@@ -25,7 +25,7 @@
+ [ghs-label-args]="[achievement.name, (achievement.required - achievement.count), achievement.required]">
@@ -39,7 +39,7 @@
+ [ghs-label-args]="[achievement.name, (achievement.required - achievement.count), achievement.required]">
@@ -53,7 +53,7 @@
+ [ghs-label-args]="[achievement.name, achievement.level]">
- - @@ -64,16 +64,16 @@ - - @@ -82,24 +82,24 @@ @@ -107,37 +107,37 @@ - - - - - - diff --git a/src/app/ui/figures/party/week-dialog/week-dialog.ts b/src/app/ui/figures/party/week-dialog/week-dialog.ts index febf93d34..9428415fc 100644 --- a/src/app/ui/figures/party/week-dialog/week-dialog.ts +++ b/src/app/ui/figures/party/week-dialog/week-dialog.ts @@ -98,7 +98,7 @@ export class PartyWeekDialogComponent { } sectionElement.classList.add('error'); if (gameManager.game.party.weekSections[this.week]?.indexOf(sectionElement.value) == -1) { - gameManager.stateManager.before("addPartyWeekSection", gameManager.game.party.name, this.week + '', sectionElement.value + ''); + gameManager.stateManager.before("addPartyWeekSection", gameManager.game.party.name, this.week, sectionElement.value); gameManager.game.party.weekSections[this.week]?.push(sectionElement.value); sectionElement.classList.remove('error'); sectionElement.value = ""; @@ -108,7 +108,7 @@ export class PartyWeekDialogComponent { removeSection(section: string) { if (gameManager.game.party.weekSections[this.week]?.indexOf(section) != -1) { - gameManager.stateManager.before("removePartyWeekSection", gameManager.game.party.name, this.week + '', section + ''); + gameManager.stateManager.before("removePartyWeekSection", gameManager.game.party.name, this.week, section); gameManager.game.party.weekSections[this.week]?.splice(gameManager.game.party.weekSections[this.week]?.indexOf(section) || -1, 1); if (gameManager.game.party.weekSections[this.week]?.length == 0) { delete gameManager.game.party.weekSections[this.week]; diff --git a/src/app/ui/figures/party/world-map/world-map.ts b/src/app/ui/figures/party/world-map/world-map.ts index c016a42a8..6aad52a24 100644 --- a/src/app/ui/figures/party/world-map/world-map.ts +++ b/src/app/ui/figures/party/world-map/world-map.ts @@ -244,7 +244,7 @@ export class WorldMapComponent implements AfterViewInit { if (buildingData.coordinates && buildingData.coordinates.length) { const overlayData = buildingData.coordinates[level || 0]; if (overlayData) { - const imageName = overlayData.image || buildingData.edition + '-' + (buildingData.id ? buildingData.id + '-' : '') + buildingData.name + (buildingData.upgrades.length || buildingData.manualUpgrades ? '-' + (level != undefined ? level : '0') : ''); + const imageName = overlayData.image || buildingData.edition + '-' + (buildingData.id ? buildingData.id + '-' : '') + buildingData.name + (buildingData.upgrades.length ? '-' + (level != undefined ? level : '0') : ''); const overlayBuilding: ImageOverlay = this.placeOverlay('./assets/images/world-map/' + buildingData.edition + '/buildings/' + imageName + '.png', overlayData, height, -1); overlayBuilding.addTo(this.map); const element = overlayBuilding.getElement(); diff --git a/src/app/ui/footer/level/level-dialog.html b/src/app/ui/footer/level/level-dialog.html index b3b66600a..4f354b600 100644 --- a/src/app/ui/footer/level/level-dialog.html +++ b/src/app/ui/footer/level/level-dialog.html @@ -51,7 +51,10 @@ : x{{loot}} - +  
: diff --git a/src/app/ui/footer/level/level-dialog.ts b/src/app/ui/footer/level/level-dialog.ts index 8fe8e6286..d2602fc13 100644 --- a/src/app/ui/footer/level/level-dialog.ts +++ b/src/app/ui/footer/level/level-dialog.ts @@ -58,7 +58,7 @@ export class LevelDialogComponent implements OnInit, OnDestroy { } setBbMonsterDifficutly(level: number) { - gameManager.stateManager.before("updateLevelAdjustmentBb", '' + level); + gameManager.stateManager.before("updateLevelAdjustmentBb", level); gameManager.game.levelAdjustment = level - 2; const editionData = gameManager.editionData.find((editionData) => editionData.edition == 'bb' && editionData.monsterAmTables && editionData.monsterAmTables.length); if (editionData) { @@ -104,7 +104,7 @@ export class LevelDialogComponent implements OnInit, OnDestroy { } setPlayerCount(playerCount: number) { - gameManager.stateManager.before("updateManualPlayerCount", '' + playerCount); + gameManager.stateManager.before("updateManualPlayerCount", playerCount); gameManager.game.playerCount = playerCount; if (gameManager.game.levelCalculation) { gameManager.levelManager.calculateScenarioLevel(); @@ -130,6 +130,10 @@ export class LevelDialogComponent implements OnInit, OnDestroy { this.experience = gameManager.levelManager.experience(); this.loot = gameManager.levelManager.loot(); this.hazardousTerrain = gameManager.levelManager.terrain(); + + if (gameManager.trialsManager.activeFavor('fh', 'wealth')) { + this.loot += gameManager.trialsManager.activeFavor('fh', 'wealth'); + } } } diff --git a/src/app/ui/footer/level/level.ts b/src/app/ui/footer/level/level.ts index bfcfcf80e..148591dac 100644 --- a/src/app/ui/footer/level/level.ts +++ b/src/app/ui/footer/level/level.ts @@ -65,6 +65,10 @@ export class LevelComponent implements OnInit, OnDestroy { this.loot = gameManager.levelManager.loot(); this.hazardousTerrain = gameManager.levelManager.terrain(); this.monsterDifficulty = gameManager.levelManager.bbMonsterDifficutly(); + + if (gameManager.trialsManager.activeFavor('fh', 'wealth')) { + this.loot += gameManager.trialsManager.activeFavor('fh', 'wealth'); + } } } \ No newline at end of file diff --git a/src/app/ui/footer/scenario-rules/scenario-rule.html b/src/app/ui/footer/scenario-rules/scenario-rule.html index ac31a6fb8..31d1653c2 100644 --- a/src/app/ui/footer/scenario-rules/scenario-rule.html +++ b/src/app/ui/footer/scenario-rules/scenario-rule.html @@ -56,7 +56,7 @@ + [ghs-label-args]="[figureNames(figureRule), figureRule.value || '', (settingsManager.settings.calculate && (figureRule.type == 'damage' && figureRule.value && figureRule.value.indexOf('H') == -1 || (figureRule.type == 'setHp' || figureRule.type == 'heal') && figureRule.value && figureRule.value.indexOf('H') == -1)) ? EntityValueFunction(figureRule.value || '') : (figureRule.value || ''), (figureRule.type == 'amAdd' || figureRule.type == 'amRemove') ? figureRule.value.split(':')[0] : '', (figureRule.type == 'amAdd' || figureRule.type == 'amRemove' || figureRule.type == 'transfer' && figureRule.value && figureRule.value.split(':').length > 1) ? figureRule.value.split(':')[1] : '', figureRule.type == 'transferEntities' && figureRule.identifier && figureRule.identifier.number ? figureRule.identifier.number : '']"> @@ -66,12 +66,12 @@

+ [ghs-label-args]="[rule.randomDungeon.dungeonCount, randomDungeonsDungeonLabel().toString()]">


+ [ghs-label-args]="[rule.randomDungeon.monsterCount, randomDungeonsMonsterLabel(rule).toString()]">

diff --git a/src/app/ui/footer/scenario/dialog/scenario-dialog.ts b/src/app/ui/footer/scenario/dialog/scenario-dialog.ts index 53ca875b7..6716feb29 100644 --- a/src/app/ui/footer/scenario/dialog/scenario-dialog.ts +++ b/src/app/ui/footer/scenario/dialog/scenario-dialog.ts @@ -113,7 +113,7 @@ export class ScenarioDialogComponent { console.error("Could not find edition data!"); return; } - gameManager.stateManager.before(roomData.marker ? "openRoomMarker" : "openRoom", this.scenario.index, "data.scenario." + this.scenario.name, '' + roomData.ref, roomData.marker || ''); + gameManager.stateManager.before(roomData.marker ? "openRoomMarker" : "openRoom", this.scenario.index, "data.scenario." + this.scenario.name, roomData.ref, roomData.marker || ''); gameManager.scenarioManager.openRoom(roomData, this.scenario, false); gameManager.stateManager.after(); this.setupComponent && this.setupComponent.updateMonster(); diff --git a/src/app/ui/footer/scenario/scenario.ts b/src/app/ui/footer/scenario/scenario.ts index 2e0c9b353..8d4dcbe54 100644 --- a/src/app/ui/footer/scenario/scenario.ts +++ b/src/app/ui/footer/scenario/scenario.ts @@ -95,7 +95,7 @@ export class ScenarioComponent implements OnInit, OnDestroy { console.error("Could not find edition data!"); return; } - gameManager.stateManager.before(roomData.marker ? "openRoomMarker" : "openRoom", scenario.index, "data.scenario." + scenario.name, '' + roomData.ref, roomData.marker || ''); + gameManager.stateManager.before(roomData.marker ? "openRoomMarker" : "openRoom", scenario.index, "data.scenario." + scenario.name, roomData.ref, roomData.marker || ''); gameManager.scenarioManager.openRoom(roomData, scenario, false); gameManager.stateManager.after(); } diff --git a/src/app/ui/footer/scenario/summary/scenario-summary.html b/src/app/ui/footer/scenario/summary/scenario-summary.html index 75c0065d4..ad747e8c9 100644 --- a/src/app/ui/footer/scenario/summary/scenario-summary.html +++ b/src/app/ui/footer/scenario/summary/scenario-summary.html @@ -115,7 +115,8 @@ {{(success && rewards && rewards.gold ? rewards.gold : 0) + (collectiveGold[index] ? collectiveGold[index] : 0) + (character.loot * - gameManager.levelManager.loot()) | ghsValueSign}} + gameManager.levelManager.loot()) + (gameManager.trialsManager.favorsEnabled && gameManager.trialsManager.apply + ? character.loot *gameManager.trialsManager.activeFavor('fh', 'wealth') : 0) | ghsValueSign}} ({{character.progress.gold + (success && rewards && rewards.gold ? rewards.gold : 0)+ (collectiveGold[index] ? collectiveGold[index] : 0) + character.loot * gameManager.levelManager.loot() + (gameManager.trialsManager.favorsEnabled && gameManager.trialsManager.apply @@ -214,7 +215,7 @@ }}) + [ghs-label-args]="[character.level, (character.level + 1)]"> @@ -349,43 +350,43 @@ @@ -581,7 +582,7 @@ + [ghs-label-args]="[item.type, ''+ EntityValueFunction(item.value)]"> @@ -592,7 +593,7 @@ + [ghs-label-args]="[item.type, ''+ EntityValueFunction(item.value)]"> @@ -614,14 +615,14 @@
-
- @@ -711,7 +712,7 @@ @@ -753,6 +754,14 @@ [ghs-label-args]="[rewards.overlaySticker.name, rewards.overlaySticker.location]">
+
+ + +
+
@@ -781,7 +790,7 @@
+ [ghs-label-args]="[lootDeckCard]"> @@ -790,7 +799,7 @@
+ [ghs-label-args]="[lootDeckCard]"> @@ -799,7 +808,7 @@
+ [ghs-label-args]="[townGuardAm.value, townGuardAm.valueType]">
@@ -810,7 +819,7 @@
-
@@ -849,7 +858,7 @@
+ [ghs-label-args]="[(gameManager.game.party.weeks + 1)]">

+ [ghs-label-args]="[character.title || '%data.character.' + character.name + '%', ('' + treasures[treasureIndex]).startsWith('G') || +treasures[treasureIndex] >= 0 ? treasures[treasureIndex] : '\'???\'']">

diff --git a/src/app/ui/header/header.html b/src/app/ui/header/header.html index b150ff2e6..0e12de88f 100644 --- a/src/app/ui/header/header.html +++ b/src/app/ui/header/header.html @@ -72,12 +72,17 @@
-
+
- + +
+ +
((value.clockOut || new Date().getTime()) - value.clockIn) / 1000).reduce((a, b) => a + b, 0) : 0; diff --git a/src/app/ui/header/menu/campaign/campaign.html b/src/app/ui/header/menu/campaign/campaign.html index 12caf073b..8a552a02a 100644 --- a/src/app/ui/header/menu/campaign/campaign.html +++ b/src/app/ui/header/menu/campaign/campaign.html @@ -137,7 +137,7 @@ diff --git a/src/app/ui/header/menu/datamanagement/datamanagement.html b/src/app/ui/header/menu/datamanagement/datamanagement.html index da9a6ef40..e60cc8119 100644 --- a/src/app/ui/header/menu/datamanagement/datamanagement.html +++ b/src/app/ui/header/menu/datamanagement/datamanagement.html @@ -286,7 +286,7 @@
@@ -235,7 +238,8 @@
+ [disabled]="!gameManager.fhRules() || !gameManager.trialsManager.trialsEnabled && !gameManager.trialsManager.favorsEnabled" + [requires]="['fhTrials']" [labelSuffix]="gameManager.trialsManager.favorsAvailable ? 'favors' : ''">
diff --git a/src/app/ui/header/menu/undo/dialog.html b/src/app/ui/header/menu/undo/dialog.html index 24bf3f6bc..c351b84ab 100644 --- a/src/app/ui/header/menu/undo/dialog.html +++ b/src/app/ui/header/menu/undo/dialog.html @@ -30,7 +30,7 @@ - @@ -50,7 +50,7 @@ - +
@@ -59,13 +59,13 @@ - - { + this.change.emit(false); + }, settingsManager.settings.animations ? 1000 : 0) } else if (!this.clicked) { this.clicked = true; this.el.nativeElement.classList.add("confirm"); } else { this.el.nativeElement.classList.add("flipped"); - this.change.emit(true); + setTimeout(() => { + this.change.emit(true); + }, settingsManager.settings.animations ? 1000 : 0) this.clicked = false; this.el.nativeElement.classList.remove("confirm"); } diff --git a/src/app/ui/helper/label.ts b/src/app/ui/helper/label.ts index 4621c269b..2621379e8 100644 --- a/src/app/ui/helper/label.ts +++ b/src/app/ui/helper/label.ts @@ -298,7 +298,7 @@ export const applyValueCalc = function (value: string, relative: boolean): strin export class GhsLabelDirective implements OnInit, OnDestroy, OnChanges { @Input('ghs-label') value!: string | number; - @Input('ghs-label-args') args: string[] = []; + @Input('ghs-label-args') args: (string | number | boolean)[] = []; @Input('ghs-label-args-replace') argLabel: boolean = true; @Input('ghs-label-empty') empty: boolean = true; @Input('ghs-label-attribute') attribute: string = ""; @@ -353,7 +353,7 @@ export class GhsLabelDirective implements OnInit, OnDestroy, OnChanges { } apply(): void { - let args = this.args || []; + let args: string[] = this.args.map((arg) => '' + arg) || []; if (this.argLabel) { args = args.map((arg) => applyPlaceholder(settingsManager.getLabel(arg, [], false, this.empty), [], this.relative, this.style)); } diff --git a/src/app/ui/helper/tooltip/tooltip.ts b/src/app/ui/helper/tooltip/tooltip.ts index 281c5d18d..c1652f0ca 100644 --- a/src/app/ui/helper/tooltip/tooltip.ts +++ b/src/app/ui/helper/tooltip/tooltip.ts @@ -36,7 +36,7 @@ export class GhsTooltipComponent { export class GhsTooltipDirective implements OnInit, OnDestroy { @Input('ghs-tooltip') value = ''; - @Input('ghs-label-args') args: string[] = []; + @Input('ghs-label-args') args: (string | number | boolean)[] = []; @Input('ghs-label-args-replace') argLabel: boolean = true; @Input('style') style: 'gh' | 'fh' | false = false; @Input() relative: boolean = false; @@ -83,7 +83,7 @@ export class GhsTooltipDirective implements OnInit, OnDestroy { const tooltipRef: ComponentRef = this.overlayRef.attach(new ComponentPortal(GhsTooltipComponent)); tooltipRef.instance.value = this.value; - tooltipRef.instance.args = this.args; + tooltipRef.instance.args = this.args.map((arg) => '' + arg); tooltipRef.instance.argLabel = this.argLabel; tooltipRef.instance.style = this.style; tooltipRef.instance.relative = this.relative; diff --git a/src/app/ui/main.html b/src/app/ui/main.html index f8e26a43e..d81e9d9df 100644 --- a/src/app/ui/main.html +++ b/src/app/ui/main.html @@ -15,7 +15,7 @@ + [ghs-label-args]="[gameManager.stateManager.connectionTries]"> @@ -145,7 +145,7 @@

+ [ghs-label-args]="[gameManager.stateManager.backupError]">

diff --git a/src/assets/artwork/pets/fh-01.png b/src/assets/artwork/pets/fh-01.png new file mode 100644 index 000000000..07dfb889e Binary files /dev/null and b/src/assets/artwork/pets/fh-01.png differ diff --git a/src/assets/artwork/pets/fh-02.png b/src/assets/artwork/pets/fh-02.png new file mode 100644 index 000000000..ff6ce9c86 Binary files /dev/null and b/src/assets/artwork/pets/fh-02.png differ diff --git a/src/assets/artwork/pets/fh-03.png b/src/assets/artwork/pets/fh-03.png new file mode 100644 index 000000000..e2696d937 Binary files /dev/null and b/src/assets/artwork/pets/fh-03.png differ diff --git a/src/assets/artwork/pets/fh-04.png b/src/assets/artwork/pets/fh-04.png new file mode 100644 index 000000000..61fe840ea Binary files /dev/null and b/src/assets/artwork/pets/fh-04.png differ diff --git a/src/assets/artwork/pets/fh-05.png b/src/assets/artwork/pets/fh-05.png new file mode 100644 index 000000000..63391fe1b Binary files /dev/null and b/src/assets/artwork/pets/fh-05.png differ diff --git a/src/assets/artwork/pets/fh-06.png b/src/assets/artwork/pets/fh-06.png new file mode 100644 index 000000000..5a6a206d5 Binary files /dev/null and b/src/assets/artwork/pets/fh-06.png differ diff --git a/src/assets/artwork/pets/fh-07.png b/src/assets/artwork/pets/fh-07.png new file mode 100644 index 000000000..412c0d780 Binary files /dev/null and b/src/assets/artwork/pets/fh-07.png differ diff --git a/src/assets/artwork/pets/fh-08.png b/src/assets/artwork/pets/fh-08.png new file mode 100644 index 000000000..4155dabf7 Binary files /dev/null and b/src/assets/artwork/pets/fh-08.png differ diff --git a/src/assets/artwork/pets/fh-09.png b/src/assets/artwork/pets/fh-09.png new file mode 100644 index 000000000..b9cb266f9 Binary files /dev/null and b/src/assets/artwork/pets/fh-09.png differ diff --git a/src/assets/artwork/pets/fh-10.png b/src/assets/artwork/pets/fh-10.png new file mode 100644 index 000000000..2f0c824c5 Binary files /dev/null and b/src/assets/artwork/pets/fh-10.png differ diff --git a/src/assets/artwork/pets/fh-11.png b/src/assets/artwork/pets/fh-11.png new file mode 100644 index 000000000..4bc0ce9cc Binary files /dev/null and b/src/assets/artwork/pets/fh-11.png differ diff --git a/src/assets/artwork/pets/fh-12.png b/src/assets/artwork/pets/fh-12.png new file mode 100644 index 000000000..bd677b1cc Binary files /dev/null and b/src/assets/artwork/pets/fh-12.png differ diff --git a/src/assets/images/fh/buildings/garden.png b/src/assets/images/fh/buildings/garden.png new file mode 100644 index 000000000..44e9c0d9d Binary files /dev/null and b/src/assets/images/fh/buildings/garden.png differ diff --git a/src/assets/images/fh/pets/pets-card-back.png b/src/assets/images/fh/pets/pets-card-back.png new file mode 100644 index 000000000..518a9883c Binary files /dev/null and b/src/assets/images/fh/pets/pets-card-back.png differ diff --git a/src/assets/images/fh/pets/pets-card-front-mask.png b/src/assets/images/fh/pets/pets-card-front-mask.png new file mode 100644 index 000000000..2aa711540 Binary files /dev/null and b/src/assets/images/fh/pets/pets-card-front-mask.png differ diff --git a/src/assets/images/fh/pets/pets-card-front.png b/src/assets/images/fh/pets/pets-card-front.png new file mode 100644 index 000000000..3943dcfc7 Binary files /dev/null and b/src/assets/images/fh/pets/pets-card-front.png differ diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index a1e48b5a3..15eff2153 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -1496,6 +1496,7 @@ "overlaySticker": "Place map overlay sticker {0} on map in location {0} ({1}).", "partyAchievements": "Party Achievements", "perks": "Gain {0} perk each", + "pet": "Add %data.pets.{1}-{0}% (Pet {0}) to the Stables. This can exceed the capacity of the Stables.", "prosperity": "{0} prosperity", "reputation": "{0} reputation", "randomItems": "Each character gains one random item ({0}-{1})", @@ -2166,10 +2167,18 @@ ".": "Apply Challenges", "hint": "Automatically apply (if possible) active challenge cards." }, + "fhGarden": { + ".": "Support for Garden", + "hint": "Plant and Harvest Herbs (Building 24 Garden)" + }, "fhGhItems": { ".": "Add Gloomhaven Items", "hint": "Add Items from Gloomhaven and Forgotten Circles to available supply as stated out in the rules. (Only on enabled character sheets or character items)" }, + "fhPets": { + ".": "Support for Pets", + "hint": "Catch and manage Pets (Building 88 Stables)" + }, "fhRules": "Frosthaven Rules", "fhSecondEdition": { ".": "2nd Edition", @@ -2554,6 +2563,22 @@ } }, "buildBuilding": "Build '%data.buildings.{1}%' ({0})", + "buildings": { + "garden": {}, + "stables": { + "pets": { + "add": "Add pet %data.pets.{0}-{1}% to Stables", + "catch": "Catch pet from {1} standee {2} of {0}", + "remove": "Remove pet %data.pets.{0}-{1}% from Stables", + "restore": "Restore pet %data.pets.{0}-{1}% card", + "play": "Play pet %data.pets.{0}-{1}% card", + "setActive": "Take pet %data.pets.{0}-{1}% card to scenario", + "setInactive": "Remove pet %data.pets.{0}-{1}% card from scenario", + "setName": "Name pet %data.pets.{0}-{1}% '{2}'", + "unsetName": "Remove name from pet %data.pets.{0}-{1}%" + } + } + }, "buyItem": "Buy Item {1} (%data.edition.{2}%) for {0}", "cancelScenario": "Cancel Scenario #{0} {1} ({2})", "changeBuildingState": "State of building '%data.buildings.{1}%' ({0}) to %party.campaign.sheet.buildings.state.{2}%",