召唤格子调整

master
chenanghui 3 days ago
parent 949ee3f394
commit ab4efb7146
  1. 2085
      assets/bundles/battleGame/prefab/BattleGame.prefab
  2. 20
      assets/bundles/first/config/Battlebuff.json
  3. BIN
      assets/res/battleGame/texture/ui/召唤.png
  4. 40
      assets/res/battleGame/texture/ui/召唤.png.meta
  5. 6
      assets/script/battle/bag/BattleBagSession.ts
  6. 68
      assets/script/battle/view/CocosBagBoardView.ts
  7. 29
      assets/script/battle/view/TextListLandHost.ts
  8. 2
      assets/script/ui/BattleGame/BattlePeachCoinFlyPresenter.ts
  9. 100
      assets/script/ui/BattleGame/UIBattleGame.ts

File diff suppressed because it is too large Load Diff

@ -345,7 +345,7 @@
"target": 1,
"targetpar": "1,1,13",
"functiontype": 102,
"functionpar": 50,
"functionpar": 5000,
"weight": 1,
"price": 0
},
@ -373,7 +373,7 @@
"target": 1,
"targetpar": "1,1,13",
"functiontype": 104,
"functionpar": 40,
"functionpar": 4000,
"weight": 1,
"price": 0
},
@ -513,7 +513,7 @@
"target": 2,
"targetpar": "1,1,13",
"functiontype": 102,
"functionpar": 50,
"functionpar": 5000,
"weight": 1,
"price": 0
},
@ -541,7 +541,7 @@
"target": 2,
"targetpar": "1,1,13",
"functiontype": 104,
"functionpar": 40,
"functionpar": 4000,
"weight": 1,
"price": 0
},
@ -681,7 +681,7 @@
"target": 3,
"targetpar": "1,1,13",
"functiontype": 102,
"functionpar": 50,
"functionpar": 5000,
"weight": 1,
"price": 0
},
@ -709,7 +709,7 @@
"target": 3,
"targetpar": "1,1,13",
"functiontype": 104,
"functionpar": 40,
"functionpar": 4000,
"weight": 1,
"price": 0
},
@ -849,7 +849,7 @@
"target": 4,
"targetpar": "1,1,13",
"functiontype": 102,
"functionpar": 50,
"functionpar": 5000,
"weight": 1,
"price": 0
},
@ -877,7 +877,7 @@
"target": 4,
"targetpar": "1,1,13",
"functiontype": 104,
"functionpar": 40,
"functionpar": 4000,
"weight": 1,
"price": 0
},
@ -1017,7 +1017,7 @@
"target": 5,
"targetpar": "1,1,13",
"functiontype": 102,
"functionpar": 50,
"functionpar": 5000,
"weight": 1,
"price": 0
},
@ -1045,7 +1045,7 @@
"target": 5,
"targetpar": "1,1,13",
"functiontype": 104,
"functionpar": 40,
"functionpar": 4000,
"weight": 1,
"price": 0
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 14 KiB

@ -45,10 +45,10 @@
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 58,
"height": 29,
"rawWidth": 58,
"rawHeight": 29,
"width": 208,
"height": 97,
"rawWidth": 208,
"rawHeight": 97,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
@ -60,17 +60,17 @@
"meshType": 0,
"vertices": {
"rawPosition": [
-29,
-14.5,
-104,
-48.5,
0,
29,
-14.5,
104,
-48.5,
0,
-29,
14.5,
-104,
48.5,
0,
29,
14.5,
104,
48.5,
0
],
"indexes": [
@ -83,12 +83,12 @@
],
"uv": [
0,
29,
58,
29,
97,
208,
97,
0,
0,
58,
208,
0
],
"nuv": [
@ -102,13 +102,13 @@
1
],
"minPos": [
-29,
-14.5,
-104,
-48.5,
0
],
"maxPos": [
29,
14.5,
104,
48.5,
0
]
},

@ -312,7 +312,7 @@ export class BattleBagSession {
/**
* 开局只给 tolockland(待解锁)填随机字。
* normalland 开启但留空;未解锁与 grid17 不填。不占刷新次数;玩家/AI 各自 bag 独立调用。
* normalland 开启但留空;未解锁不填。不占刷新次数;玩家/AI 各自 bag 独立调用。
*/
autoFillOpenSlots(): void {
if (!this.landStateOf) {
@ -851,7 +851,7 @@ export class BattleBagSession {
}
/**
* 地图字拖到空装饰格时删掉,不退回字池。
* 地图字拖到 ui_delete 时删掉,不退回字池。
* @param gridIndex 来源地图格
*/
discardMapGlyph(gridIndex: number): boolean {
@ -870,7 +870,7 @@ export class BattleBagSession {
}
/**
* 背包字拖到空装饰格时删掉,不退回字池。
* 背包字拖到 ui_delete 时删掉,不退回字池。
* @param slotIndex 来源槽
*/
discardBagSlot(slotIndex: number): boolean {

@ -135,6 +135,8 @@ export class CocosBagBoardView {
private _deploySync: DeploySync | null = null;
private _map: MapManager | null = null;
private _bagList: Node | null = null;
/** 拖字删除落点(BattleGame ui_delete) */
private _deleteNode: Node | null = null;
/** ui_textList 开启 / 待解锁 / 未解锁 */
private readonly _land = new TextListLandHost();
/** 开格展示字块补显重试帧计数(textGrid 预制体异步加载未就绪时留空格) */
@ -1250,6 +1252,14 @@ export class CocosBagBoardView {
this._otherBagAnchor = node?.isValid ? node : null;
}
/**
* 绑定拖字删除节点(BattleGame ui_delete)。
* @param node 删除落点;无效则不能拖字删除
*/
setDeleteNode(node: Node | null | undefined): void {
this._deleteNode = node?.isValid ? node : null;
}
bind(
battle: ProjectBattleHandle,
map: MapManager,
@ -1306,7 +1316,7 @@ export class CocosBagBoardView {
this._bindMapGridClicks();
this._warmTextGridPool();
// 开局只给 tolockland 放字;normalland 留空;grid17 不出字
// 开局只给 tolockland 放字;normalland 留空
this._bagSession.autoFillOpenSlots();
this._rebuildBagFromSession(true);
this._refreshDragEnabled();
@ -1431,6 +1441,7 @@ export class CocosBagBoardView {
this.disarmBagEntrance();
this.setInkstoneWorldPosProvider(null);
this.setOtherBagAnchor(null);
this.setDeleteNode(null);
this.setBagEntranceHandler(null, null);
this.setSkillPeachFlyHandler(null);
this._unbindMapGridClicks();
@ -3308,6 +3319,20 @@ export class CocosBagBoardView {
const fromSlot = drag?.source.kind === 'bag' ? drag.source.slotIndex : -1;
const fromMapIndex = this._mapDragFromIndex(drag);
if (this._isOverDelete(uiPos)) {
this._setDragSelectTarget(null);
const del = this._deleteNode;
if (lineFrom && del?.isValid) {
this._linePresenter.show(lineFrom, del.worldPosition);
} else {
this._linePresenter.hide();
}
if (drag && this._isType3Drag(grid, drag)) {
this._updateType3PathPreview(null, drag);
}
return;
}
const bagSlotIndex = this._resolveBagSlotAt(uiPos);
if (bagSlotIndex != null) {
const bagSwap = fromSlot >= 0 && bagSlotIndex !== fromSlot;
@ -3419,8 +3444,8 @@ export class CocosBagBoardView {
}
const target = this._resolveDropTarget(event, drag.source);
if (target.kind === 'bag' && this._land.isBlank(target.slotIndex)) {
this._discardGlyphToBlank(grid, drag);
if (this._isOverDelete(event.getUILocation())) {
this._discardGlyphToDelete(grid, drag);
return;
}
if (target.kind === 'bag' && this._interceptLandDrop(target.slotIndex)) {
@ -3510,8 +3535,8 @@ export class CocosBagBoardView {
}
const bagSlotIndex = this._resolveBagSlotAt(uiPos);
if (bagSlotIndex != null && this._land.isBlank(bagSlotIndex)) {
this._discardGlyphToBlank(grid, drag);
if (this._isOverDelete(uiPos)) {
this._discardGlyphToDelete(grid, drag);
return;
}
if (bagSlotIndex != null && this._interceptLandDrop(bagSlotIndex)) {
@ -4116,17 +4141,22 @@ export class CocosBagBoardView {
}
/**
* 背包或地图上的字拖到空装饰格:飞到 grid17 后消失,不留在格子上。
* 背包或地图上的字拖到 ui_delete:飞到该节点后消失,不留在格子上。
* @param grid 被拖的字
* @param drag 本次拖拽
*/
private _discardGlyphToBlank(grid: TextGrid, drag: NonNullable<typeof this._activeDrag>): void {
private _discardGlyphToDelete(grid: TextGrid, drag: NonNullable<typeof this._activeDrag>): void {
const session = this._bagSession;
const blank = this._blankLandNode();
const del = this._deleteNode;
const layer = this._dragLayer;
if (!session || !blank?.isValid || !layer?.isValid || !grid.node?.isValid) {
this._discardGlyphData(drag);
if (grid.node?.isValid) this._forceRecycleTextGrid(grid);
if (!del?.isValid || !del.activeInHierarchy) {
console.log('[CocosBagBoardView] ui_delete 不可用,取消删除');
this._lineDragCancel(grid, drag);
return;
}
if (!session || !layer?.isValid || !grid.node?.isValid) {
console.log('[CocosBagBoardView] 删除字块缺少拖拽层或会话');
this._lineDragCancel(grid, drag);
return;
}
const from: GridDetachSource = drag.source.kind === 'bag'
@ -4154,7 +4184,7 @@ export class CocosBagBoardView {
this._lineDragCancel(grid, drag);
return;
}
const target = blank.worldPosition.clone();
const target = del.worldPosition.clone();
BagToMapPlaceAnimator.play(grid, target, layer, () => {
this._finishDiscardFlight(grid);
}, { showTrail: DeviceProfile.enableBoardPlaceTrail });
@ -4176,7 +4206,7 @@ export class CocosBagBoardView {
}
/**
* 飞到空装饰格后回收字块,并补上推迟的盘面刷新。
* 飞到 ui_delete 后回收字块,并补上推迟的盘面刷新。
* @param grid 飞行中的字
*/
private _finishDiscardFlight(grid: TextGrid): void {
@ -4188,7 +4218,17 @@ export class CocosBagBoardView {
this._refreshCanUpgradeHints();
}
/** 空装饰格节点(grid17) */
/**
* 手指是否在 ui_delete 上。
* @param uiPos 屏幕坐标
*/
private _isOverDelete(uiPos: { x: number; y: number }): boolean {
const node = this._deleteNode;
if (!node?.isValid || !node.activeInHierarchy) return false;
return this._hitNode(node, uiPos);
}
/** 空装饰格节点。当前没有这种格子 */
private _blankLandNode(): Node | null {
const list = this._bagList;
if (!list?.isValid) return null;

@ -10,7 +10,7 @@ export enum TextListCellState {
OpenNoDrag = 2,
/** 未解锁:不可拖、不可落字 */
Locked = 3,
/** 空装饰格:不刷底、不出字、不可拖放 */
/** 空装饰格:不刷底、不出字、不可拖放。当前没有这种格子 */
Blank = 4,
}
@ -23,9 +23,6 @@ const GATE_NODE = '待开启';
/** 模板节点,不参与状态 */
const TEMP_NODE = 'temp';
/** 空装饰格下标(grid17):预制体底图保持关闭,逻辑不处理 */
const BLANK_INDEX = 17;
/** 一次待开启推进:新生待解锁格,以及本次要累加到这些格子数字上的等级 */
export interface TextListPromoteResult {
/** 新变成待开启、需要出字的格子 */
@ -40,7 +37,7 @@ export interface TextListPromoteResult {
* 待开启被合成后自身变为开启,上下左右的未解锁格变为待开启。其余为未解锁。
* 每局记录解锁次数,bind 时清零。第 1 次读 Const 39 的 index 0,依次类推;超出则用最后一项。
* 该加值累加到本次上下左右新待解锁格的数字等级上。
* grid17 是空装饰格,不刷底、不出字、不可拖放。名为 temp 的节点跳过。
* grid1-24 都是普通地块(含 grid17、grid23、grid24)。名为 temp 的节点跳过。
* 格子上的字块是背包槽位数据(BattleBagSession),由视图按槽位记录生成标准 TextGrid;
* 拖拽预览、合成解锁走既有背包管线,本类只负责格子底图状态与解锁规则。
*/
@ -109,8 +106,6 @@ export class TextListLandHost {
this._useTable = true;
this._fill(this._open, normal, 'normalland');
this._fill(this._gate, gate, 'tolockland');
this._stripBlank(this._open, 'normalland');
this._stripBlank(this._gate, 'tolockland');
for (let i = this._gate.length - 1; i >= 0; i--) {
if (!this._has(this._open, this._gate[i])) continue;
console.log('[TextListLand] 下标同时在开启与待解锁,按开启', this._gate[i]);
@ -131,7 +126,7 @@ export class TextListLandHost {
}
/**
* 节点对应的格子下标。命名 gridN → N(grid1-23),temp 跳过,其余名字打日志返回 null。
* 节点对应的格子下标。命名 gridN → N(grid1-24),temp 跳过,其余名字打日志返回 null。
* @param node ui_textList 子节点
*/
static indexOfNode(node: Node | null | undefined): number | null {
@ -163,11 +158,11 @@ export class TextListLandHost {
}
/**
* 是否空装饰格(grid17)。
* 是否空装饰格。当前没有这种格子,grid17 起都按普通地块。
* @param index 格子下标
*/
isBlank(index: number): boolean {
return index === BLANK_INDEX;
isBlank(_index: number): boolean {
return false;
}
/**
@ -347,18 +342,6 @@ export class TextListLandHost {
return null;
}
/**
* 配表若写了空装饰格,去掉并打日志。
* @param arr 下标数组
* @param label 字段名
*/
private _stripBlank(arr: number[], label: string): void {
const idx = arr.indexOf(BLANK_INDEX);
if (idx < 0) return;
console.log('[TextListLand] 空装饰格不参与地块配置', label, BLANK_INDEX);
arr.splice(idx, 1);
}
/** 数组是否包含 */
private _has(arr: readonly number[], value: number): boolean {
return arr.indexOf(value) >= 0;

@ -10,7 +10,7 @@ const FLY_STAGGER_SEC = 0.05;
/** 波次结算每枚金币的冒出间隔(秒) */
const WAVE_COIN_SPAWN_GAP_SEC = 0.15;
/** 波次结算金币起点相对屏幕中心的世界 Y 偏移 */
const WAVE_COIN_START_OFFSET_Y = 100;
const WAVE_COIN_START_OFFSET_Y = 150;
/** 波次金币冒出时的水平散开(世界像素) */
const WAVE_POP_SPREAD_X = 140;
/** 波次金币冒出时的垂直散开(世界像素) */

@ -4,7 +4,7 @@
// time: Tue Aug 18 2026
// desc: 战斗界面 — 地图、出怪、背包刷新(召唤)
//*********************
import { _decorator, find, game, Label, Node, Sprite, Tween, tween, v3, Vec3, sp } from 'cc';
import { _decorator, find, game, Label, Node, Tween, tween, v3, Vec3, sp } from 'cc';
import { Auto_BattleGame } from './Auto_BattleGame';
import { CocosBattleMapHost } from '../../battle/map/CocosBattleMapHost';
import { applyMapTopologyToSession } from '../../battle/map/importTopologyFromMapManager';
@ -64,10 +64,6 @@ import { WeaponComposeConfigProvider } from '../../battle/bag/WeaponComposeConfi
const { ccclass } = _decorator;
/** 召唤按钮 bg fillRange 变化 tween 时长(秒) */
const REFRESH_FILL_TWEEN_SEC = 0.2;
/** btnRefresh/bg2 fillRange 下限(负值,接近凑够时不再小于 -0.1) */
const REFRESH_FILL_RANGE_MIN = -0.1;
/** 召唤按钮进度满后 Q 弹第一下相对缩放增量 */
const REFRESH_BOUNCE_AMP = 0.1;
/** 单段弹起/回落时长(秒) */
@ -95,12 +91,6 @@ export class UIBattleGame extends Auto_BattleGame {
private _bagView: CocosBagBoardView | null = null;
/** 普攻/技能表现:监听 PresentationCue → HeroActor */
private _skillController: SkillController | null = null;
/** 召唤按钮填充进度(btnRefresh/bg2,水平从右到左) */
private _refreshFill: Sprite | null = null;
/** 召唤按钮 bg2 目标 fillRange(= -(1 - 桃子进度)) */
private _refreshFillTarget = 0;
/** 召唤按钮可刷新态(bg3) */
private _refreshBg3: Node | null = null;
/** 召唤按钮基准缩放 */
private readonly _refreshBtnScaleHome = new Vec3(1, 1, 1);
/** 本段「桃子足够」是否已播过满进度 Q 弹 */
@ -276,13 +266,9 @@ export class UIBattleGame extends Auto_BattleGame {
this._ensureOwnedDesc();
this._ensureOtherOwnedDesc();
this._linkSkillDescPeers();
this._refreshFill = this.btnRefresh?.getChildByName('bg2')?.getComponent(Sprite)
?? this.btnRefresh?.getChildByName('bg')?.getComponent(Sprite)
?? null;
this._refreshBg3 = this.btnRefresh?.getChildByName('bg3') ?? null;
this._showRefreshBg1();
this._disableRefreshBreathe();
this.btnRefresh?.getScale(this._refreshBtnScaleHome);
this._setupRefreshFillSprite();
this._lbWaveTip = this.ui_nextWaveTime?.getChildByName('lb1')?.getComponent(Label) ?? null;
this._lbWaveTipTime = this.ui_nextWaveTime?.getChildByName('lb2')?.getComponent(Label) ?? null;
if (!this.ui_nextWaveTime?.isValid) {
@ -711,6 +697,10 @@ export class UIBattleGame extends Auto_BattleGame {
rangeNode,
this.ui_textGrids,
);
if (!this.ui_delete?.isValid) {
console.log('[UIBattleGame] ui_delete 缺失,无法拖字删除');
}
this._bagView.setDeleteNode(this.ui_delete);
// bind() 开头会 unbind,入场回调必须在 bind 之后再挂
this._bagView.setOtherBagAnchor(this.ui_otherBag);
this._bagView.setInkstoneWorldPosProvider(this._inkstoneWorldPos);
@ -1074,27 +1064,8 @@ export class UIBattleGame extends Auto_BattleGame {
if (this.lb_RefreshNeed?.isValid) {
this.lb_RefreshNeed.string = `${cost}`;
}
this._showRefreshBg1();
const enough = cost <= 0 || gold >= cost;
if (this._refreshBg3?.isValid) {
this._refreshBg3.active = enough;
}
if (this._refreshFill?.isValid) {
const fillNode = this._refreshFill.node;
if (enough) {
if (fillNode.active) {
this._tweenRefreshFillRange(0, this._hideRefreshFill.bind(this));
} else {
this._stopRefreshFillTween();
this._refreshFillTarget = 0;
this._refreshFill.fillRange = 0;
}
} else {
fillNode.active = true;
const progress = cost > 0 ? Math.min(1, Math.max(0, gold / cost)) : 1;
const fillRange = Math.min(REFRESH_FILL_RANGE_MIN, -(1 - progress));
this._tweenRefreshFillRange(fillRange);
}
}
this._syncRefreshBounce(enough);
}
@ -1127,47 +1098,20 @@ export class UIBattleGame extends Auto_BattleGame {
if (node?.isValid && node.active !== active) node.active = active;
}
/** btnRefresh/bg2:水平从右到左;fillRange = min(-0.1, -(1 - 桃子/消耗)),凑够为 0 */
private _setupRefreshFillSprite(): void {
const sprite = this._refreshFill;
if (!sprite?.isValid) return;
sprite.type = Sprite.Type.FILLED;
sprite.fillType = Sprite.FillType.HORIZONTAL;
sprite.fillStart = 1;
sprite.fillRange = -1;
this._refreshFillTarget = -1;
}
/** btnRefresh/bg2:fillRange 0.2s tween 到目标(不足时为负,凑够为 0) */
private _tweenRefreshFillRange(target: number, onComplete?: () => void): void {
const sprite = this._refreshFill;
if (!sprite?.isValid) return;
if (Math.abs(target - this._refreshFillTarget) < 1e-4) {
onComplete?.();
return;
}
this._refreshFillTarget = target;
Tween.stopAllByTarget(sprite);
const tw = tween(sprite).to(REFRESH_FILL_TWEEN_SEC, { fillRange: target });
if (onComplete) {
tw.call(onComplete);
}
tw.start();
}
/** 桃子已够:fillRange 到 0 后隐藏 bg2 */
private _hideRefreshFill(): void {
const fillNode = this._refreshFill?.node;
if (fillNode?.isValid) {
fillNode.active = false;
}
}
/** 停止召唤按钮 bg fillRange tween */
private _stopRefreshFillTween(): void {
if (this._refreshFill?.isValid) {
Tween.stopAllByTarget(this._refreshFill);
/** btnRefresh 只显示 bg1,隐藏进度底 bg2 和可刷新底 bg3 */
private _showRefreshBg1(): void {
const btn = this.btnRefresh;
if (!btn?.isValid) return;
const bg1 = btn.getChildByName('bg1');
const bg2 = btn.getChildByName('bg2');
const bg3 = btn.getChildByName('bg3');
if (!bg1?.isValid) {
console.log('[UIBattleGame] btnRefresh 缺少 bg1');
}
this._setNodeActive(bg1, true);
if (bg2?.isValid) Tween.stopAllByTarget(bg2);
this._setNodeActive(bg2, false);
this._setNodeActive(bg3, false);
}
/** 关掉预制体上的 TweenBreathe,避免与 Q 弹抢缩放 */
@ -1368,10 +1312,6 @@ export class UIBattleGame extends Auto_BattleGame {
this._mapHost = null;
this._battle = null;
this._combatStarted = false;
this._stopRefreshFillTween();
this._refreshFill = null;
this._refreshBg3 = null;
this._refreshFillTarget = 0;
this._stopRefreshBounce();
this._refreshBounceHinted = false;
this._illustratedHintShownThisBattle = false;

Loading…
Cancel
Save