Skip to content

Commit

Permalink
Add the "High Tide" skill.
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 35e264e1abaa884c6904375a67349cd1f5ed1804
  • Loading branch information
cpojer committed Nov 1, 2024
1 parent 20d7865 commit 2a1aad1
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 61 deletions.
27 changes: 25 additions & 2 deletions apollo/actions/applyPower.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ import getAirUnitsToRecover from '@deities/athena/lib/getAirUnitsToRecover.tsx';
import matchesActiveType from '@deities/athena/lib/matchesActiveType.tsx';
import updatePlayer from '@deities/athena/lib/updatePlayer.tsx';
import updatePlayers from '@deities/athena/lib/updatePlayers.tsx';
import { Charge, HealAmount } from '@deities/athena/map/Configuration.tsx';
import {
Charge,
HealAmount,
MinSize,
} from '@deities/athena/map/Configuration.tsx';
import Player from '@deities/athena/map/Player.tsx';
import Unit, { UnitConversion } from '@deities/athena/map/Unit.tsx';
import Vector from '@deities/athena/map/Vector.tsx';
import MapData from '@deities/athena/MapData.tsx';
import MapData, { SizeVector } from '@deities/athena/MapData.tsx';
import { VisionT } from '@deities/athena/Vision.tsx';
import resizeMap from '../lib/resizeMap.tsx';

const conversions = new Map<Skill, Readonly<UnitConversion>>([
[Skill.SpawnUnitInfernoJetpack, { from: Flamethrower, to: InfernoJetpack }],
Expand Down Expand Up @@ -133,6 +138,24 @@ export default function applyPower(skill: Skill, map: MapData) {
const healTypes = getHealUnitTypes(skill);
let player = map.getCurrentPlayer();

if (skill === Skill.HighTide) {
map = resizeMap(
resizeMap(
map,
new SizeVector(
Math.max(MinSize, map.size.width - 1),
Math.max(MinSize, map.size.height - 1),
),
new Set(['left', 'top']),
),
new SizeVector(
Math.max(MinSize, map.size.width - 2),
Math.max(MinSize, map.size.height - 2),
),
new Set(),
);
}

if (skill === Skill.Charge) {
player = player.setCharge(player.charge + ChargeSkillCharges * Charge);
map = map.copy({
Expand Down
31 changes: 29 additions & 2 deletions apollo/lib/applyConditions.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import MapData from '@deities/athena/MapData.tsx';
import { Skill } from '@deities/athena/info/Skill.tsx';
import { MinSize } from '@deities/athena/map/Configuration.tsx';
import MapData, { SizeVector } from '@deities/athena/MapData.tsx';
import { ActionResponse } from '../ActionResponse.tsx';
import applyActionResponse from '../actions/applyActionResponse.tsx';
import { applyEffects, Effects } from '../Effects.tsx';
import { applyObjectives } from '../Objective.tsx';
import { GameState, GameStateWithEffects } from '../Types.tsx';
import getLosingPlayer from './getLosingPlayer.tsx';
import resizeEffects from './resizeEffects.tsx';

export default function applyConditions(
currentMap: MapData,
Expand All @@ -22,7 +25,31 @@ export default function applyConditions(

while (queue.length) {
const previousMap = currentMap;
const [actionResponse, currentEffects, _addToGameState] = queue.shift()!;
const [actionResponse, _currentEffects, _addToGameState] = queue.shift()!;
let currentEffects = _currentEffects;
if (
actionResponse.type === 'ActivatePower' &&
actionResponse.skill === Skill.HighTide
) {
effects = currentEffects = resizeEffects(
resizeEffects(
effects,
currentMap.size,
new SizeVector(
Math.max(MinSize, currentMap.size.width - 1),
Math.max(MinSize, currentMap.size.height - 1),
),
new Set(['left', 'top']),
),
currentMap.size,
new SizeVector(
Math.max(MinSize, currentMap.size.width - 2),
Math.max(MinSize, currentMap.size.height - 2),
),
new Set(),
);
}

const activeMap = applyActionResponse(
previousMap,
previousMap.createVisionObject(previousMap.currentPlayer),
Expand Down
31 changes: 31 additions & 0 deletions apollo/lib/resizeEffects.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { SizeVector } from '@deities/athena/MapData.tsx';
import { Effects } from '../Effects.tsx';
import { resizeEntities, ResizeOrigin } from './resizeMap.tsx';

export default function resizeEffects(
effects: Effects,
previousSize: SizeVector,
size: SizeVector,
origin: Set<ResizeOrigin>,
) {
const offsetX = origin.has('left') ? previousSize.width - size.width : 0;
const offsetY = origin.has('top') ? previousSize.height - size.height : 0;
return new Map(
[...effects].map(([trigger, effectList]) => [
trigger,
new Set(
[...effectList].map((effect) => ({
...effect,
actions: [...effect.actions].map((action) =>
action.type === 'SpawnEffect'
? ({
...action,
units: resizeEntities(action.units, size, offsetX, offsetY),
} as const)
: action,
),
})),
),
]),
);
}
57 changes: 14 additions & 43 deletions apollo/lib/resizeMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ import Vector from '@deities/athena/map/Vector.tsx';
import MapData, { SizeVector } from '@deities/athena/MapData.tsx';
import { objectiveHasVectors } from '@deities/athena/Objectives.tsx';
import ImmutableMap from '@nkzw/immutable-map';
import { Effects } from '../Effects.tsx';

export type ResizeOrigin = 'top' | 'right' | 'bottom' | 'left';

const updateEntities = <T extends Entity>(
export const resizeEntities = <T extends Entity>(
entities: ImmutableMap<Vector, T>,
size: SizeVector,
offsetX: number,
Expand All @@ -27,12 +26,11 @@ const updateEntities = <T extends Entity>(
.mapKeys((vector) =>
new SpriteVector(vector.x, vector.y).left(offsetX).up(offsetY),
)
.filter((_: unknown, vector: Vector): boolean => size.contains(vector))
.filter((_: unknown, vector: Vector) => size.contains(vector))
.mapKeys((vector) => vec(vector.x, vector.y));

export default function resizeMap(
map: MapData,
effects: Effects,
size: SizeVector,
origin: Set<ResizeOrigin>,
fill?: number,
Expand Down Expand Up @@ -63,14 +61,7 @@ export default function resizeMap(
Math.floor((subVector.y - 1) / DecoratorsPerSide) + 1,
),
)
? [
...decorators,
[subVector.x, subVector.y, decorator.id] as [
number,
number,
number,
],
]
? [...decorators, [subVector.x, subVector.y, decorator.id] as const]
: decorators;
},
[] as PlainEntitiesList<Decorator>,
Expand All @@ -92,36 +83,16 @@ export default function resizeMap(
: objective,
);

return [
verifyMap(
withModifiers(
map.copy({
buildings: updateEntities(map.buildings, size, offsetX, offsetY),
config: map.config.copy({ objectives }),
decorators: decodeDecorators(size, decorators),
map: tiles,
size,
units: updateEntities(map.units, size, offsetX, offsetY),
}),
),
return verifyMap(
withModifiers(
map.copy({
buildings: resizeEntities(map.buildings, size, offsetX, offsetY),
config: map.config.copy({ objectives }),
decorators: decodeDecorators(size, decorators),
map: tiles,
size,
units: resizeEntities(map.units, size, offsetX, offsetY),
}),
),
new Map(
[...effects].map(([trigger, effectList]) => [
trigger,
new Set(
[...effectList].map((effect) => ({
...effect,
actions: [...effect.actions].map((action) =>
action.type === 'SpawnEffect'
? ({
...action,
units: updateEntities(action.units, size, offsetX, offsetY),
} as const)
: action,
),
})),
),
]),
),
] as const;
);
}
10 changes: 10 additions & 0 deletions athena/info/Skill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export enum Skill {
Shield = 38,
Charge = 39,
DragonSaboteur = 40,
HighTide = 41,
}

export const Skills = new Set<Skill>([
Expand Down Expand Up @@ -92,6 +93,7 @@ export const Skills = new Set<Skill>([
Skill.RecoverAirUnits,
Skill.Shield,
Skill.Charge,
Skill.HighTide,
Skill.SpawnUnitInfernoJetpack,
Skill.AttackAndDefenseDecreaseEasy,
Skill.AttackAndDefenseIncreaseHard,
Expand Down Expand Up @@ -282,6 +284,13 @@ const skillConfig: Record<
cost: 1000,
group: SkillGroup.Special,
},
[Skill.HighTide]: {
activateOnInvasion,
charges: 1,
cost: 200,
group: SkillGroup.Invasion,
requiresCrystal,
},
};

export const CampaignOnlySkills = new Set(
Expand Down Expand Up @@ -1363,6 +1372,7 @@ export function shouldUpgradeUnit(unit: Unit, skill: Skill) {
case Skill.UnitRailDefenseIncreasePowerAttackIncrease:
case Skill.UnlockPowerStation:
case Skill.VampireHeal:
case Skill.HighTide:
return false;
default: {
skill satisfies never;
Expand Down
1 change: 1 addition & 0 deletions dionysus/lib/shouldActivatePower.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const shouldConsiderUnitRatio = (skill: Skill) => {
case Skill.BuyUnitBear:
case Skill.BuyUnitOctopus:
case Skill.DecreaseUnitCostAttackAndDefenseDecreaseMinor:
case Skill.HighTide:
case Skill.Shield:
return false;

Expand Down
35 changes: 30 additions & 5 deletions hera/behavior/activatePower/activatePowerAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ import {
import { HealEntry } from '@deities/athena/lib/getUnitsToHeal.tsx';
import matchesActiveType from '@deities/athena/lib/matchesActiveType.tsx';
import updatePlayer from '@deities/athena/lib/updatePlayer.tsx';
import { HealAmount, MaxHealth } from '@deities/athena/map/Configuration.tsx';
import {
FastAnimationConfig,
HealAmount,
MaxHealth,
} from '@deities/athena/map/Configuration.tsx';
import { PlayerID } from '@deities/athena/map/Player.tsx';
import vec from '@deities/athena/map/vec.tsx';
import Vector, {
sortByVectorKey,
sortVectors,
Expand All @@ -27,6 +32,7 @@ import animateHeal from '../../lib/animateHeal.tsx';
import AnimationKey from '../../lib/AnimationKey.tsx';
import damageUnits from '../../lib/damageUnits.tsx';
import getSkillConfigForDisplay from '../../lib/getSkillConfigForDisplay.tsx';
import sleep from '../../lib/sleep.tsx';
import spawn from '../../lib/spawn.tsx';
import upgradeUnits from '../../lib/upgradeUnits.tsx';
import { Actions, State, StateLike } from '../../Types.tsx';
Expand Down Expand Up @@ -56,7 +62,7 @@ export default async function activatePowerAction(
state: State,
actionResponse: ActivatePowerActionResponse,
): Promise<State> {
const { requestFrame, update } = actions;
const { requestFrame, scheduleTimer, scrollIntoView, update } = actions;
const { skill, units: unitsToSpawn } = actionResponse;
const { colors, name } = getSkillConfigForDisplay(skill);
const player = state.map.getCurrentPlayer();
Expand Down Expand Up @@ -175,9 +181,28 @@ export default async function activatePowerAction(
return fn(state);
}

requestFrame(() =>
resolve({ ...state, map: finalMap, ...resetBehavior() }),
);
requestFrame(async () => {
if (skill === Skill.HighTide) {
await scrollIntoView([
vec(
Math.floor(state.map.size.width / 2),
Math.floor(state.map.size.height / 2),
),
]);
await update({
animations: state.animations.set(new AnimationKey(), {
type: 'shake',
}),
});
await sleep(scheduleTimer, FastAnimationConfig, 'long');
await update({
animations: state.animations.set(new AnimationKey(), {
type: 'shake',
}),
});
}
resolve({ ...state, map: finalMap, ...resetBehavior() });
});
return null;
};

Expand Down
7 changes: 4 additions & 3 deletions hera/editor/MapEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
encodeEffects,
Scenario,
} from '@deities/apollo/Effects.tsx';
import resizeEffects from '@deities/apollo/lib/resizeEffects.tsx';
import resizeMap, { ResizeOrigin } from '@deities/apollo/lib/resizeMap.tsx';
import { Route } from '@deities/apollo/Routes.tsx';
import getCampaignRoute from '@deities/apollo/routes/getCampaignRoute.tsx';
Expand Down Expand Up @@ -791,13 +792,13 @@ export default function MapEditor({
(size: SizeVector, origin: Set<ResizeOrigin>) => {
const map = stateRef.current?.map;
if (map && !size.equals(map.size)) {
const [newMap, newEffects] = resizeMap(
map,
const newEffects = resizeEffects(
editor.effects,
map.size,
size,
origin,
editor?.selected?.tile,
);
const newMap = resizeMap(map, size, origin, editor?.selected?.tile);
setMap('resize', newMap);
setEditorState({
action: undefined,
Expand Down
3 changes: 2 additions & 1 deletion hera/lib/getSkillBasedPortrait.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,17 @@ export default function getSkillBasedPortrait(skill: Skill): UnitInfo | null {
case Skill.HealInfantryMedicPower:
case Skill.VampireHeal:
return Medic;
case Skill.Charge:
case Skill.ArtilleryRangeIncrease:
case Skill.AttackAndDefenseDecreaseEasy:
case Skill.AttackAndDefenseIncreaseHard:
case Skill.AttackIncreaseMajorDefenseDecreaseMajor:
case Skill.AttackIncreaseMinor:
case Skill.Charge:
case Skill.CounterAttackPower:
case Skill.DecreaseUnitCostAttackAndDefenseDecreaseMinor:
case Skill.DefenseIncreaseMinor:
case Skill.HealVehiclesAttackDecrease:
case Skill.HighTide:
case Skill.MovementIncreaseGroundUnitDefenseDecrease:
case Skill.NoUnitRestrictions:
case Skill.RecoverAirUnits:
Expand Down
11 changes: 10 additions & 1 deletion hera/lib/getSkillConfigForDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Paw from '@deities/ui/icons/Paw.tsx';
import Poison from '@deities/ui/icons/Poison.tsx';
import { SkillIconBorderStyle } from '@deities/ui/icons/SkillBorder.tsx';
import Skull from '@deities/ui/icons/Skull.tsx';
import Tide from '@deities/ui/icons/Tide.tsx';
import Track from '@deities/ui/icons/Track.tsx';
import Tree from '@deities/ui/icons/Tree.tsx';
import Zombie from '@deities/ui/icons/Zombie.tsx';
Expand Down Expand Up @@ -361,10 +362,18 @@ export default function getSkillConfigForDisplay(skill: Skill): SkillConfig {
return {
alpha: 0.1,
borderStyle: 'up2x',
colors: 'purple',
colors: ['purple', 'blue'],
icon: Glasses,
name: fbt('Sneaky Dragon', 'Skill name'),
};
case Skill.HighTide:
return {
alpha: 0.3,
borderStyle: 'down',
colors: ['blue', 'purple'],
icon: Tide,
name: fbt('High Tide', 'Skill name'),
};
default: {
skill satisfies never;
throw new UnknownTypeError('getSkillConfig', skill);
Expand Down
Loading

0 comments on commit 2a1aad1

Please sign in to comment.