1
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
getEncounterCharacterBottomOffsetPx,
|
||||
getEncounterCharacterOpponentBottom,
|
||||
getHostileNpcSceneBottomOffsetPx,
|
||||
getMonsterWorldLeft,
|
||||
getMirroredStageEntityLeft,
|
||||
getNpcCombatHpTop,
|
||||
getSceneNpcVisualBottomOffsetPx,
|
||||
@@ -173,6 +174,59 @@ describe('GameCanvasEntityLayer', () => {
|
||||
expect(getNpcCombatHpTop(null, null)).toBe(CHARACTER_COMBAT_HP_TOP_PX);
|
||||
});
|
||||
|
||||
it('does not apply scene visual ground offset twice for custom character enemies in battle', () => {
|
||||
const hostileNpc = createHostileNpc({
|
||||
encounter: createEncounter({
|
||||
id: 'npc-shark-elder',
|
||||
npcName: '珊瑚长老',
|
||||
characterId: 'hero',
|
||||
imageSrc: '/generated-custom-world-npc/shark-elder.png',
|
||||
}),
|
||||
});
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
<GameCanvasEntityLayer
|
||||
companions={[]}
|
||||
sceneActAmbientEncounters={[]}
|
||||
currentScenePreset={null}
|
||||
sceneTransitionToken={0}
|
||||
isSceneTransitionEntering={false}
|
||||
isSceneTransitionExiting={false}
|
||||
transitionSweepPx={320}
|
||||
sceneTransitionExitDurationS={0.2}
|
||||
sceneTransitionEntryDurationS={0.2}
|
||||
companionAnchorLeft="10%"
|
||||
companionAnchorBottom="20%"
|
||||
playerBottomOffsetPx={0}
|
||||
sceneTransitionPhase="idle"
|
||||
inBattle={true}
|
||||
onEntitySelect={null}
|
||||
playerLeft="20%"
|
||||
playerCharacter={createCharacter()}
|
||||
playerHp={100}
|
||||
playerMaxHp={100}
|
||||
effectivePlayerFacing="right"
|
||||
effectivePlayerAnimationState={AnimationState.IDLE}
|
||||
shouldShowPlayerDialogueIcon={false}
|
||||
dialogueIndicator={null}
|
||||
npcAffinityEffect={null}
|
||||
sceneCombatants={[hostileNpc]}
|
||||
monsters={[]}
|
||||
getHostileNpcOuterLeft={() => '70%'}
|
||||
groundBottom="18%"
|
||||
stageLiftPx={68}
|
||||
encounter={null}
|
||||
sideAnchor="15%"
|
||||
cameraAnchorX={0}
|
||||
monsterAnchorMeters={3.2}
|
||||
playerX={0}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain('bottom:calc(calc(18% + 68px) + -78px)');
|
||||
expect(html).not.toContain('bottom:calc(calc(18% + 68px - 78px) + -78px)');
|
||||
});
|
||||
|
||||
it('renders affinity effect on the matching hostile npc', () => {
|
||||
const html = renderEntityLayer('npc-liu');
|
||||
|
||||
@@ -283,4 +337,160 @@ describe('GameCanvasEntityLayer', () => {
|
||||
expect(html).toContain('查看后排甲详情');
|
||||
expect(html).toContain('查看后排乙详情');
|
||||
});
|
||||
|
||||
it('keeps hostile combatant identity stable while attack position changes', () => {
|
||||
const sideAnchor = '15%';
|
||||
const cameraAnchorX = 0;
|
||||
const monsterAnchorMeters = 3.2;
|
||||
const attackingNpc = createHostileNpc({
|
||||
id: 'npc-attacker',
|
||||
xMeters: 0.1,
|
||||
animation: 'attack',
|
||||
combatMode: 'melee',
|
||||
encounter: createEncounter({
|
||||
id: 'npc-attacker',
|
||||
npcName: '突进敌人',
|
||||
}),
|
||||
});
|
||||
|
||||
const renderedLeft = getMonsterWorldLeft(
|
||||
sideAnchor,
|
||||
attackingNpc.xMeters,
|
||||
cameraAnchorX,
|
||||
monsterAnchorMeters,
|
||||
);
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
<GameCanvasEntityLayer
|
||||
companions={[]}
|
||||
sceneActAmbientEncounters={[]}
|
||||
currentScenePreset={null}
|
||||
sceneTransitionToken={0}
|
||||
isSceneTransitionEntering={false}
|
||||
isSceneTransitionExiting={false}
|
||||
transitionSweepPx={320}
|
||||
sceneTransitionExitDurationS={0.2}
|
||||
sceneTransitionEntryDurationS={0.2}
|
||||
companionAnchorLeft="10%"
|
||||
companionAnchorBottom="20%"
|
||||
playerBottomOffsetPx={0}
|
||||
sceneTransitionPhase="idle"
|
||||
inBattle={true}
|
||||
onEntitySelect={null}
|
||||
playerLeft="20%"
|
||||
playerCharacter={createCharacter()}
|
||||
playerHp={100}
|
||||
playerMaxHp={100}
|
||||
effectivePlayerFacing="right"
|
||||
effectivePlayerAnimationState={AnimationState.IDLE}
|
||||
shouldShowPlayerDialogueIcon={false}
|
||||
dialogueIndicator={null}
|
||||
npcAffinityEffect={null}
|
||||
sceneCombatants={[attackingNpc]}
|
||||
monsters={[]}
|
||||
getHostileNpcOuterLeft={(hostileNpc) =>
|
||||
getMonsterWorldLeft(
|
||||
sideAnchor,
|
||||
hostileNpc.xMeters,
|
||||
cameraAnchorX,
|
||||
monsterAnchorMeters,
|
||||
)
|
||||
}
|
||||
groundBottom="18%"
|
||||
stageLiftPx={68}
|
||||
encounter={null}
|
||||
sideAnchor={sideAnchor}
|
||||
cameraAnchorX={cameraAnchorX}
|
||||
monsterAnchorMeters={monsterAnchorMeters}
|
||||
playerX={0}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain(`left:${renderedLeft}`);
|
||||
});
|
||||
|
||||
it('keeps enemy formation positions when battle starts before any attack dash', () => {
|
||||
const sideAnchor = '15%';
|
||||
const cameraAnchorX = 0;
|
||||
const monsterAnchorMeters = 3.2;
|
||||
const frontNpc = createHostileNpc({
|
||||
id: 'npc-front',
|
||||
xMeters: 3.2,
|
||||
encounter: createEncounter({
|
||||
id: 'npc-front',
|
||||
npcName: '前排敌人',
|
||||
}),
|
||||
});
|
||||
const backNpc = createHostileNpc({
|
||||
id: 'npc-back',
|
||||
xMeters: 4.28,
|
||||
yOffset: 62,
|
||||
encounter: createEncounter({
|
||||
id: 'npc-back',
|
||||
npcName: '后排敌人',
|
||||
}),
|
||||
});
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
<GameCanvasEntityLayer
|
||||
companions={[]}
|
||||
sceneActAmbientEncounters={[]}
|
||||
currentScenePreset={null}
|
||||
sceneTransitionToken={0}
|
||||
isSceneTransitionEntering={false}
|
||||
isSceneTransitionExiting={false}
|
||||
transitionSweepPx={320}
|
||||
sceneTransitionExitDurationS={0.2}
|
||||
sceneTransitionEntryDurationS={0.2}
|
||||
companionAnchorLeft="10%"
|
||||
companionAnchorBottom="20%"
|
||||
playerBottomOffsetPx={0}
|
||||
sceneTransitionPhase="idle"
|
||||
inBattle={true}
|
||||
onEntitySelect={null}
|
||||
playerLeft="20%"
|
||||
playerCharacter={createCharacter()}
|
||||
playerHp={100}
|
||||
playerMaxHp={100}
|
||||
effectivePlayerFacing="right"
|
||||
effectivePlayerAnimationState={AnimationState.IDLE}
|
||||
shouldShowPlayerDialogueIcon={false}
|
||||
dialogueIndicator={null}
|
||||
npcAffinityEffect={null}
|
||||
sceneCombatants={[frontNpc, backNpc]}
|
||||
monsters={[]}
|
||||
getHostileNpcOuterLeft={(hostileNpc) =>
|
||||
getMonsterWorldLeft(
|
||||
sideAnchor,
|
||||
hostileNpc.xMeters,
|
||||
cameraAnchorX,
|
||||
monsterAnchorMeters,
|
||||
)
|
||||
}
|
||||
groundBottom="18%"
|
||||
stageLiftPx={68}
|
||||
encounter={null}
|
||||
sideAnchor={sideAnchor}
|
||||
cameraAnchorX={cameraAnchorX}
|
||||
monsterAnchorMeters={monsterAnchorMeters}
|
||||
playerX={0}
|
||||
/>,
|
||||
);
|
||||
|
||||
const frontLeft = `left:${getMonsterWorldLeft(
|
||||
sideAnchor,
|
||||
frontNpc.xMeters,
|
||||
cameraAnchorX,
|
||||
monsterAnchorMeters,
|
||||
)}`;
|
||||
const backLeft = `left:${getMonsterWorldLeft(
|
||||
sideAnchor,
|
||||
backNpc.xMeters,
|
||||
cameraAnchorX,
|
||||
monsterAnchorMeters,
|
||||
)}`;
|
||||
|
||||
expect(html).toContain(frontLeft);
|
||||
expect(html).toContain(backLeft);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user