diff --git a/public/project/floors/tower7.js b/public/project/floors/tower7.js index 86f5bd2..1d4e295 100644 --- a/public/project/floors/tower7.js +++ b/public/project/floors/tower7.js @@ -113,11 +113,10 @@ main.floors.tower7= ], "no": [ "屏幕上方有boss血条和提示等,请注意阅读", - "注意:重新开始特殊战需要刷新页面!!!!!!!!!!!!", "下面,就让我们开始吧!", { "type": "function", - "function": "function(){\ncore.plugin.replay.readyClip();\n}" + "function": "function(){\nMota.Plugin.require('replay_g').readyClip();\n}" } ] }, @@ -153,7 +152,7 @@ main.floors.tower7= }, { "type": "function", - "function": "function(){\ncore.drawWarning(7, 2, \"智慧之神\");\n}" + "function": "function(){\n// core.drawWarning(7, 2, \"智慧之神\");\n}" }, { "type": "sleep", @@ -162,7 +161,7 @@ main.floors.tower7= }, { "type": "function", - "function": "function(){\ncore.plugin.towerBoss.initTowerBoss();\n}" + "function": "function(){\nMota.Plugin.require('boss_r').startTowerBoss();\n}" } ], "eachArrive": [], diff --git a/src/core/render/item.ts b/src/core/render/item.ts index 6626766..6126304 100644 --- a/src/core/render/item.ts +++ b/src/core/render/item.ts @@ -427,6 +427,9 @@ export abstract class RenderItem this.remove(); parent.children.add(this); this.parent = parent; + parent.requestSort(); + this.needUpdate = false; + this.update(); if (this._id !== '') { const root = this.findRoot(); if (!root) return; @@ -439,8 +442,11 @@ export abstract class RenderItem */ remove(): boolean { if (!this.parent) return false; - const success = this.parent.children.delete(this); + const parent = this.parent; + const success = parent.children.delete(this); this.parent = void 0; + parent.requestSort(); + parent.update(); if (!success) return false; RenderItem.itemMap.delete(this._id); return true; diff --git a/src/core/render/render.ts b/src/core/render/render.ts index afbda1d..bab6d2b 100644 --- a/src/core/render/render.ts +++ b/src/core/render/render.ts @@ -31,12 +31,13 @@ export class MotaRenderer extends Container { if (this.needUpdate) return; this.needUpdate = true; this.requestRenderFrame(() => { - this.needUpdate = false; this.refresh(item); }); } protected refresh(item: RenderItem = this): void { + if (!this.needUpdate) return; + this.needUpdate = false; this.emit('beforeUpdate', item); this.target.clear(); this.renderContent(this.target, Transform.identity); diff --git a/src/game/system.ts b/src/game/system.ts index 83f6152..e50cbaa 100644 --- a/src/game/system.ts +++ b/src/game/system.ts @@ -160,6 +160,7 @@ interface PluginInterface { completion_r: typeof import('../plugin/completion'); gameCanvas_r: typeof import('../plugin/fx/gameCanvas'); frag_r: typeof import('../plugin/fx/frag'); + boss_r: typeof import('../plugin/boss'); // 游戏进程定义的插件 utils_g: typeof import('../plugin/game/utils'); shop_g: typeof import('../plugin/game/shop'); diff --git a/src/plugin/boss/index.ts b/src/plugin/boss/index.ts new file mode 100644 index 0000000..7b18e39 --- /dev/null +++ b/src/plugin/boss/index.ts @@ -0,0 +1,13 @@ +import { BarrageBoss } from './barrage'; +import { TowerBoss } from './towerBoss'; + +let boss: BarrageBoss; + +export function startTowerBoss() { + boss = new TowerBoss(); + boss.start(); +} + +export function getBoss(): T { + return boss as T; +} diff --git a/src/plugin/boss/towerBoss.ts b/src/plugin/boss/towerBoss.ts index 0090171..0fccabc 100644 --- a/src/plugin/boss/towerBoss.ts +++ b/src/plugin/boss/towerBoss.ts @@ -17,6 +17,7 @@ import { import { Container } from '@/core/render/container'; import { ArrowProjectile, + AttackProjectile, BoomProjectile, ChainProjectile, IceProjectile, @@ -56,23 +57,6 @@ const enum HealthBarStatus { End } -interface TowerBossAttack { - x: number; - y: number; - damage: number; - /** 生成时刻 */ - spwan: number; - /** 持续时长 */ - last: number; -} - -interface AttackCircleRenderable { - cx: number; - cy: number; - alpha: number; - lineOffset: number; -} - export class TowerBoss extends BarrageBoss { static effect: PointEffect = new PointEffect(); static shader: Shader; @@ -88,9 +72,6 @@ export class TowerBoss extends BarrageBoss { readonly state: IStateDamageable; readonly main: BossEffect; - /** 攻击位点 */ - private attackLoc: Set = new Set(); - /** 血条显示元素 */ private healthBar: HealthBar; /** 对话文字显示元素 */ @@ -108,8 +89,6 @@ export class TowerBoss extends BarrageBoss { private attackTime: number = 0; /** 攻击boss的红圈间隔时长 */ private attackInterval: number = 7000; - private attackIn: TimingFn = hyper('sin', 'out'); - private attackOut: TimingFn = hyper('sin', 'in'); /** 使用技能1 智慧之矢 的次数 */ private skill1Time: number = 0; @@ -156,9 +135,7 @@ export class TowerBoss extends BarrageBoss { this.word.init(); this.main.init(); - this.healthBar.append(this.group); - this.word.append(this.group); - this.main.append(this.group); + TowerBoss.effect.setTransform(this.group.camera); const { x, y } = core.status.hero.loc; const cell = 32; @@ -171,10 +148,17 @@ export class TowerBoss extends BarrageBoss { this.group.remove(); this.group.append(TowerBoss.shader); TowerBoss.shader.append(this.mapDraw); + this.healthBar.append(this.group); + this.word.append(this.group); + this.main.append(this.group); ArrowProjectile.init(); PortalProjectile.init(); ThunderProjectile.init(); + AttackProjectile.init(); + + TowerBoss.effect.start(); + TowerBoss.effect.use(); } override end() { @@ -188,6 +172,9 @@ export class TowerBoss extends BarrageBoss { ArrowProjectile.end(); PortalProjectile.end(); ThunderProjectile.end(); + AttackProjectile.end(); + + TowerBoss.effect.end(); } /** @@ -229,64 +216,20 @@ export class TowerBoss extends BarrageBoss { * @param last 持续时长 * @param damage 造成的伤害 */ - addAttackCircle(last: number, damage: number) { - let nx = 0; - let ny = 0; - if (this.stage === TowerBossStage.Stage3) { - nx = Math.floor(Math.random() * 11 + 2); - ny = Math.floor(Math.random() * 11 + 2); - } else if (this.stage === TowerBossStage.Stage4) { - nx = Math.floor(Math.random() * 9 + 3); - ny = Math.floor(Math.random() * 9 + 3); - } else if (this.stage === TowerBossStage.Stage5) { - nx = Math.floor(Math.random() * 7 + 4); - ny = Math.floor(Math.random() * 7 + 4); - } else { - nx = Math.floor(Math.random() * 13 + 1); - ny = Math.floor(Math.random() * 13 + 1); - } - const obj: TowerBossAttack = { - x: nx, - y: ny, - spwan: this.time, - damage, - last - }; - this.attackLoc.add(obj); - } - - private getAttackCircleRenderable(): AttackCircleRenderable[] { - return [...this.attackLoc].map(v => { - const progress = (this.time - v.spwan) / v.last; - let alpha = 1; - let offset = 0; - if (progress < 0.1) { - alpha = progress * 10; - offset = 32 * this.attackIn(10 * (0.1 - progress)); - } else if (progress > 0.9) { - alpha = 10 * (1 - progress); - offset = 32 * this.attackOut(10 * (progress - 0.9)); - } - return { - cx: v.x * 32, - cy: v.y * 32, - alpha, - lineOffset: offset - }; - }); - } - - private renderAttack() { - const renderable = this.getAttackCircleRenderable(); - this.main.setAttackCircle(renderable); + addAttackCircle(damage: number, n: number) { + const s = 13 - n * 2; + const nx = Math.floor(Math.random() * s + n + 1); + const ny = Math.floor(Math.random() * s + n + 1); + const proj = this.createProjectile(AttackProjectile, nx * 32, ny * 32); + proj.damage = damage; } ai(time: number, frame: number): void { this.time = time; const fixedTime = time - this.stageStartTime; this.main.update(); - this.renderAttack(); this.check(time); + TowerBoss.effect.requestUpdate(); switch (this.stage) { case TowerBossStage.Prologue: this.aiPrologue(fixedTime, frame); @@ -351,9 +294,9 @@ export class TowerBoss extends BarrageBoss { let i = 0; while (i < count) { const dir = Math.floor(Math.random() * 2); - const pos = Math.floor(Math.random() * 13 + 1); + const pos = Math.floor(Math.random() * 13); const loc = pos + dir * 13; - if (!locs.has(loc)) continue; + if (locs.has(loc)) continue; i++; locs.add(loc); const proj = this.createProjectile(ArrowProjectile, 0, 0); @@ -412,7 +355,7 @@ export class TowerBoss extends BarrageBoss { this.skill3Time++; } if (time > attack) { - this.addAttackCircle(3000, 500); + this.addAttackCircle(500, 0); this.attackTime++; } @@ -481,7 +424,7 @@ export class TowerBoss extends BarrageBoss { this.skill5Time++; } if (time > attack) { - this.addAttackCircle(3000, 500); + this.addAttackCircle(500, 0); this.attackTime++; } @@ -575,7 +518,7 @@ export class TowerBoss extends BarrageBoss { this.skill7Time++; } if (time > attack) { - this.addAttackCircle(3000, 500); + this.addAttackCircle(500, 1); this.attackTime++; } @@ -603,7 +546,7 @@ export class TowerBoss extends BarrageBoss { this.skill7Time++; } if (time > attack) { - this.addAttackCircle(3000, 500); + this.addAttackCircle(500, 2); this.attackTime++; } @@ -631,7 +574,7 @@ export class TowerBoss extends BarrageBoss { this.skill7Time++; } if (time > attack) { - this.addAttackCircle(3000, 500); + this.addAttackCircle(500, 3); this.attackTime++; } @@ -644,9 +587,6 @@ export class TowerBoss extends BarrageBoss { } class BossEffect extends BossSprite { - private attackCircle: AttackCircleRenderable[] = []; - private chainPath: LocArr[] = []; - /** * 初始化 */ @@ -656,18 +596,10 @@ class BossEffect extends BossSprite { this.setZIndex(80); } - /** - * 设置攻击boss圆圈的渲染信息 - */ - setAttackCircle(renderable: AttackCircleRenderable[]) { - this.attackCircle = renderable; - } - protected preDraw( canvas: MotaOffscreenCanvas2D, transform: Transform ): boolean { - this.renderAttackCircle(canvas); return true; } @@ -675,32 +607,6 @@ class BossEffect extends BossSprite { canvas: MotaOffscreenCanvas2D, transform: Transform ): void {} - - private renderAttackCircle(canvas: MotaOffscreenCanvas2D) { - const ctx = canvas.ctx; - ctx.strokeStyle = '#ffe229'; - ctx.fillStyle = '#ffe229'; - ctx.lineWidth = 2; - this.attackCircle.forEach(({ cx, cy, lineOffset, alpha }) => { - ctx.globalAlpha = alpha; - ctx.beginPath(); - const offset = lineOffset + 8; - ctx.arc(cx, cy, 2, 0, Math.PI * 2); - ctx.fill(); - ctx.beginPath(); - ctx.arc(cx, cy, offset, 0, Math.PI * 2); - ctx.moveTo(cx + offset, cy); - ctx.lineTo(cx + offset + 16, cy); - ctx.moveTo(cx, cy + offset); - ctx.lineTo(cx, cy + offset + 16); - ctx.moveTo(cx - offset, cy); - ctx.lineTo(cx - offset - 16, cy); - ctx.moveTo(cx, cy - offset); - ctx.lineTo(cx, cy - offset - 16); - ctx.stroke(); - }); - ctx.globalAlpha = 1; - } } interface TextRenderable { diff --git a/src/plugin/boss/towerBossProjectile.ts b/src/plugin/boss/towerBossProjectile.ts index 71f2eaa..642273a 100644 --- a/src/plugin/boss/towerBossProjectile.ts +++ b/src/plugin/boss/towerBossProjectile.ts @@ -17,6 +17,88 @@ export const enum ProjectileDirection { BottomToTop } +export class AttackProjectile extends Projectile { + static easeIn?: TimingFn; + static easeOut?: TimingFn; + + damage: number = 500; + hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 32, 32); + + static init() { + this.easeIn = hyper('sin', 'out'); + this.easeOut = hyper('sin', 'in'); + } + + static end() { + this.easeIn = void 0; + this.easeOut = void 0; + } + + isIntersect(hitbox: Hitbox.HitboxType): boolean { + if (hitbox instanceof Hitbox.Rect) { + return Hitbox.checkRectRect(this.hitbox, hitbox); + } else { + return false; + } + } + + updateHitbox(x: number, y: number): void { + this.hitbox.setPosition(x, y); + } + + doDamage(target: IStateDamageable): boolean { + this.boss.attackBoss(this.damage); + this.destroy(); + return true; + } + + ai(boss: TowerBoss, time: number, frame: number): void { + if (time > 4000) { + this.destroy(); + } + } + + render(canvas: MotaOffscreenCanvas2D, transform: Transform): void { + const progress = this.time / 4000; + let alpha = 1; + let offset = 0; + if (progress < 0.1) { + alpha = progress * 10; + offset = 24 * AttackProjectile.easeIn!(10 * (0.1 - progress)); + } else if (progress > 0.9) { + alpha = 10 * (1 - progress); + offset = 24 * AttackProjectile.easeOut!(10 * (progress - 0.9)); + } else { + alpha = 1; + offset = 0; + } + const ctx = canvas.ctx; + ctx.save(); + ctx.strokeStyle = '#ffe229'; + ctx.fillStyle = '#ffe229'; + ctx.lineWidth = 2; + ctx.globalAlpha = alpha; + ctx.beginPath(); + const o = offset + 16; + const cx = this.x + 16; + const cy = this.y + 16; + ctx.arc(cx, cy, 2, 0, Math.PI * 2); + ctx.fill(); + ctx.beginPath(); + ctx.arc(cx, cy, o, 0, Math.PI * 2); + ctx.moveTo(cx + o, cy); + ctx.lineTo(cx + o + 16, cy); + ctx.moveTo(cx, cy + o); + ctx.lineTo(cx, cy + o + 16); + ctx.moveTo(cx - o, cy); + ctx.lineTo(cx - o - 16, cy); + ctx.moveTo(cx, cy - o); + ctx.lineTo(cx, cy - o - 16); + ctx.stroke(); + ctx.restore(); + } +} + export class ArrowProjectile extends Projectile { static easing?: TimingFn; static dangerEasing?: TimingFn; @@ -42,23 +124,23 @@ export class ArrowProjectile extends Projectile { this.horizontal = new MotaOffscreenCanvas2D(); this.vertical = new MotaOffscreenCanvas2D(); const hor = this.horizontal; - hor.size(480 - 64, 32); + hor.size(480, 32); hor.setHD(true); hor.withGameScale(true); const ctxHor = hor.ctx; ctxHor.fillStyle = '#f00'; ctxHor.globalAlpha = 0.6; - for (let i = 0; i < 13; i++) { + for (let i = 0; i < 15; i++) { ctxHor.fillRect(i * 32 + 2, 2, 28, 28); } const ver = this.vertical; - ver.size(480 - 64, 32); + ver.size(32, 480); ver.setHD(true); ver.withGameScale(true); const ctxVer = ver.ctx; ctxVer.fillStyle = '#f00'; ctxVer.globalAlpha = 0.6; - for (let i = 0; i < 13; i++) { + for (let i = 0; i < 15; i++) { ctxVer.fillRect(2, i * 32 + 2, 28, 28); } } @@ -113,9 +195,9 @@ export class ArrowProjectile extends Projectile { const dx = res * 640; const x = 480 - 32 - dx; if (this.direction === ProjectileDirection.Horizontal) { - this.setPosition(this.x, x); - } else { this.setPosition(x, this.y); + } else { + this.setPosition(this.x, x); } } else if (time > 5000) { this.destroy(); @@ -125,6 +207,9 @@ export class ArrowProjectile extends Projectile { render(canvas: MotaOffscreenCanvas2D, transform: Transform): void { const ctx = canvas.ctx; ctx.globalAlpha = 1; + const ratio = devicePixelRatio * core.domStyle.scale; + const cell = 32 * ratio; + ctx.save(); if (this.time < 3000) { let begin = 1; @@ -132,27 +217,39 @@ export class ArrowProjectile extends Projectile { begin = ArrowProjectile.dangerEasing!(this.time / 2000); } const len = begin * 13 * 32; + const fl = len * ratio; const x1 = 480 - 32 - len; + const fx1 = x1 * ratio; if (this.direction === ProjectileDirection.Horizontal) { const canvas = ArrowProjectile.horizontal!.canvas; - ctx.drawImage(canvas, x1, 0, len, 32, x1, this.y, len, 32); + ctx.drawImage(canvas, fx1, 0, fl, cell, x1, this.y, len, 32); } else { const canvas = ArrowProjectile.vertical!.canvas; - ctx.drawImage(canvas, 0, x1, 32, len, this.y, x1, 32, len); + ctx.drawImage(canvas, 0, fx1, cell, fl, this.x, x1, 32, len); } } else { - const len = Math.max(this.y - 32, 0); if (this.direction === ProjectileDirection.Horizontal) { + const len = Math.max(this.x - 32, 0); + const fl = len * ratio; const canvas = ArrowProjectile.horizontal!.canvas; - ctx.drawImage(canvas, 32, 0, len, 32, 32, this.y, len, 32); + ctx.drawImage(canvas, cell, 0, fl, cell, 32, this.y, len, 32); } else { + const len = Math.max(this.y - 32, 0); + const fl = len * ratio; const canvas = ArrowProjectile.vertical!.canvas; - ctx.drawImage(canvas, 0, 32, 32, len, this.y, 32, 32, len); + ctx.drawImage(canvas, 0, cell, cell, fl, this.x, 32, 32, len); } } const img = core.material.images.images['arrow.png']; - ctx.drawImage(img, this.x, this.y, 102, 32); + if (this.direction === ProjectileDirection.Vertical) { + ctx.translate(this.x + 32, this.y); + ctx.rotate(Math.PI / 2); + ctx.drawImage(img, 0, 0, 102, 32); + } else { + ctx.drawImage(img, this.x, this.y, 102, 32); + } + ctx.restore(); } } @@ -170,7 +267,8 @@ export class PortalProjectile extends Projectile { private transfered: boolean = false; private effect?: PointEffect; - private effectId?: number; + private effectId1?: number; + private effectId2?: number; static init() { this.easing = hyper('sin', 'out'); @@ -182,13 +280,20 @@ export class PortalProjectile extends Projectile { createEffect(effect: PointEffect) { this.effect = effect; - const id = effect.addEffect( + const id1 = effect.addEffect( PointEffectType.CircleWarpTangetial, Date.now(), 4000, - [this.tx * 32, this.ty * 32, 12, 20] + [this.tx * 32 + 16, this.ty * 32 + 16, 0, 32] ); - this.effectId = id; + const id2 = effect.addEffect( + PointEffectType.CircleContrast, + Date.now(), + 4000, + [this.tx * 32 + 16, this.ty * 32 + 16, 32, 24] + ); + this.effectId1 = id1; + this.effectId2 = id2; } /** @@ -225,16 +330,21 @@ export class PortalProjectile extends Projectile { render(canvas: MotaOffscreenCanvas2D, transform: Transform): void { const effect = this.effect; - const id = this.effectId; - if (!effect || isNil(id)) return; + const id1 = this.effectId1; + const id2 = this.effectId2; + if (!effect || isNil(id1) || isNil(id2)) return; const time = this.time; - const max = Math.PI * 8; + const max = Math.PI * 2; if (time < 2000) { const progress = PortalProjectile.easing!(time / 2000); - effect.setEffect(id, void 0, [0, max * progress, 0, 0]); + const ratio = Math.min(progress * 3, 1); + effect.setEffect(id1, void 0, [0, max * progress, 0, 0]); + effect.setEffect(id2, void 0, [ratio, 0, 0, 0]); } else { const progress = PortalProjectile.easing!((time - 2000) / 2000); - effect.setEffect(id, void 0, [max * progress, max, 0, 0]); + const ratio = Math.min((1 - progress) * 3, 1); + effect.setEffect(id1, void 0, [max * progress, max, 0, 0]); + effect.setEffect(id2, void 0, [ratio, 0, 0, 0]); } } } diff --git a/src/plugin/fx/pointShader.ts b/src/plugin/fx/pointShader.ts index c1e0231..a2cb45e 100644 --- a/src/plugin/fx/pointShader.ts +++ b/src/plugin/fx/pointShader.ts @@ -138,6 +138,7 @@ export class PointEffect { this.effectCount = itemCount; this.dataList = new Float32Array(itemCount * 16); this.transformed = new Float32Array(itemCount * 16); + this.transform = shader.transform; return program; } @@ -146,7 +147,9 @@ export class PointEffect { * 在下一帧更新特效数据 */ requestUpdate() { - this.needUpdateData = true; + if (this.dataList[0] !== PointEffectType.None) { + this.needUpdateData = true; + } if (this.shader) this.shader.update(this.shader); } @@ -369,6 +372,7 @@ export class PointEffect { transformed.set(list); this.transformData(); block.set(transformed); + this.needUpdateData = false; } }); } diff --git a/src/plugin/index.ts b/src/plugin/index.ts index d591b8d..0c4146c 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -7,6 +7,7 @@ import * as use from './use'; import * as gameCanvas from './fx/gameCanvas'; import * as animateController from './animateController'; import * as achievement from './ui/achievement'; +import * as boss from './boss'; import './loopMap'; Mota.Plugin.register('fly_r', fly); @@ -23,3 +24,4 @@ Mota.Plugin.register( ); Mota.Plugin.register('chase_r', chase); Mota.Plugin.register('achievement_r', achievement); +Mota.Plugin.register('boss_r', boss);