优化0828-1

main
chenanghui 4 days ago
parent 756e09a89c
commit 0b7bf3c20e
  1. 452
      assets/bundles/first/config/Monster2.json
  2. 10
      assets/bundles/first/config/TarTask.json
  3. 1
      assets/script/GameLaunch.ts
  4. 18
      assets/script/game/BattleResLoader.ts
  5. 8
      assets/script/game/GameController.ts
  6. 20
      assets/script/interface/AutoTable.ts
  7. 8
      assets/script/tetriCard/tetriCardItem.ts
  8. 20
      assets/script/tetriCard/tetriFloorNode.ts
  9. 41
      assets/script/tetriCard/tetriNode.ts
  10. 19
      assets/script/tetriCard/tetriWeaponCarrier.ts
  11. 23
      assets/script/tetriMap/tetriMap.ts
  12. 2
      assets/script/ui/BattleGame/BattleCore.ts
  13. 10
      assets/script/ui/BattleGame/Monster.ts
  14. 19
      assets/script/ui/SkillSelect/SkillSelectItem.ts
  15. 273
      assets/script/ui/jiujiuwoyaGame/Aelement.ts
  16. 183
      assets/script/ui/jiujiuwoyaGame/LuosiDing.ts
  17. 115
      assets/script/ui/jiujiuwoyaGame/UIjiujiuwoyaGame.ts
  18. 14
      assets/script/ui/tetriGame/UItetriGame.ts
  19. 2
      bin/tt_bknsb1.0.1.bin
  20. BIN
      配置表/excelList/怪物表.xlsx
  21. BIN
      配置表/excelList/目标任务表.xlsx
  22. BIN
      配置表/策划配置/俄罗斯方块战斗武器12波.xlsx

@ -4,23 +4,23 @@
"attr0": 25,
"attr1": 18,
"attr2": 5,
"attr3": 11,
"attr4": 11,
"attr5": 11,
"attr6": 11,
"attr7": 11,
"attr8": 11,
"attr9": 11,
"attr10": 11,
"attr11": 11,
"attr12": 11,
"attr13": 11,
"attr14": 11,
"attr15": 11,
"attr16": 11,
"attr17": 11,
"attr18": 11,
"attr19": 11,
"attr3": 9,
"attr4": 9,
"attr5": 9,
"attr6": 9,
"attr7": 9,
"attr8": 9,
"attr9": 9,
"attr10": 9,
"attr11": 9,
"attr12": 9,
"attr13": 9,
"attr14": 9,
"attr15": 9,
"attr16": 9,
"attr17": 9,
"attr18": 9,
"attr19": 9,
"attr20": 11,
"attr21": 18,
"attr22": 18,
@ -37,25 +37,25 @@
"id": 2,
"attr0": 38,
"attr1": 44,
"attr2": 11,
"attr3": 26,
"attr4": 26,
"attr5": 27,
"attr6": 27,
"attr7": 27,
"attr8": 28,
"attr9": 28,
"attr10": 28,
"attr11": 29,
"attr12": 29,
"attr13": 29,
"attr14": 30,
"attr15": 30,
"attr16": 30,
"attr17": 31,
"attr18": 31,
"attr19": 32,
"attr20": 32,
"attr2": 20,
"attr3": 24,
"attr4": 24,
"attr5": 25,
"attr6": 25,
"attr7": 25,
"attr8": 26,
"attr9": 26,
"attr10": 26,
"attr11": 26,
"attr12": 27,
"attr13": 27,
"attr14": 27,
"attr15": 28,
"attr16": 28,
"attr17": 28,
"attr18": 29,
"attr19": 29,
"attr20": 34,
"attr21": 37,
"attr22": 37,
"attr23": 37,
@ -71,25 +71,25 @@
"id": 3,
"attr0": 94,
"attr1": 88,
"attr2": 35,
"attr3": 70,
"attr4": 71,
"attr5": 71,
"attr6": 72,
"attr7": 72,
"attr8": 73,
"attr9": 73,
"attr10": 74,
"attr11": 74,
"attr12": 75,
"attr13": 75,
"attr14": 76,
"attr15": 76,
"attr16": 77,
"attr17": 77,
"attr18": 78,
"attr19": 79,
"attr20": 79,
"attr2": 70,
"attr3": 76,
"attr4": 77,
"attr5": 77,
"attr6": 78,
"attr7": 78,
"attr8": 79,
"attr9": 79,
"attr10": 80,
"attr11": 81,
"attr12": 81,
"attr13": 82,
"attr14": 82,
"attr15": 83,
"attr16": 83,
"attr17": 84,
"attr18": 85,
"attr19": 85,
"attr20": 92,
"attr21": 63,
"attr22": 63,
"attr23": 63,
@ -105,25 +105,25 @@
"id": 4,
"attr0": 113,
"attr1": 58,
"attr2": 51,
"attr3": 47,
"attr4": 49,
"attr5": 50,
"attr6": 52,
"attr7": 54,
"attr8": 56,
"attr9": 58,
"attr10": 60,
"attr11": 62,
"attr12": 64,
"attr13": 67,
"attr14": 69,
"attr15": 71,
"attr16": 74,
"attr17": 77,
"attr18": 79,
"attr19": 82,
"attr20": 85,
"attr2": 102,
"attr3": 64,
"attr4": 66,
"attr5": 69,
"attr6": 71,
"attr7": 74,
"attr8": 76,
"attr9": 79,
"attr10": 82,
"attr11": 85,
"attr12": 88,
"attr13": 91,
"attr14": 94,
"attr15": 97,
"attr16": 101,
"attr17": 104,
"attr18": 108,
"attr19": 112,
"attr20": 92,
"attr21": 74,
"attr22": 74,
"attr23": 74,
@ -139,25 +139,25 @@
"id": 5,
"attr0": 225,
"attr1": 140,
"attr2": 61,
"attr3": 182,
"attr4": 184,
"attr5": 185,
"attr6": 187,
"attr7": 189,
"attr8": 191,
"attr9": 192,
"attr10": 194,
"attr11": 196,
"attr12": 198,
"attr13": 199,
"attr14": 201,
"attr15": 203,
"attr16": 205,
"attr17": 207,
"attr18": 209,
"attr19": 211,
"attr20": 228,
"attr2": 106,
"attr3": 228,
"attr4": 230,
"attr5": 232,
"attr6": 234,
"attr7": 237,
"attr8": 239,
"attr9": 241,
"attr10": 243,
"attr11": 245,
"attr12": 248,
"attr13": 250,
"attr14": 252,
"attr15": 255,
"attr16": 257,
"attr17": 259,
"attr18": 262,
"attr19": 264,
"attr20": 263,
"attr21": 53,
"attr22": 53,
"attr23": 53,
@ -173,25 +173,25 @@
"id": 6,
"attr0": 281,
"attr1": 198,
"attr2": 105,
"attr3": 261,
"attr4": 274,
"attr5": 288,
"attr6": 302,
"attr7": 317,
"attr8": 333,
"attr9": 350,
"attr10": 367,
"attr11": 386,
"attr12": 405,
"attr13": 425,
"attr14": 446,
"attr15": 469,
"attr16": 492,
"attr17": 517,
"attr18": 543,
"attr19": 570,
"attr20": 570,
"attr2": 175,
"attr3": 324,
"attr4": 340,
"attr5": 357,
"attr6": 375,
"attr7": 394,
"attr8": 414,
"attr9": 434,
"attr10": 456,
"attr11": 479,
"attr12": 503,
"attr13": 528,
"attr14": 554,
"attr15": 582,
"attr16": 611,
"attr17": 641,
"attr18": 674,
"attr19": 707,
"attr20": 658,
"attr21": 130,
"attr22": 130,
"attr23": 130,
@ -207,25 +207,25 @@
"id": 7,
"attr0": 375,
"attr1": 236,
"attr2": 162,
"attr3": 364,
"attr4": 393,
"attr5": 425,
"attr6": 459,
"attr7": 495,
"attr8": 535,
"attr9": 578,
"attr10": 624,
"attr11": 674,
"attr12": 728,
"attr13": 786,
"attr14": 849,
"attr15": 917,
"attr16": 990,
"attr17": 1069,
"attr18": 1155,
"attr19": 1247,
"attr20": 1425,
"attr2": 259,
"attr3": 389,
"attr4": 420,
"attr5": 454,
"attr6": 490,
"attr7": 529,
"attr8": 572,
"attr9": 617,
"attr10": 667,
"attr11": 720,
"attr12": 778,
"attr13": 840,
"attr14": 907,
"attr15": 980,
"attr16": 1058,
"attr17": 1143,
"attr18": 1234,
"attr19": 1333,
"attr20": 1644,
"attr21": 154,
"attr22": 154,
"attr23": 154,
@ -241,25 +241,25 @@
"id": 8,
"attr0": 450,
"attr1": 236,
"attr2": 274,
"attr3": 270,
"attr4": 317,
"attr5": 373,
"attr6": 438,
"attr7": 515,
"attr8": 605,
"attr9": 711,
"attr10": 835,
"attr11": 981,
"attr12": 1153,
"attr13": 1354,
"attr14": 1591,
"attr15": 1870,
"attr16": 2197,
"attr17": 2582,
"attr18": 3033,
"attr19": 3564,
"attr20": 4275,
"attr2": 434,
"attr3": 343,
"attr4": 394,
"attr5": 454,
"attr6": 522,
"attr7": 600,
"attr8": 690,
"attr9": 793,
"attr10": 912,
"attr11": 1049,
"attr12": 1207,
"attr13": 1388,
"attr14": 1596,
"attr15": 1835,
"attr16": 2110,
"attr17": 2427,
"attr18": 2791,
"attr19": 3210,
"attr20": 4931,
"attr21": 165,
"attr22": 165,
"attr23": 165,
@ -275,25 +275,25 @@
"id": 9,
"attr0": 656,
"attr1": 236,
"attr2": 438,
"attr3": 470,
"attr4": 564,
"attr5": 677,
"attr6": 812,
"attr7": 975,
"attr8": 1170,
"attr9": 1403,
"attr10": 1684,
"attr11": 2021,
"attr12": 2425,
"attr13": 2910,
"attr14": 3492,
"attr15": 4191,
"attr16": 5029,
"attr17": 6034,
"attr18": 7241,
"attr19": 8690,
"attr20": 12825,
"attr2": 595,
"attr3": 722,
"attr4": 866,
"attr5": 1040,
"attr6": 1248,
"attr7": 1497,
"attr8": 1797,
"attr9": 2156,
"attr10": 2587,
"attr11": 3104,
"attr12": 3725,
"attr13": 4470,
"attr14": 5365,
"attr15": 6437,
"attr16": 7725,
"attr17": 9270,
"attr18": 11124,
"attr19": 13349,
"attr20": 14794,
"attr21": 189,
"attr22": 189,
"attr23": 189,
@ -309,25 +309,25 @@
"id": 10,
"attr0": 938,
"attr1": 236,
"attr2": 629,
"attr3": 720,
"attr4": 900,
"attr5": 1125,
"attr6": 1406,
"attr7": 1758,
"attr8": 2197,
"attr9": 2747,
"attr10": 3433,
"attr11": 4292,
"attr12": 5364,
"attr13": 6706,
"attr14": 8382,
"attr15": 10477,
"attr16": 13097,
"attr17": 16371,
"attr18": 20464,
"attr19": 25580,
"attr20": 38475,
"attr2": 839,
"attr3": 1010,
"attr4": 1242,
"attr5": 1528,
"attr6": 1879,
"attr7": 2312,
"attr8": 2843,
"attr9": 3497,
"attr10": 4302,
"attr11": 5291,
"attr12": 6508,
"attr13": 8005,
"attr14": 9846,
"attr15": 12111,
"attr16": 14897,
"attr17": 18323,
"attr18": 22537,
"attr19": 27721,
"attr20": 44381,
"attr21": 118,
"attr22": 118,
"attr23": 118,
@ -343,25 +343,25 @@
"id": 11,
"attr0": 1296,
"attr1": 236,
"attr2": 629,
"attr3": 960,
"attr4": 1268,
"attr5": 1674,
"attr6": 2210,
"attr7": 2918,
"attr8": 3853,
"attr9": 5088,
"attr10": 6718,
"attr11": 8870,
"attr12": 11713,
"attr13": 15465,
"attr14": 20421,
"attr15": 26964,
"attr16": 35603,
"attr17": 47011,
"attr18": 62074,
"attr19": 81963,
"attr20": 115425,
"attr2": 839,
"attr3": 1319,
"attr4": 1649,
"attr5": 2061,
"attr6": 2576,
"attr7": 3220,
"attr8": 4025,
"attr9": 5032,
"attr10": 6289,
"attr11": 7862,
"attr12": 9827,
"attr13": 12284,
"attr14": 15355,
"attr15": 19194,
"attr16": 23992,
"attr17": 29991,
"attr18": 37488,
"attr19": 46860,
"attr20": 133144,
"attr21": 175,
"attr22": 184,
"attr23": 184,
@ -377,25 +377,25 @@
"id": 12,
"attr0": 1620,
"attr1": 236,
"attr2": 629,
"attr3": 1068,
"attr4": 1407,
"attr5": 1854,
"attr6": 2443,
"attr7": 3219,
"attr8": 4241,
"attr9": 5588,
"attr10": 7363,
"attr11": 9702,
"attr12": 12783,
"attr13": 16843,
"attr14": 22193,
"attr15": 29241,
"attr16": 38529,
"attr17": 50766,
"attr18": 66889,
"attr19": 88133,
"attr20": 112219,
"attr2": 839,
"attr3": 1334,
"attr4": 1668,
"attr5": 2084,
"attr6": 2605,
"attr7": 3257,
"attr8": 4071,
"attr9": 5089,
"attr10": 6361,
"attr11": 7951,
"attr12": 9939,
"attr13": 12424,
"attr14": 15530,
"attr15": 19412,
"attr16": 24265,
"attr17": 30332,
"attr18": 37915,
"attr19": 47393,
"attr20": 140273,
"attr21": 212,
"attr22": 221,
"attr23": 221,

@ -155,7 +155,7 @@
"par1": 0,
"par2": 1,
"counttype": 1,
"desc": "领取1次离线挂机收益",
"desc": "领取1次离线收益",
"rewards": "3,10",
"jumpto": 101
},
@ -185,7 +185,7 @@
"par1": 14,
"par2": 1,
"counttype": 0,
"desc": "换电磁炮上阵并参与一次战斗",
"desc": "换电磁炮参与1次战斗",
"rewards": "3,10",
"jumpto": 101
},
@ -325,7 +325,7 @@
"par1": 15,
"par2": 1,
"counttype": 1,
"desc": "使用冰冻手雷参与战斗1次",
"desc": "冰冻手雷参与战斗1次",
"rewards": "3,10",
"jumpto": 101
},
@ -355,7 +355,7 @@
"par1": 0,
"par2": 1,
"counttype": 1,
"desc": "领取1次离线挂机收益",
"desc": "领取1次离线收益",
"rewards": "3,10",
"jumpto": 101
},
@ -405,7 +405,7 @@
"par1": 10,
"par2": 1,
"counttype": 1,
"desc": "使用大炮参与一次战斗",
"desc": "换大炮参与1次战斗",
"rewards": "3,10",
"jumpto": 101
},

@ -58,6 +58,7 @@ export class GameLaunch extends Component {
GEvent.Ins.on(GEvent.SHOW_LOADING, this.showLoading, this);
GEvent.Ins.on(GEvent.HIDE_LOADING, this.hideLoading, this);
//PhysicsSystem2D.instance.debugDrawFlags = 1 << 0 | 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4 | 1 << 5 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 9 | 1 << 10 | 1 << 11 | 1 << 12 | 1 << 13 | 1 << 14 | 1 << 15;
// game.on(Game.EVENT_SHOW, this.onShow, this)
// game.on(Game.EVENT_HIDE, this.onHide, this)
}

@ -241,6 +241,24 @@ export class BattleResLoader {
]);
}
/** 救救我鸭:木板 / 螺丝(同步 getNode 生成,未预载则首帧空) */
static async preloadJiujiuwoyaCardAssets(): Promise<void> {
if (!gg?.res) return;
await this.ensureBundles(JIUJIUWOYA_BUNDLE_GROUP);
const tasks: Promise<unknown>[] = [
gg.res.ensurePrefab('螺丝', 'prefab/', GBundle.jiujiuwoyaCard),
gg.res.ensurePrefab('jiujiuwoyaGame', 'prefab/', GBundle.jiujiuwoya),
];
for (let i = 0; i < this.JIUJIUWOYA_MUBAN_PREFABS.length; i++) {
tasks.push(gg.res.ensurePrefab(this.JIUJIUWOYA_MUBAN_PREFABS[i], 'prefab/', GBundle.jiujiuwoyaCard));
}
await Promise.all(tasks);
}
private static readonly JIUJIUWOYA_MUBAN_PREFABS: string[] = [
'木板1_2', '木板2_2', '木板3_2', '木板4_2', '木板5_2', '木板6_1', '木板7_3', '木板8_2',
];
/** hitEffect: `spine名|爆炸预制体名` → 取预制体名 */
private static _parseHitEffectPrefabName(hitEffect: string | null | undefined): string {
if (!hitEffect || typeof hitEffect !== 'string') return '';

@ -536,10 +536,14 @@ export class GameController extends Component {
}
// 单个 weapon1 + hitEffect,耗时通常远小于地图/界面;await 可避免首枪空等
await Promise.all([
const preloadTasks: Promise<unknown>[] = [
BattleResLoader.preloadWeapon1Bullet(),
BattleResLoader.preloadBattlePopupAssets(),
]);
];
if (gg.game.CurentSelectBattleType === BattleType.OrangeWeaponMode_Ya) {
preloadTasks.push(BattleResLoader.preloadJiujiuwoyaCardAssets());
}
await Promise.all(preloadTasks);
await gg.ui.openPanel(panelId);
const duration = new Date().getTime() - sdkConfig.enterBattleTime;

@ -1853,6 +1853,16 @@ declare global {
@ -2614,6 +2624,16 @@ declare global {

@ -135,7 +135,13 @@ export class tetriCardItem extends Component {
/** 从卡牌模板实例化战场方块(与拖拽预览彻底分离) */
public createBattlePiece(): Node | null {
if (!this._pieceNode?.isValid) return null;
return instantiate(this._pieceNode);
const node = instantiate(this._pieceNode);
if (node?.isValid) {
for (const wc of node.getComponentsInChildren(tetriWeaponCarrier)) {
wc.resetWeaponForBattlePlace();
}
}
return node;
}
/** 递归销毁名为「角色」的子树(预览二号禁止挂角色/武器) */

@ -97,6 +97,26 @@ export class tetriFloorNode extends Component {
get isSettled(): boolean { return this._settled; }
public hasStackSupportStable(): boolean { return this._stackSupportStable; }
/** 落稳后补标稳定支撑(台子/稳定塔顶),避免无重力砖块未计入堆高 */
public tryPromoteStackSupportStable(): boolean {
if (this._stackSupportStable || !this._settled) return false;
if (this._isOverlappingSupportOnTaizi()) {
this._stackSupportStable = true;
const map = (gg as any)?.game?.CurentBattle?.TetraMap;
map?.invalidateStackTopCache?.();
map?._updateHighLine?.();
return true;
}
this._inheritStackSupportFromTableOverlap();
if (this._stackSupportStable) {
const map = (gg as any)?.game?.CurentBattle?.TetraMap;
map?.invalidateStackTopCache?.();
map?._updateHighLine?.();
return true;
}
return false;
}
/**是否正在脚本下落(用于 tetriMap 在 contact 时判定立即钉住) */
public get isSimFalling(): boolean { return this._simFallActive && !this._settled; }

@ -6,6 +6,7 @@ import { GBundle, GPath } from '../game/ConfigRes';
import { UILayer } from '../../mx/module/ui/UIManager';
import { TetriStackDiagnostics } from '../tetriMap/TetriStackDiagnostics';
import { BattlePerformance } from '../ui/BattleGame/BattlePerformance';
import { tetriFloorNode } from './tetriFloorNode';
import { isTetriBlockInShop, isTetriBlockOnTetrisTable, playTetriBlockSpine, TetriBlockSpineState } from './tetriBlockSpine';
const { ccclass, property } = _decorator;
@ -421,14 +422,48 @@ export class tetriNode extends Component {
}
}
private _inheritStackSupportFromContacts(): void {
if (this._stackSupportStable) return;
/**
* contact settle AABB 线/
*/
public tryPromoteStackSupportStable(): boolean {
if (this._stackSupportStable || !this._settled) return false;
for (const [partner, cnt] of this._touchingByContactCount) {
if (cnt > 0 && partner?.hasStackSupportStable()) {
this.markStackSupportStable();
return;
return true;
}
}
const map = gg.game?.CurentBattle?.TetraMap;
const table = map?._tetrisTable;
if (!table?.isValid) return false;
const selfCols = this._getAllColliders2D(this.node);
if (!selfCols.length) return false;
const pad = 1.5;
const minOv = 0.35;
for (const child of table.children) {
if (!child?.isValid || child === this.node) continue;
const t = child.getComponent(tetriNode) ?? child.getComponentInChildren(tetriNode);
if (t && t !== this && t.shouldCountTowardStackHeight()) {
const otherCols = t._getAllColliders2D(t.node);
if (otherCols.length && this._anyAabbPairIntersects(selfCols, otherCols, pad, minOv)) {
this.markStackSupportStable();
return true;
}
}
const f = child.getComponent(tetriFloorNode) ?? child.getComponentInChildren(tetriFloorNode);
if (f?.hasStackSupportStable()) {
const fCols = f.node.getComponentsInChildren(PolygonCollider2D) ?? [];
if (fCols.length && this._anyAabbPairIntersects(selfCols, fCols, pad, minOv)) {
this.markStackSupportStable();
return true;
}
}
}
return false;
}
private _inheritStackSupportFromContacts(): void {
this.tryPromoteStackSupportStable();
}

@ -88,6 +88,13 @@ export class tetriWeaponCarrier extends Component {
return this._config?.weapontype ?? 0;
}
/** 松手放置前:清卡牌模板拷贝上的武器引用,避免 instantiate 后重复挂载 */
public resetWeaponForBattlePlace(): void {
this.weaponNode = null;
this._weaponLoading = false;
this._weaponLoadSeq++;
}
/** 放置到战场:若 start 早于 setConfig 或武器未挂上,在此补初始化(武器仍挂方块「武器」节点) */
ensureBattleReady(): void {
if (!this._config || !this._isBattleScene) return;
@ -155,6 +162,13 @@ export class tetriWeaponCarrier extends Component {
GEvent.Ins.on(GEvent.StraightTargetDamage, this.StraightTargetDamage, this)//直线目标持续伤害
GEvent.Ins.on(GEvent.LightningElectricity, this.LightningElectricity, this)//雷电电击攻击单体
const table = gg.game?.CurentBattle?.TetraMap?._tetrisTable;
const onSceneTable = table?.isValid && this.node.parent === table;
if (onSceneTable) {
this.scheduleOnce(() => this.ensureBattleReady(), 0.02);
return;
}
if (this._isBattleScene) return;
this.scheduleOnce(() => this.initCarrier(), 0.02);
}
@ -323,7 +337,10 @@ export class tetriWeaponCarrier extends Component {
return;
}
this._syncWeaponNodeFromMount();
if (this.weaponNode?.isValid || this._weaponLoading) return;
if (this.weaponNode?.isValid) {
if (this.weaponNode.name === conf.weaponani) return;
}
if (this._weaponLoading) return;
this.addWeapon(conf.weaponani);
//this.changeIcon(conf.typeicon);
}

@ -1169,6 +1169,9 @@ export class tetriMap extends Component {
this._recalcAcc += dt;
if (this._recalcAcc >= this.recalcInterval) {
this._recalcAcc = 0;
if (this._reconcileStackSupportStable()) {
this._committedStackTopWorldYMax = Number.NaN;
}
this.invalidateStackTopCache();
this._updateHighLine();
}
@ -1297,6 +1300,9 @@ export class tetriMap extends Component {
this._physicsStabilizeScheduled = false;
this._resyncAllTetriRigidBodiesFromNodes();
this._syncGameCameraToUiCamera(true);
this._committedStackTopWorldYMax = Number.NaN;
this.invalidateStackTopCache();
this.refreshHighLineFromStack();
role.refreshFollowOffsetsAfterViewportChange();
}, 0);
}, 0);
@ -1861,10 +1867,21 @@ export class tetriMap extends Component {
this.invalidateStackTopCache();
}
private _resolveStackTopWorldYMax(groundWorldYMax: number): number {
if (!this._stackTopCacheDirty && this._cachedStackTopWorldYMax >= groundWorldYMax) {
return this._cachedStackTopWorldYMax;
/** 周期补标:已落稳但未计入堆高的块,避免白线/镜头偶发停住 */
private _reconcileStackSupportStable(): boolean {
if (!this._tetrisTable?.isValid) return false;
let changed = false;
for (const child of this._tetrisTable.children) {
if (!child?.isValid) continue;
const t = child.getComponent(tetriNode) ?? child.getComponentInChildren(tetriNode);
if (t?.tryPromoteStackSupportStable()) changed = true;
const f = child.getComponent(tetriFloorNode) ?? child.getComponentInChildren(tetriFloorNode);
if (f?.tryPromoteStackSupportStable()) changed = true;
}
return changed;
}
private _resolveStackTopWorldYMax(groundWorldYMax: number): number {
let stackTopWorldYMax = groundWorldYMax;
if (this._tetrisTable?.isValid) {
for (const child of this._tetrisTable.children) {

@ -382,6 +382,8 @@ export class BattleCore {
tetriSaveBoxNum = 0;
/**3选1弹出次数 宝箱打开次数 */
IsOpenSkillSelectTimes: number = 0;
/** 三选一选中负秒伤项后,下一次 DPS 刷新只改数字不飞字 */
skipNextDpsFlyAnim = false;
/**转盘“再抽一次”引导:单场战斗仅触发一次 */
IsTurnTableAgainGuideShown: boolean = false;

@ -107,6 +107,8 @@ export class Monster extends Component {
/**是不是保护期间, 保护期间怪物不会受到伤害*/
private _isProtecting: boolean = true;
/**保护时间 */
private _protectTime: number = 0;
isMonsterPlayGude = false
checkTag(tag: MonsterTag | string): boolean {
return this._tagStatus.get(tag) || false;
@ -188,9 +190,7 @@ export class Monster extends Component {
this.maxShield = 0;
this._isProtecting = true
let time = gg.data.table.getConst(ConstNumType.MonsterRefreshUnselectableTime);
this.scheduleOnce(() => {
this._isProtecting = false;
}, time);
this._protectTime = time;
/**保护期间,时间内变化*/
this.init();
}
@ -293,6 +293,10 @@ export class Monster extends Component {
}
protected update(dt: number): void {
this._protectTime -= dt;
if (this._protectTime <= 0) {
this._isProtecting = false;
}
if (!gg.game.isBattleContextActive()) return;
if (this.isMonsterPlayGude) {
// 引导怪只做演出攻击,不参与寻路移动,避免出现“乱跳/抖动”

@ -18,6 +18,7 @@ export class SkillSelectItem extends Component {
rtdesc: RichText = null;
newItem: Node = null;
rtTNum: Label = null;
rtbg: Node = null;
bg: Sprite = null;
sbg: Sprite = null;
@ -33,6 +34,7 @@ export class SkillSelectItem extends Component {
this.lbname = this.node.find("lbname").getComponent(Label);
this.rtdesc = this.node.find("rtdesc").getComponent(RichText);
this.newItem = this.node.find("newItem");
this.rtbg = this.newItem?.find("rtbg") ?? this.node.find("rtbg");
this.bg = this.node.find("bg").getComponent(Sprite);
this.sbg = this.node.find("sbg").getComponent(Sprite);
this.wbg = this.node.find("wbg").getComponent(Sprite);
@ -194,17 +196,24 @@ export class SkillSelectItem extends Component {
this.rtdesc.string = gg.lang.get(this._desc);
this.newItem.active = this._new;
const d = this._dpsDelta;
//转为汉字
const signStr = (d >= 0 ? '+' : '-') + MTools.formatChineseUnit({ num: Math.abs(d) });;
this.rtTNum.string = gg.lang.get(`秒伤{1}`, [signStr]);
const showDps = d >= 0;
if (this.rtbg?.isValid) this.rtbg.active = showDps;
if (this.rtTNum?.node?.isValid) {
this.rtTNum.node.active = showDps;
if (showDps) {
const signStr = (d >= 0 ? '+' : '-') + MTools.formatChineseUnit({ num: Math.abs(d) });
this.rtTNum.string = gg.lang.get(`秒伤{1}`, [signStr]);
}
}
}
onClick() {
console.log("点击选择了技能或者武器");
if (this._dpsDelta < 0) {
gg.game.CurentBattle.skipNextDpsFlyAnim = true;
}
if (this.Skill) {
gg.game.CurentBattle.addCurRogueSkillTimes();
gg.game.CurentBattle.addCurHaveSkillArr([this.Skill]);

@ -1,10 +1,18 @@
import { _decorator, CCInteger, Color, Component, Node, PolygonCollider2D, RigidBody2D, Sprite } from 'cc';
import {
_decorator, CCInteger, Collider2D, Color, Component, Contact2DType,
ERigidBody2DType, IPhysics2DContact, Node, PolygonCollider2D, RigidBody2D, Sprite, Vec2,
} from 'cc';
import { LuosiDing } from './LuosiDing';
const { ccclass, property } = _decorator;
/** 木板脱离屏幕后销毁的 Y 阈值(本地坐标) */
const BOARD_FALL_DESTROY_Y = -1000;
/** 螺丝全部拔完后木板掉落时的整体色调 */
const BOARD_FALL_GRAY = '#646464';
/** 进入自由掉落后:本地 Y 低于此值(相对 layer)即可销毁 */
const BOARD_FALL_DESTROY_LOCAL_Y = -60;
/** 进入自由掉落后最长存活(秒) */
const BOARD_FALL_MAX_LIFE_SEC = 2.5;
/** 碰到 floor 后延迟销毁(秒) */
const BOARD_FALL_FLOOR_DESTROY_DELAY = 0.15;
/**木板子元素*/
@ccclass('Aelement')
@ -13,16 +21,29 @@ export class Aelement extends Component {
@property(CCInteger)
aelementHoleNum: number = 2;
/** 木板上尚未脱落的螺丝数量 */
/** 生成时孔洞螺丝总数 */
private _initialScrewCount = 0;
/** 木板上尚未脱落的螺丝数量(仅按 onScrewLeft 递减,不含铰链锚点) */
private _remainingScrews = 0;
/** 螺丝全部脱落后,开始检测木板掉落 */
private _fallCheckEnabled = false;
/** 已启用铰链约束物理(仅剩一颗螺丝) */
private _constrainedPhysicsActive = false;
/** 进入自由掉落累计时间 */
private _fallLifeAcc = 0;
private _fallContactBound = false;
private _physicsActivating = false;
/** 木板是否已进入掉落态(置灰),不再参与遮挡其它螺丝 */
get isFalling(): boolean {
return this._fallCheckEnabled;
}
/** 仅剩一颗螺丝、绕铰链摆动中 */
get isSwinging(): boolean {
return this._constrainedPhysicsActive && !this._fallCheckEnabled;
}
/** 获取木板上所有孔洞节点(孔洞1、孔洞2…) */
getHoleNodes(): Node[] {
return this.node.children
@ -32,18 +53,71 @@ export class Aelement extends Component {
/** 生成螺丝后登记数量 */
initScrewCount(count: number) {
this._initialScrewCount = count;
this._remainingScrews = count;
this._fallCheckEnabled = false;
this._constrainedPhysicsActive = false;
this._fallLifeAcc = 0;
this._physicsActivating = false;
}
/** 螺丝从木板上脱落(弹出或消除)时由 LuosiDing 调用 */
onScrewLeft() {
if (this._remainingScrews <= 0) return;
this._remainingScrews--;
if (this._remainingScrews <= 0) {
this._fallCheckEnabled = true;
if (this._remainingScrews > 0) {
this._remainingScrews--;
}
this._applyPhysicsForScrewCount();
}
/** 复活 / 广告消钉后:按孔洞内实际螺丝数恢复 */
reconcilePhysicsState() {
if (!this.node?.isValid) return;
this._remainingScrews = LuosiDing.countAttachedScrewsInHoles(this.node);
this._applyPhysicsForScrewCount();
}
/**
* prefab
* - 2+
* - 1 Dynamic +
* - 0
*/
private _applyPhysicsForScrewCount() {
if (this._remainingScrews > 1) {
this._fallCheckEnabled = false;
this._fallLifeAcc = 0;
this._constrainedPhysicsActive = false;
this._unbindFallContact();
LuosiDing.detachAllBoardHinges(this.node);
this.prepareColliderForGameplay();
return;
}
if (this._remainingScrews === 1) {
this._enterConstrainedPhysics();
return;
}
if (this._initialScrewCount > 0) {
this._enterFreeFall();
}
}
private _enterConstrainedPhysics() {
this._fallCheckEnabled = false;
this._fallLifeAcc = 0;
this._unbindFallContact();
this.beginConstrainedPhysics();
}
private _enterFreeFall() {
if (!this.node?.isValid) return;
if (!this._fallCheckEnabled) {
this._applyFallGrayColor();
}
this._fallCheckEnabled = true;
this._constrainedPhysicsActive = false;
this._fallLifeAcc = 0;
LuosiDing.detachAllBoardHinges(this.node);
this.beginFallingPhysics();
}
/** 螺丝拔完开始掉落时,将木板及子节点 Sprite 整体置灰 */
@ -57,30 +131,185 @@ export class Aelement extends Component {
visit(this.node);
}
/** 开启木板刚体与多边形碰撞体(prefab 默认关闭,生成后再启用) */
enablePhysics() {
/**
* Polygon LuosiDing
*/
prepareColliderForGameplay() {
this.unschedule(this._activateConstrainedRigidbody);
this.unschedule(this._activateFallRigidbody);
this._physicsActivating = false;
this._unbindFallContact();
LuosiDing.detachAllBoardHinges(this.node);
this._constrainedPhysicsActive = false;
const rb = this.node.getComponent(RigidBody2D);
if (rb){
rb.enabled = true;
//rb.gravityScale = 0.5
rb.angularDamping = 0.8;
if (rb) {
rb.enabled = false;
rb.linearVelocity = Vec2.ZERO;
rb.angularVelocity = 0;
}
const poly = this.node.getComponent(PolygonCollider2D);
if (poly) {
poly.enabled = false;
poly.enabled = true;
}
}
update() {
/** 仅剩一颗螺丝:Dynamic + 单铰链,受重力自然摆 */
beginConstrainedPhysics() {
if (this._physicsActivating) return;
this.unschedule(this._activateConstrainedRigidbody);
this.scheduleOnce(this._activateConstrainedRigidbody, 0.06);
}
private _activateConstrainedRigidbody = () => {
if (!this.node?.isValid || this._fallCheckEnabled || this._remainingScrews !== 1) return;
this._physicsActivating = true;
const rb = this.node.getComponent(RigidBody2D);
if (!rb) {
this._physicsActivating = false;
return;
}
if (rb.enabled) {
rb.enabled = false;
}
// 传感器:不与堆叠木板/斜坡产生分离冲量,只靠铰链+重力按重心摆
this._enableBoardCollider(true);
rb.enabled = true;
rb.type = ERigidBody2DType.Dynamic;
rb.gravityScale = 5;
rb.linearDamping = 0.2;
rb.angularDamping = 5;
rb.fixedRotation = false;
rb.allowSleep = true;
rb.bullet = false;
rb.enabledContactListener = false;
rb.linearVelocity = Vec2.ZERO;
rb.angularVelocity = 0;
LuosiDing.refreshBoardHinges(this.node);
rb.wakeUp();
this._constrainedPhysicsActive = true;
this._physicsActivating = false;
};
/** 螺丝全部拔完后脱离铰链,沿斜坡自然滑落 */
beginFallingPhysics() {
LuosiDing.detachAllBoardHinges(this.node);
this.unschedule(this._activateFallRigidbody);
this.scheduleOnce(this._activateFallRigidbody, 0.06);
}
private _activateFallRigidbody = () => {
if (!this._fallCheckEnabled || !this.node?.isValid) return;
if (this.node.position.y < BOARD_FALL_DESTROY_Y) {
console.log('木板销毁完成');
this.node.destroy();
this._physicsActivating = true;
const rb = this.node.getComponent(RigidBody2D);
if (!rb) {
this._physicsActivating = false;
return;
}
if (rb.enabled) {
rb.enabled = false;
}
this._enableBoardCollider(false);
this._bindFallContact();
rb.enabled = true;
rb.type = ERigidBody2DType.Dynamic;
rb.gravityScale = 5;
rb.linearDamping = 0.2;
rb.angularDamping = 5;
rb.fixedRotation = false;
rb.allowSleep = true;
rb.bullet = false;
rb.enabledContactListener = true;
rb.linearVelocity = Vec2.ZERO;
rb.angularVelocity = 0;
rb.wakeUp();
this._physicsActivating = false;
};
private _bindFallContact() {
if (this._fallContactBound) return;
const col = this.node.getComponent(Collider2D);
if (!col) return;
col.on(Contact2DType.BEGIN_CONTACT, this._onFallFloorContact, this);
this._fallContactBound = true;
}
private _unbindFallContact() {
if (!this._fallContactBound) return;
const col = this.node.getComponent(Collider2D);
col?.off(Contact2DType.BEGIN_CONTACT, this._onFallFloorContact, this);
this._fallContactBound = false;
}
private _scheduleFloorDestroy = () => {
this._destroyFallenBoard();
};
private _onFallFloorContact(_self: Collider2D, other: Collider2D, _contact: IPhysics2DContact | null) {
if (!this._fallCheckEnabled || !other?.node?.isValid) return;
const n = other.node.name;
const p = other.node.parent?.name ?? '';
if (n.indexOf('floor') >= 0 || p === 'ui_floorNode') {
this.scheduleOnce(this._scheduleFloorDestroy, BOARD_FALL_FLOOR_DESTROY_DELAY);
}
}
private _enableBoardCollider(sensor: boolean) {
const poly = this.node.getComponent(PolygonCollider2D);
if (!poly) return;
poly.enabled = true;
poly.sensor = sensor;
poly.friction = 0.4;
poly.restitution = 0;
const apply = (poly as { apply?: () => void }).apply;
if (typeof apply === 'function') apply.call(poly);
}
private _destroyFallenBoard() {
if (!this.node?.isValid) return;
this.unschedule(this._scheduleFloorDestroy);
this._unbindFallContact();
const rb = this.node.getComponent(RigidBody2D);
if (rb) rb.enabled = false;
this.node.destroy();
}
onDestroy() {
this._unbindFallContact();
}
}
update(dt: number) {
if (!this.node?.isValid) return;
if (!this._fallCheckEnabled) {
if (this._remainingScrews <= 0 && this._initialScrewCount > 0
&& LuosiDing.countAttachedScrewsInHoles(this.node) === 0) {
this._enterFreeFall();
}
return;
}
this._fallLifeAcc += dt;
if (this._fallLifeAcc >= BOARD_FALL_MAX_LIFE_SEC) {
this._destroyFallenBoard();
return;
}
if (this.node.position.y < BOARD_FALL_DESTROY_LOCAL_Y) {
this._destroyFallenBoard();
return;
}
const rb = this.node.getComponent(RigidBody2D);
if (!rb?.enabled && !this._physicsActivating) {
this._activateFallRigidbody();
}
}
}

@ -72,6 +72,10 @@ export class LuosiDing extends Component {
return !this._released && !this._removing && !this._popTweening;
}
getBoardNode(): Node | null {
return this._boardAelement?.node ?? null;
}
isRemoving(): boolean {
return this._removing;
}
@ -347,7 +351,7 @@ export class LuosiDing extends Component {
const key = boardNode.uuid;
if (cache.has(key)) return cache.get(key) ?? null;
const poly = boardNode.getComponent(PolygonCollider2D);
const worldPoly = poly?.enabled ? LuosiDing._getPolygonWorldPoints(poly) : null;
const worldPoly = poly ? LuosiDing._getPolygonWorldPoints(poly) : null;
cache.set(key, worldPoly);
return worldPoly;
}
@ -417,17 +421,157 @@ export class LuosiDing extends Component {
}
private _detachHinge() {
this.detachBoardHinge();
}
/** 孔洞内仍固定的螺丝(不含提到 layer 的铰链锚点) */
static countAttachedScrewsInHoles(boardNode: Node): number {
if (!boardNode?.isValid) return 0;
let count = 0;
const aelement = boardNode.getComponent(Aelement);
for (const hole of aelement?.getHoleNodes() ?? []) {
if (!hole?.isValid) continue;
for (const child of hole.children) {
if (!child?.isValid) continue;
if (child.getComponent(LuosiDing)?.isAttachedOnBoard()) count++;
}
}
return count;
}
/** 铰链锚点:孔洞内 + 已提到 layer 的螺丝 */
static findPhysicsAnchorScrews(boardNode: Node): LuosiDing[] {
if (!boardNode?.isValid) return [];
const screws: LuosiDing[] = [];
const seen = new Set<LuosiDing>();
const aelement = boardNode.getComponent(Aelement);
const tryAdd = (luosi: LuosiDing | null) => {
if (!luosi?.isValid || !luosi.isAttachedOnBoard()) return;
const owner = luosi.getBoardNode();
if (!owner?.isValid || owner !== boardNode) return;
if (seen.has(luosi)) return;
seen.add(luosi);
screws.push(luosi);
};
for (const hole of aelement?.getHoleNodes() ?? []) {
if (!hole?.isValid) continue;
for (const child of hole.children) {
if (!child?.isValid) continue;
tryAdd(child.getComponent(LuosiDing));
}
}
const layer = boardNode.parent;
if (layer?.isValid) {
for (const child of layer.children) {
if (!child?.isValid || child === boardNode) continue;
tryAdd(child.getComponent(LuosiDing));
}
}
return screws;
}
/** @deprecated 用 countAttachedScrewsInHoles / findPhysicsAnchorScrews */
static findAttachedScrews(boardNode: Node): LuosiDing[] {
return LuosiDing.findPhysicsAnchorScrews(boardNode);
}
/** 按当前仍固定的螺丝刷新铰链(螺丝提到 layer 作静态锚点) */
static refreshBoardHinges(boardNode: Node) {
if (!boardNode?.isValid) return;
LuosiDing.detachAllBoardHinges(boardNode);
const boardRb = boardNode.getComponent(RigidBody2D);
if (!boardRb) return;
for (const screw of LuosiDing.findPhysicsAnchorScrews(boardNode)) {
if (!screw?.isValid) continue;
screw.attachBoardHinge(boardNode, boardRb);
}
}
static detachAllBoardHinges(boardNode: Node) {
if (!boardNode?.isValid) return;
for (const screw of LuosiDing.findPhysicsAnchorScrews(boardNode)) {
if (!screw?.isValid) continue;
screw.detachBoardHinge();
}
}
/**
* HingeJoint2D
*
* layer children
* 穿
*/
attachBoardHinge(boardNode: Node, boardRb?: RigidBody2D | null) {
if (!boardNode?.isValid || !this.isAttachedOnBoard()) return;
const rb = boardRb ?? boardNode.getComponent(RigidBody2D);
if (!rb) return;
const anchorLayer = boardNode.parent;
if (anchorLayer?.isValid && this.node.parent !== anchorLayer) {
const wp = this.node.worldPosition.clone();
this.node.setParent(anchorLayer);
this.node.worldPosition = wp;
}
this._placeScrewSiblingJustAboveBoard(boardNode);
const screwRb = this.getComponent(RigidBody2D);
if (screwRb) {
screwRb.enabled = true;
screwRb.type = ERigidBody2DType.Static;
screwRb.linearVelocity = Vec2.ZERO;
screwRb.angularVelocity = 0;
}
const hinge = this.getComponent(HingeJoint2D);
if (!hinge) return;
hinge.connectedBody = null;
hinge.enabled = false;
if (hinge) {
const boardLocal = new Vec3();
boardNode.inverseTransformPoint(boardLocal, this.node.worldPosition);
hinge.anchor = Vec2.ZERO;
hinge.connectedAnchor = new Vec2(boardLocal.x, boardLocal.y);
hinge.connectedBody = rb;
hinge.collideConnected = false;
hinge.enabled = true;
const apply = (hinge as { apply?: () => void }).apply;
if (typeof apply === 'function') apply.call(hinge);
}
this._setColliderSensor(true);
}
/** 同层内:螺丝紧挨在所属木板之后,仍低于后续 sibling 木板(上层遮挡) */
private _placeScrewSiblingJustAboveBoard(boardNode: Node) {
if (!this.node?.isValid || !boardNode?.isValid) return;
if (this.node.parent !== boardNode.parent) return;
const boardIdx = boardNode.getSiblingIndex();
const screwIdx = this.node.getSiblingIndex();
// 螺丝已在木板前时,目标索引用 boardIdx(移动后木板会前移);否则用 boardIdx+1
const target = screwIdx < boardIdx ? boardIdx : boardIdx + 1;
if (screwIdx !== target) {
this.node.setSiblingIndex(target);
}
}
detachBoardHinge() {
const hinge = this.getComponent(HingeJoint2D);
if (hinge) {
hinge.enabled = false;
hinge.connectedBody = null;
}
const screwRb = this.getComponent(RigidBody2D);
if (screwRb) {
screwRb.enabled = false;
screwRb.linearVelocity = Vec2.ZERO;
screwRb.angularVelocity = 0;
}
}
/** 通知木板:本螺丝已脱离 */
private _notifyBoardScrewLeft() {
if (this._notifiedBoardLeft) return;
this._notifiedBoardLeft = true;
this._boardAelement?.onScrewLeft();
const board = this._boardAelement;
if (!board?.isValid || !board.node?.isValid) return;
board.onScrewLeft();
}
/** 弹出动画结束:恢复碰撞并启用 Dynamic 下落 */
@ -554,6 +698,7 @@ export class LuosiDing extends Component {
/** 广告消除:移到临时层、播粒子并销毁(不经过物理配对) */
private _prepareForceEliminate(tempParent: Node) {
if (this._removing) return;
const wasOnBoard = this.isAttachedOnBoard();
this._removing = true;
this.node.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
const col = this.getComponent(Collider2D);
@ -570,7 +715,7 @@ export class LuosiDing extends Component {
const collider = this.getComponent(Collider2D);
if (collider) collider.enabled = false;
if (this.isAttachedOnBoard()) {
if (wasOnBoard) {
this._released = true;
this._notifyBoardScrewLeft();
}
@ -625,35 +770,13 @@ export class LuosiDing extends Component {
}
/**
* connectedBody
*
* /
*/
bindBoardPhysics(boardNode: Node) {
if (!boardNode?.isValid) return;
this._boardAelement = boardNode.getComponent(Aelement);
const boardRb = boardNode.getComponent(RigidBody2D);
if (!boardRb) {
console.warn('[LuosiDing] 木板缺少 RigidBody2D', boardNode.name);
return;
}
const screwRb = this.getComponent(RigidBody2D);
if (screwRb) {
screwRb.enabled = true;
screwRb.type = ERigidBody2DType.Static;
}
this.detachBoardHinge();
this._setColliderSensor(true);
const hinge = this.getComponent(HingeJoint2D);
if (!hinge) return;
hinge.connectedBody = boardRb;
const boardUt = boardNode.getComponent(UITransform);
if (boardUt) {
const local = new Vec3();
boardUt.convertToNodeSpaceAR(this.node.worldPosition, local);
hinge.connectedAnchor = new Vec2(local.x, local.y);
hinge.anchor = new Vec2(0, 0);
}
hinge.enabled = true;
}
/**

@ -6,9 +6,9 @@
//*********************
import {
_decorator, Color, Collider2D, Component, Contact2DType, ERigidBody2DType, find,
instantiate, IPhysics2DContact, isValid, Layers, Node, ParticleSystem2D, PhysicsSystem2D, ProgressBar,
instantiate, isValid, Layers, Node, ParticleSystem2D, PhysicsSystem2D, ProgressBar,
RigidBody2D, sp, Tween, UITransform, tween, v3, Vec3,
Label,
Label, IPhysics2DContact,
} from 'cc';
import { AelementLayer } from './AelementLayer';
import { Aelement } from './Aelement';
@ -61,8 +61,6 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
private static readonly MUBAN_PREFAB_NAMES = [
'木板1_2','木板2_2','木板3_2','木板4_2','木板5_2','木板6_1','木板7_3','木板8_2'
];
/** 与 settings physics.collisionGroups index 11「muban1」一致 */
private static readonly MUBAN_PHYSICS_GROUP = 1 << 11;
onLoad(): void {
super.onLoad();
}
@ -70,24 +68,24 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
onEnable(): void {
super.onEnable();
this._ensureJiujiuwoyaPhysics();
this._bindMubanNoBouncePhysics();
GEvent.Ins.on(GEvent.playerHit, this.onPlayerHit, this);
GEvent.Ins.on(GEvent.tetriGameStart, this.onTetriGameStart, this);
GEvent.Ins.on(GEvent.GameReborn, this.gameReborn, this);
GEvent.Ins.on(GEvent.UpdateDps, this.updateDps, this);
GEvent.Ins.on(GEvent.UpdateWallHp, this.refreshWallHp, this);
GEvent.Ins.on(GEvent.BattleWaveRefresh, this.onBattleWaveRefresh, this);
PhysicsSystem2D.instance.on(Contact2DType.PRE_SOLVE, this._onMubanPreSolve, this);
}
onDisable(): void {
super.onDisable();
this._unbindMubanNoBouncePhysics();
GEvent.Ins.off(GEvent.playerHit, this.onPlayerHit, this);
GEvent.Ins.off(GEvent.tetriGameStart, this.onTetriGameStart, this);
GEvent.Ins.off(GEvent.GameReborn, this.gameReborn, this);
GEvent.Ins.off(GEvent.UpdateDps, this.updateDps, this);
GEvent.Ins.off(GEvent.UpdateWallHp, this.refreshWallHp, this);
GEvent.Ins.off(GEvent.BattleWaveRefresh, this.onBattleWaveRefresh, this);
PhysicsSystem2D.instance.off(Contact2DType.PRE_SOLVE, this._onMubanPreSolve, this);
if (gg.game.CurentBattle) {
gg.game.CurentBattle.onJiujiuwoyaLuosiEnteredSlot = null;
gg.game.CurentBattle.onJiujiuwoyaLuosiPairRemovedFromSlot = null;
@ -104,15 +102,34 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
this.CurentBattle.jiujiuwoyaSoltFullFail = false;
this.CurentBattle.startGame()
this.eliminateTwoScrewPairs();
this.scheduleOnce(() => this._reconcileAllBoardPhysics(), 1.5);
}, 1.0)
}
/** 复活后按场上实际螺丝数恢复木板约束/掉落物理 */
private _reconcileAllBoardPhysics() {
const parent = this.ui_mubanLayer;
if (!parent?.isValid) return;
for (const layer of parent.children) {
if (!layer?.isValid) continue;
for (const board of layer.children) {
board.getComponent(Aelement)?.reconcilePhysicsState();
}
}
}
updateDps(delay: number = 0) {
if(gg.game.noMonsterShow) return;
let dps = this.CurentBattle.dps();
dps = Math.floor(dps);
if (this.CurentBattle?.skipNextDpsFlyAnim) {
this.CurentBattle.skipNextDpsFlyAnim = false;
this._lastDps = dps;
this.lb_dps.string = MTools.formatChineseUnit({ num: dps });
return;
}
if (dps == this._lastDps || dps == 0) {
if (dps == 0) {
this.lb_dps.string = '0';
@ -187,56 +204,44 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
Physics2DGate.beginBattle();
}
/** 向上找到挂 Aelement 的木板根节点 */
private _findMubanRoot(from: Node): Node | null {
let n: Node | null = from;
while (n?.isValid) {
if (n.getComponent(Aelement)) return n;
n = n.parent;
}
return null;
/** 木板/地板碰撞:解算前强制无弹性(不在此阶段读写 velocity,避免只读 Vec2 报错) */
private _bindMubanNoBouncePhysics() {
PhysicsSystem2D.instance.on(Contact2DType.PRE_SOLVE, this._onMubanPreSolve, this);
}
/** 木板所在逻辑层(layer1…layer25,挂 AelementLayer) */
private _getMubanLogicLayerNode(mubanRoot: Node): Node | null {
const p = mubanRoot.parent;
return p?.isValid && p.getComponent(AelementLayer) ? p : null;
private _unbindMubanNoBouncePhysics() {
PhysicsSystem2D.instance.off(Contact2DType.PRE_SOLVE, this._onMubanPreSolve, this);
}
/**
* muban1 layer 穿 floor
*/
private _onMubanPreSolve(self: Collider2D, other: Collider2D, contact: IPhysics2DContact | null): void {
private _onMubanPreSolve(self: Collider2D, other: Collider2D, contact: IPhysics2DContact | null) {
if (!contact) return;
if (self.group !== UIjiujiuwoyaGame.MUBAN_PHYSICS_GROUP
|| other.group !== UIjiujiuwoyaGame.MUBAN_PHYSICS_GROUP) return;
const aRoot = this._findMubanRoot(self.node);
const bRoot = this._findMubanRoot(other.node);
if (!aRoot || !bRoot || aRoot === bRoot) return;
const aElem = aRoot.getComponent(Aelement);
const bElem = bRoot.getComponent(Aelement);
if (aElem?.isFalling || bElem?.isFalling) {
this._disableMubanContact(contact);
const aeA = this._getAelementFromCollider(self);
const aeB = this._getAelementFromCollider(other);
// 堆叠木板互相穿插,开刚体后分离冲量会表现为弹跳/颤抖:木板之间一律不碰撞
if (aeA && aeB) {
contact.disabled = true;
return;
}
const layerA = this._getMubanLogicLayerNode(aRoot);
const layerB = this._getMubanLogicLayerNode(bRoot);
if (!layerA || !layerB || layerA === layerB) return;
this._disableMubanContact(contact);
const swinging = aeA?.isSwinging || aeB?.isSwinging;
if (swinging) {
contact.disabled = true;
return;
}
if (!aeA && !aeB) return;
try { contact.setRestitution(0); } catch { /* ignore */ }
try { contact.setFriction(0.4); } catch { /* ignore */ }
}
private _disableMubanContact(contact: IPhysics2DContact): void {
try {
const se = (contact as { setEnabled?: (e: boolean) => void }).setEnabled;
if (typeof se === 'function') se.call(contact, false);
} catch { /* ignore */ }
private _getAelementFromCollider(col: Collider2D): Aelement | null {
let n: Node | null = col?.node ?? null;
while (n?.isValid) {
const ae = n.getComponent(Aelement);
if (ae) return ae;
n = n.parent;
}
return null;
}
setLayerMask(n: Node, layerMask: number) {
n.layer = layerMask;
for (let i = 0; i < n.children.length; i++) {
@ -346,7 +351,8 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
let roleNode = roleNodes[i]
if (!roleNode?.isValid) continue;
const carrier = roleNode.getOrAddComponent(tetriWeaponCarrier)
carrier.setConfig(cards[i])
carrier.setConfig(cards[i], true, true)
carrier.ensureBattleReady()
carriers.push(carrier);
let weaponId = carrier.getCurWeaponId()
if (weaponId) {
@ -388,6 +394,8 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
col.group = floorGroup;
col.sensor = false;
col.enabled = true;
col.restitution = 0;
col.friction = 0.35;
const apply = (col as { apply?: () => void }).apply;
if (typeof apply === 'function') apply.call(col);
}
@ -443,11 +451,24 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
private onMubanSpawnFinished() {
this._mubanSpawnDone++;
if (this._battleStartedAfterMuban || this._mubanSpawnDone < this._mubanSpawnTotal) return;
this._finalizeMubanSpawnPhysics();
this._battleStartedAfterMuban = true;
this.CurentBattle?.startWarnTime();
this.CurentBattle.mubanSpawnFinished = true;
}
/** 全部木板与螺丝就位后,仅开启 Polygon 遮挡检测(不启用刚体,避免 128 板 + 铰链拖垮物理) */
private _finalizeMubanSpawnPhysics() {
const parent = this.ui_mubanLayer;
if (!parent?.isValid) return;
for (const layer of parent.children) {
if (!layer?.isValid) continue;
for (const board of layer.children) {
board.getComponent(Aelement)?.prepareColliderForGameplay();
}
}
}
/** 在指定 layer 内生成一块木板,随机位置与角度 */
private spawnMubanCard(prefabName: string, layerIndex: number) {
const parent = this.ui_mubanLayer;
@ -468,6 +489,7 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
return;
}
boardNode.parent = layerNode;
boardNode.getComponent(Aelement)?.prepareColliderForGameplay();
boardNode.scale = v3(0, 0, 0);
tween(boardNode)
.to(0.1, { scale: v3(1.1, 1.1, 1.1) })
@ -506,7 +528,6 @@ export class UIjiujiuwoyaGame extends Auto_jiujiuwoyaGame {
const parent = this.ui_mubanLayer;
if (!boardNode?.isValid || !parent?.isValid) return;
const aelement = boardNode.getComponent(Aelement);
aelement?.enablePhysics();
const holes = aelement?.getHoleNodes() ?? [];
aelement?.initScrewCount(holes.length);
for (const hole of holes) {

@ -577,6 +577,10 @@ export class UItetraGame extends Auto_tetriGame {
gg.sdk?.pfLogKey?.('BATTLE:PREFAB_FAIL', 'cardItem');
continue
}
if (!parentNode?.isValid) {
cardItem.destroy();
continue
}
cardItem.setParent(parentNode, false)
cardItem.setPosition(0, 0)
const cardComp = cardItem.getComponent(tetriCardItem)
@ -604,6 +608,10 @@ export class UItetraGame extends Auto_tetriGame {
gg.sdk?.pfLogKey?.('BATTLE:PREFAB_FAIL', tetriConfig.cubename);
continue
}
if (!cardItem?.isValid) {
cardNode.destroy();
continue
}
cardNode.setParent(cardItem, false)
cardNode.opacity = 255
cardNode.setPosition(0, 30)
@ -2018,6 +2026,12 @@ export class UItetraGame extends Auto_tetriGame {
let dps = this.CurentBattle.dps();
dps = Math.floor(dps);
if (this.CurentBattle?.skipNextDpsFlyAnim) {
this.CurentBattle.skipNextDpsFlyAnim = false;
this._lastDps = dps;
this.lb_dps.string = MTools.formatChineseUnit({ num: dps });
return;
}
if (dps == this._lastDps || dps == 0) {
if (dps == 0) {
this.lb_dps.string = '0';

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save