diff --git a/src/plugin/boss/barrage.ts b/src/plugin/boss/barrage.ts index 78a7070..c525a3a 100644 --- a/src/plugin/boss/barrage.ts +++ b/src/plugin/boss/barrage.ts @@ -414,6 +414,8 @@ export namespace Hitbox { const { x: cx, y: cy, radius: r } = circle; const { x, y, w, h } = rect; + if (cx > x && cx < x + w && cy > y && cy < y + h) return false; + // 找到圆心到矩形的最近点 const closestX = Math.max(x, Math.min(cx, x + w)); const closestY = Math.max(y, Math.min(cy, y + h)); diff --git a/src/plugin/boss/towerBoss.ts b/src/plugin/boss/towerBoss.ts index ad3b9bb..0090171 100644 --- a/src/plugin/boss/towerBoss.ts +++ b/src/plugin/boss/towerBoss.ts @@ -17,6 +17,8 @@ import { import { Container } from '@/core/render/container'; import { ArrowProjectile, + BoomProjectile, + ChainProjectile, IceProjectile, PortalProjectile, ProjectileDirection, @@ -513,6 +515,10 @@ export class TowerBoss extends BarrageBoss { core.setBlock(527, n, ny); core.setBlock(527, 15 - n, ny); } + core.setHeroLoc('x', 7); + core.setHeroLoc('y', 7); + core.setHeroLoc('direction', 'up'); + core.setBlock(557, 7, n + 1); } private aiDialogue2(time: number, frame: number) { @@ -523,21 +529,123 @@ export class TowerBoss extends BarrageBoss { this.skill7Time = 2; } - releaseSkill6() {} + releaseSkill6(n: number, last: number) { + const s = 13 - n * 2; + const x = Math.floor(Math.random() * s + n); + const y = Math.floor(Math.random() * s + n); + const proj = this.createProjectile(BoomProjectile, 0, 0); + proj.setData(x, y, last); + } - releaseSkill7() {} + async releaseSkill7(n: number) { + const count = Math.floor(Math.random() * 6 + 3); + const nodes: LocArr[] = []; + const s = 13 - n * 2; + const used = new Set(); + let i = 0; + while (i < count) { + const x = Math.floor(Math.random() * s + n); + const y = Math.floor(Math.random() * s + n); + const index = x + y * s; + if (used.has(index)) continue; + i++; + used.add(index); + nodes.push([x, y]); + if (nodes.length > 1) { + const [lx, ly] = nodes[i - 1]; + const proj = this.createProjectile(ChainProjectile, 0, 0); + proj.hitbox.setPoint1(lx, ly); + proj.hitbox.setPoint2(x, y); + } + await sleep(200); + } + } - private aiStage3(time: number, frame: number) {} + private aiStage3(time: number, frame: number) { + const skill6Release = this.skill6Time * this.skill6Interval; + const skill7Release = this.skill7Time * this.skill7Interval; + const attack = this.attackTime * this.attackInterval; - private aiStage4(time: number, frame: number) {} + if (time > skill6Release) { + this.releaseSkill6(2, 500); + this.skill6Time++; + } + if (time > skill7Release) { + this.releaseSkill7(2); + this.skill7Time++; + } + if (time > attack) { + this.addAttackCircle(3000, 500); + this.attackTime++; + } - private aiStage5(time: number, frame: number) {} + if (this.hp <= 2000) { + this.changeStage(TowerBossStage.Stage4, time); + this.terrainClose(2); + this.attackTime = 1; + this.skill6Time = 12; + this.skill6Interval = 400; + this.skill7Time = 1; + } + } + + private aiStage4(time: number, frame: number) { + const skill6Release = this.skill6Time * this.skill6Interval; + const skill7Release = this.skill7Time * this.skill7Interval; + const attack = this.attackTime * this.attackInterval; + + if (time > skill6Release) { + this.releaseSkill6(3, 500); + this.skill6Time++; + } + if (time > skill7Release) { + this.releaseSkill7(3); + this.skill7Time++; + } + if (time > attack) { + this.addAttackCircle(3000, 500); + this.attackTime++; + } + + if (this.hp <= 1000) { + this.changeStage(TowerBossStage.Stage5, time); + this.terrainClose(3); + this.attackTime = 1; + this.skill6Time = 17; + this.skill6Interval = 300; + this.skill7Time = 1; + } + } + + private aiStage5(time: number, frame: number) { + const skill6Release = this.skill6Time * this.skill6Interval; + const skill7Release = this.skill7Time * this.skill7Interval; + const attack = this.attackTime * this.attackInterval; + + if (time > skill6Release) { + this.releaseSkill6(4, 500); + this.skill6Time++; + } + if (time > skill7Release) { + this.releaseSkill7(4); + this.skill7Time++; + } + if (time > attack) { + this.addAttackCircle(3000, 500); + this.attackTime++; + } + + if (this.hp <= 1000) { + this.changeStage(TowerBossStage.End, time); + } + } private aiEnd(time: number, frame: number) {} } class BossEffect extends BossSprite { private attackCircle: AttackCircleRenderable[] = []; + private chainPath: LocArr[] = []; /** * 初始化 diff --git a/src/plugin/boss/towerBossProjectile.ts b/src/plugin/boss/towerBossProjectile.ts index 0872153..71f2eaa 100644 --- a/src/plugin/boss/towerBossProjectile.ts +++ b/src/plugin/boss/towerBossProjectile.ts @@ -124,6 +124,7 @@ export class ArrowProjectile extends Projectile { render(canvas: MotaOffscreenCanvas2D, transform: Transform): void { const ctx = canvas.ctx; + ctx.globalAlpha = 1; if (this.time < 3000) { let begin = 1; @@ -295,13 +296,9 @@ export class IceProjectile extends Projectile { render(canvas: MotaOffscreenCanvas2D, transform: Transform): void { const ctx = canvas.ctx; if (this.time < 2000) { - const fill = ctx.fillStyle; - const alpha = ctx.globalAlpha; ctx.fillStyle = 'rgb(150,150,255)'; ctx.globalAlpha = 0.6; ctx.fillRect(this.x + 2, this.y + 2, 28, 28); - ctx.fillStyle = fill; - ctx.globalAlpha = alpha; } else { if (!this.animated) { this.animated = true; @@ -683,24 +680,79 @@ export class BoomProjectile extends Projectile { damage: number = 3000; hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 32, 32); + private bx: number = 0; + private by: number = 0; + private last: number = 500; + + private damaged: boolean = false; + private animated: boolean = false; + + setData(x: number, y: number, last: number) { + this.bx = x; + this.by = y; + this.last = last; + this.setPosition(x * 32, y * 32); + } + isIntersect(hitbox: Hitbox.HitboxType): boolean { - throw new Error('Method not implemented.'); + if (this.time < this.last + 1000) return false; + if (this.damaged) return false; + if (hitbox instanceof Hitbox.Rect) { + return Hitbox.checkRectRect(this.hitbox, hitbox); + } else { + return false; + } } updateHitbox(x: number, y: number): void { - throw new Error('Method not implemented.'); + this.hitbox.setPosition(x, y); } doDamage(target: IStateDamageable): boolean { - throw new Error('Method not implemented.'); + if (this.damaged) return false; + target.hp -= this.damage; + this.damaged = true; + return true; } ai(boss: TowerBoss, time: number, frame: number): void { - throw new Error('Method not implemented.'); + if (!this.animated && time > this.last + 1000) { + core.drawAnimate('explosion1', this.bx, this.by); + } + if (time > this.last + 1100) { + this.destroy(); + } } render(canvas: MotaOffscreenCanvas2D, transform: Transform): void { - throw new Error('Method not implemented.'); + const ctx = canvas.ctx; + const end = this.last + 1000; + const r = 12; + const mr = 27; + if (this.time < end) { + const angle = this.time / 30; + const sin = Math.sin(angle); + const cos = Math.cos(angle); + ctx.fillStyle = 'rgb(255,50,50)'; + ctx.strokeStyle = 'rgb(255,50,50)'; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(this.x + r * cos, this.y + r * sin); + ctx.lineTo(this.x + mr * cos, this.y + mr * sin); + ctx.moveTo(this.x - r * cos, this.y - r * sin); + ctx.lineTo(this.x - mr * cos, this.y - mr * sin); + ctx.arc(this.x, this.y, r, 0, Math.PI * 2); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(this.x, this.y, 2, 0, Math.PI * 2); + ctx.fill(); + } + if (this.time > end - 500) { + const dt = this.time - end + 500; + const pos = this.y - (1 - dt / 500) * 480; + const img = core.material.images.images['boom.png']; + ctx.drawImage(img, this.x - 16, pos - 80, 36, 80); + } } } @@ -708,23 +760,53 @@ export class ChainProjectile extends Projectile { damage: number = 4000; hitbox: Hitbox.Line = new Hitbox.Line(0, 0, 0, 0); + private damaged: boolean = false; + isIntersect(hitbox: Hitbox.HitboxType): boolean { - throw new Error('Method not implemented.'); + if (this.time < 1000) return false; + if (this.damaged) return false; + if (hitbox instanceof Hitbox.Rect) { + return Hitbox.checkLineRect(this.hitbox, hitbox); + } else { + return false; + } } updateHitbox(x: number, y: number): void { - throw new Error('Method not implemented.'); + this.hitbox.setPoint1(x, y); } doDamage(target: IStateDamageable): boolean { - throw new Error('Method not implemented.'); + if (this.damaged) return false; + target.hp -= this.damage; + this.damaged = true; + return true; } ai(boss: TowerBoss, time: number, frame: number): void { - throw new Error('Method not implemented.'); + if (time > 2000) { + this.destroy(); + } } render(canvas: MotaOffscreenCanvas2D, transform: Transform): void { - throw new Error('Method not implemented.'); + const ctx = canvas.ctx; + ctx.beginPath(); + ctx.moveTo(this.hitbox.x1, this.hitbox.y1); + ctx.lineTo(this.hitbox.x2, this.hitbox.y2); + + if (this.time < 1000) { + ctx.globalAlpha = 0.6; + ctx.strokeStyle = 'rgb(220,100,255)'; + ctx.stroke(); + } else { + ctx.strokeStyle = '#fff'; + ctx.shadowBlur = 3; + ctx.shadowColor = '#62c8f4'; + ctx.globalAlpha = 0.6; + ctx.stroke(); + ctx.shadowBlur = 0; + ctx.shadowColor = ''; + } } }