(hex: Hex)
| 753 | } |
| 754 | |
| 755 | scoreMoveHex(hex: Hex) { |
| 756 | const activeCreature = this.game.activeCreature; |
| 757 | if (!activeCreature) { |
| 758 | return Number.NEGATIVE_INFINITY; |
| 759 | } |
| 760 | |
| 761 | if (this.isRetreating(activeCreature)) { |
| 762 | return this.scoreRetreatHex(hex); |
| 763 | } |
| 764 | |
| 765 | const strategy = this.getStrategyFor(activeCreature); |
| 766 | if (strategy?.scoreMoveHex) { |
| 767 | const score = strategy.scoreMoveHex(hex, this); |
| 768 | if (score !== undefined) return score; |
| 769 | } |
| 770 | |
| 771 | const adjHexes = hex.adjacentHex(1); |
| 772 | const adjacentEnemyCreatures = new Map<number, Creature>(); |
| 773 | adjHexes.forEach((adjacentHex) => { |
| 774 | if ( |
| 775 | adjacentHex.creature instanceof Creature && |
| 776 | isTeam(activeCreature, adjacentHex.creature, Team.Enemy) |
| 777 | ) { |
| 778 | adjacentEnemyCreatures.set(adjacentHex.creature.id, adjacentHex.creature); |
| 779 | } |
| 780 | }); |
| 781 | const adjacentEnemyCount = adjacentEnemyCreatures.size; |
| 782 | |
| 783 | const aggression = this.getAggressionFactor(activeCreature); |
| 784 | |
| 785 | let score = 0; |
| 786 | // Adjacent-enemy bonus grows with aggression, pushing units to seek contact. |
| 787 | // First adjacent enemy gives the full bonus; each additional enemy beyond one |
| 788 | // is diminished — being flanked by 3 enemies is dangerous even for fighters. |
| 789 | if (adjacentEnemyCount >= 1) { |
| 790 | score += 120 + aggression * 25; |
| 791 | const extraEnemies = adjacentEnemyCount - 1; |
| 792 | const healthRatio = activeCreature.health / activeCreature.stats.health; |
| 793 | score -= extraEnemies * Math.round(80 + (1 - healthRatio) * 120); |
| 794 | } |
| 795 | |
| 796 | score -= activeCreature.hexagons.some( |
| 797 | (creatureHex) => creatureHex.pos.x === hex.x && creatureHex.pos.y === hex.y, |
| 798 | ) |
| 799 | ? 1000 |
| 800 | : 0; |
| 801 | |
| 802 | // Apply enemy-owned proximity penalties for adjacent hostile units. |
| 803 | adjacentEnemyCreatures.forEach((enemy) => { |
| 804 | const enemyStrategy = unitStrategies[enemy.type as string]; |
| 805 | score += enemyStrategy?.getProximityPenalty?.(activeCreature, enemy, hex, this) ?? 0; |
| 806 | }); |
| 807 | |
| 808 | // Zone preference weakens with aggression so units stop hugging safe ground. |
| 809 | const preferredX = this.getPreferredX(activeCreature); |
| 810 | const zoneWeight = Math.max(1, 10 - aggression); |
| 811 | score -= Math.abs(hex.x - preferredX) * zoneWeight; |
| 812 |
no test coverage detected