feat: 智慧塔弹幕战最后一个阶段

This commit is contained in:
unanmed 2024-11-17 14:33:14 +08:00
parent b353eba55c
commit 3202cffd5d
3 changed files with 211 additions and 19 deletions

View File

@ -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));

View File

@ -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<number>();
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<TowerBoss> {
private attackCircle: AttackCircleRenderable[] = [];
private chainPath: LocArr[] = [];
/**
*

View File

@ -124,6 +124,7 @@ export class ArrowProjectile extends Projectile<TowerBoss> {
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<TowerBoss> {
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<TowerBoss> {
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<TowerBoss> {
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 = '';
}
}
}