494
									
								
								packages/api/src/particle.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										494
									
								
								packages/api/src/particle.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,494 @@
 | 
			
		||||
import { Autowired, injectable } from '@ccms/container'
 | 
			
		||||
 | 
			
		||||
import { task, plugin } from './index'
 | 
			
		||||
 | 
			
		||||
const UUID = Java.type('java.util.UUID')
 | 
			
		||||
const Math = Java.type('java.lang.Math')
 | 
			
		||||
 | 
			
		||||
export namespace particle {
 | 
			
		||||
    /**
 | 
			
		||||
     * 表示一个特效对象
 | 
			
		||||
     *
 | 
			
		||||
     * @author Zoyn
 | 
			
		||||
     */
 | 
			
		||||
    export abstract class Particle {
 | 
			
		||||
        private spawner: ParticleSpawner
 | 
			
		||||
        private readonly uuid: string
 | 
			
		||||
 | 
			
		||||
        private particle: any /* Particle */
 | 
			
		||||
        private count: number = 1;
 | 
			
		||||
        private offsetX: number = 0;
 | 
			
		||||
        private offsetY: number = 0;
 | 
			
		||||
        private offsetZ: number = 0;
 | 
			
		||||
        private extra: number = 0;
 | 
			
		||||
        private data: Object = null;
 | 
			
		||||
 | 
			
		||||
        constructor() {
 | 
			
		||||
            this.uuid = UUID.randomUUID().toString()
 | 
			
		||||
        }
 | 
			
		||||
        abstract show(location: any)
 | 
			
		||||
 | 
			
		||||
        getUUID() {
 | 
			
		||||
            return this.uuid
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getSpawner() {
 | 
			
		||||
            return this.spawner
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setSpawner(spawner: ParticleSpawner) {
 | 
			
		||||
            this.spawner = spawner
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getParticle() {
 | 
			
		||||
            return this.particle
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setParticle(particle: any, data: any = null) {
 | 
			
		||||
            this.particle = particle
 | 
			
		||||
            this.data = data
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getCount() {
 | 
			
		||||
            return this.count
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setCount(count: number) {
 | 
			
		||||
            this.count = count
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getOffsetX() {
 | 
			
		||||
            return this.offsetX
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setOffsetX(offsetX: number) {
 | 
			
		||||
            this.offsetX = offsetX
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getOffsetY() {
 | 
			
		||||
            return this.offsetY
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setOffsetY(offsetY: number) {
 | 
			
		||||
            this.offsetY = offsetY
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getOffsetZ() {
 | 
			
		||||
            return this.offsetZ
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setOffsetZ(offsetZ: number) {
 | 
			
		||||
            this.offsetZ = offsetZ
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getExtra() {
 | 
			
		||||
            return this.extra
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setExtra(extra: number) {
 | 
			
		||||
            this.extra = extra
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getData() {
 | 
			
		||||
            return this.data
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setData(data) {
 | 
			
		||||
            this.data = data
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 通过给定一个坐标就可以使用已经指定的参数来播放粒子
 | 
			
		||||
         *
 | 
			
		||||
         * @param location 坐标
 | 
			
		||||
         */
 | 
			
		||||
        spawn(location: any) {
 | 
			
		||||
            if (!this.spawner) throw new Error(`particle ${this.uuid} not set spawner can't spawn!`)
 | 
			
		||||
            this.spawner.spawn(location, this)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 表示一条线
 | 
			
		||||
     *
 | 
			
		||||
     * @author Zoyn
 | 
			
		||||
     */
 | 
			
		||||
    export class Line extends Particle {
 | 
			
		||||
        private vector: any
 | 
			
		||||
        private start: any
 | 
			
		||||
        private end: any
 | 
			
		||||
        /**
 | 
			
		||||
         * 步长
 | 
			
		||||
         */
 | 
			
		||||
        private step: number
 | 
			
		||||
        /**
 | 
			
		||||
         * 向量长度
 | 
			
		||||
         */
 | 
			
		||||
        private length: number
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 构造一条线
 | 
			
		||||
         *
 | 
			
		||||
         * @param start 线的起点
 | 
			
		||||
         * @param end   线的终点
 | 
			
		||||
         */
 | 
			
		||||
        constructor(start: any, end: any)
 | 
			
		||||
        /**
 | 
			
		||||
         * 构造一条线
 | 
			
		||||
         *
 | 
			
		||||
         * @param start  线的起点
 | 
			
		||||
         * @param end    线的终点
 | 
			
		||||
         * @param step   每个粒子之间的间隔 (也即步长)
 | 
			
		||||
         */
 | 
			
		||||
        constructor(start: any, end: any, step: number = 0.1) {
 | 
			
		||||
            super()
 | 
			
		||||
            this.start = start
 | 
			
		||||
            this.end = end
 | 
			
		||||
            this.step = step
 | 
			
		||||
            // 对向量进行重置
 | 
			
		||||
            this.resetVector()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        show() {
 | 
			
		||||
            for (let i = 0; i < this.length; i += this.step) {
 | 
			
		||||
                let vectorTemp = this.vector.clone().multiply(i)
 | 
			
		||||
                this.spawn(this.start.clone().add(vectorTemp))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 获取线的起始坐标
 | 
			
		||||
         *
 | 
			
		||||
         * @return {@link Location}
 | 
			
		||||
         */
 | 
			
		||||
        getStart() {
 | 
			
		||||
            return this.start
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 利用给定的坐标设置线的起始坐标
 | 
			
		||||
         *
 | 
			
		||||
         * @param start 起始坐标
 | 
			
		||||
         * @return {@link Line}
 | 
			
		||||
         */
 | 
			
		||||
        setStart(start) {
 | 
			
		||||
            this.start = start
 | 
			
		||||
            this.resetVector()
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 获取线的终点坐标
 | 
			
		||||
         *
 | 
			
		||||
         * @return {@link Location}
 | 
			
		||||
         */
 | 
			
		||||
        getEnd() {
 | 
			
		||||
            return this.end
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 利用给定的坐标设置线的终点坐标
 | 
			
		||||
         *
 | 
			
		||||
         * @param end 终点
 | 
			
		||||
         * @return {@link Line}
 | 
			
		||||
         */
 | 
			
		||||
        setEnd(end) {
 | 
			
		||||
            this.end = end
 | 
			
		||||
            this.resetVector()
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 获取每个粒子之间的间隔
 | 
			
		||||
         *
 | 
			
		||||
         * @return 也就是循环的步长
 | 
			
		||||
         */
 | 
			
		||||
        getStep() {
 | 
			
		||||
            return this.step
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 设置每个粒子之间的间隔
 | 
			
		||||
         *
 | 
			
		||||
         * @param step 间隔
 | 
			
		||||
         * @return {@link Line}
 | 
			
		||||
         */
 | 
			
		||||
        setStep(step) {
 | 
			
		||||
            this.step = step
 | 
			
		||||
            this.resetVector()
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 手动重设线的向量
 | 
			
		||||
         */
 | 
			
		||||
        resetVector() {
 | 
			
		||||
            this.vector = this.end.clone().subtract(this.start).toVector()
 | 
			
		||||
            this.length = this.vector.length()
 | 
			
		||||
            this.vector.normalize()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static buildLine(locA: any, locB: any, step: number, particle: any) {
 | 
			
		||||
            let vectorAB = locB.clone().subtract(locA).toVector()
 | 
			
		||||
            let vectorLength = vectorAB.length()
 | 
			
		||||
            vectorAB.normalize()
 | 
			
		||||
            for (let i = 0; i < vectorLength; i += step) {
 | 
			
		||||
                ParticleManager.globalSpawner.spawn(locA.clone().add(vectorAB.clone().multiply(i)), particle)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 表示一个弧
 | 
			
		||||
     *
 | 
			
		||||
     * @author Zoyn
 | 
			
		||||
     */
 | 
			
		||||
    export class Arc extends Particle {
 | 
			
		||||
        private angle: number
 | 
			
		||||
        private radius: number
 | 
			
		||||
        private step: number
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 构造一个弧
 | 
			
		||||
         *
 | 
			
		||||
         * @param origin 弧所在的圆的圆点
 | 
			
		||||
         * @param angle  弧所占的角度
 | 
			
		||||
         * @param radius 弧所在的圆的半径
 | 
			
		||||
         * @param step   每个粒子的间隔(也即步长)
 | 
			
		||||
         */
 | 
			
		||||
        constructor(angle: number = 30, radius: number = 1, step: number = 1) {
 | 
			
		||||
            super()
 | 
			
		||||
            this.angle = angle
 | 
			
		||||
            this.radius = radius
 | 
			
		||||
            this.step = step
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        show(location: any) {
 | 
			
		||||
            for (let i = 0; i < this.angle; i += this.step) {
 | 
			
		||||
                let radians: number = Math.toRadians(i)
 | 
			
		||||
                let x: number = this.radius * Math.cos(radians)
 | 
			
		||||
                let z: number = this.radius * Math.sin(radians)
 | 
			
		||||
 | 
			
		||||
                super.spawn(location.clone().add(x, 0, z))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getAngle(): number {
 | 
			
		||||
            return this.angle
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setAngle(angle: number): Arc {
 | 
			
		||||
            this.angle = angle
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getRadius(): number {
 | 
			
		||||
            return this.radius
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setRadius(radius: number): Arc {
 | 
			
		||||
            this.radius = radius
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getStep(): number {
 | 
			
		||||
            return this.step
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setStep(step: number): Arc {
 | 
			
		||||
            this.step = step
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 表示一个圆
 | 
			
		||||
     *
 | 
			
		||||
     * @author Zoyn
 | 
			
		||||
     */
 | 
			
		||||
    export class Circle extends Arc {
 | 
			
		||||
        constructor(radius: number)
 | 
			
		||||
        constructor(radius: number, step: number)
 | 
			
		||||
        /**
 | 
			
		||||
         * 构造一个圆
 | 
			
		||||
         *
 | 
			
		||||
         * @param origin 圆的圆点
 | 
			
		||||
         * @param radius 圆的半径
 | 
			
		||||
         * @param step   每个粒子的间隔(也即步长)
 | 
			
		||||
         * @param period 特效周期(如果需要可以使用)
 | 
			
		||||
         */
 | 
			
		||||
        constructor(radius: number = 1, step: number = 1) {
 | 
			
		||||
            // Circle只需要控制这个fullArc就可以满足所有的要求
 | 
			
		||||
            super(360, radius, step)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger")
 | 
			
		||||
 | 
			
		||||
    @injectable()
 | 
			
		||||
    export abstract class ParticleManager {
 | 
			
		||||
        public static globalSpawner: ParticleSpawner = undefined
 | 
			
		||||
        @Autowired()
 | 
			
		||||
        private taskManager: task.TaskManager
 | 
			
		||||
 | 
			
		||||
        protected taskId: java.util.concurrent.atomic.AtomicInteger
 | 
			
		||||
        protected cacheTasks = new Map<string, ParticleTask>()
 | 
			
		||||
        protected pluginCacheTasks = new Map<string, Map<number, ParticleTask>>()
 | 
			
		||||
 | 
			
		||||
        constructor() {
 | 
			
		||||
            this.taskId = new AtomicInteger(0)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 获得自增的任务ID
 | 
			
		||||
         */
 | 
			
		||||
        public nextId() {
 | 
			
		||||
            return this.taskId.incrementAndGet()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public getTaskManager() {
 | 
			
		||||
            return this.taskManager
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public create(particle: Particle, plugin?: plugin.Plugin) {
 | 
			
		||||
            let uuid = particle.getUUID()
 | 
			
		||||
            if (this.cacheTasks.has(uuid)) {
 | 
			
		||||
                return this.cacheTasks.get(uuid)
 | 
			
		||||
            }
 | 
			
		||||
            let task = this.create0(plugin, particle)
 | 
			
		||||
            this.cacheTasks.set(uuid, task)
 | 
			
		||||
            if (plugin) {
 | 
			
		||||
                if (!this.pluginCacheTasks.has(plugin.description.name)) {
 | 
			
		||||
                    this.pluginCacheTasks.set(plugin.description.name, new Map())
 | 
			
		||||
                }
 | 
			
		||||
                this.pluginCacheTasks.get(plugin.description.name).set(task.getTaskId(), task)
 | 
			
		||||
            }
 | 
			
		||||
            return task
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public cancel(particle: Particle) {
 | 
			
		||||
            let uuid = particle.getUUID()
 | 
			
		||||
            if (this.cacheTasks.has(uuid)) {
 | 
			
		||||
                this.cacheTasks.get(uuid).cancel()
 | 
			
		||||
                this.cacheTasks.delete(uuid)
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new Error(`particle ${uuid} not found!`)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        disable(plugin: plugin.Plugin) {
 | 
			
		||||
            if (this.pluginCacheTasks.has(plugin.description.name)) {
 | 
			
		||||
                this.pluginCacheTasks
 | 
			
		||||
                    .get(plugin.description.name)
 | 
			
		||||
                    .forEach((task) => task.cancel())
 | 
			
		||||
                this.pluginCacheTasks.delete(plugin.description.name)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        protected create0(owner: plugin.Plugin, particle: Particle): ParticleTask {
 | 
			
		||||
            particle.setSpawner(this.getGlobalSpawner())
 | 
			
		||||
            return new ParticleTask(owner, particle, this)
 | 
			
		||||
        }
 | 
			
		||||
        protected abstract getGlobalSpawner(): ParticleSpawner
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export class ParticleTask {
 | 
			
		||||
 | 
			
		||||
        private particle: Particle
 | 
			
		||||
        private isAsync: boolean = false
 | 
			
		||||
        private interval: number = 0
 | 
			
		||||
        private _location: any
 | 
			
		||||
        private _follow: any
 | 
			
		||||
 | 
			
		||||
        private owner: plugin.Plugin
 | 
			
		||||
        private taskId: number
 | 
			
		||||
        private task: task.Task
 | 
			
		||||
        private taskManager: task.TaskManager
 | 
			
		||||
        protected particleManager: ParticleManager
 | 
			
		||||
 | 
			
		||||
        constructor(owner: plugin.Plugin, particle: Particle, particleManager: ParticleManager) {
 | 
			
		||||
            this.owner = owner
 | 
			
		||||
            this.taskId = particleManager.nextId()
 | 
			
		||||
            this.particle = particle
 | 
			
		||||
            this.taskManager = particleManager.getTaskManager()
 | 
			
		||||
            this.particleManager = particleManager
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getOwner() {
 | 
			
		||||
            return this.task.getOwner()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getTaskId() {
 | 
			
		||||
            return this.taskId
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getParticle() {
 | 
			
		||||
            return this.particle
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async(isAsync: boolean = true) {
 | 
			
		||||
            this.isAsync = isAsync
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        timer(tick: number) {
 | 
			
		||||
            this.interval = tick
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        follow(entity: { getLocation: () => any }) {
 | 
			
		||||
            this._follow = entity
 | 
			
		||||
            this._location = undefined
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        location(location: any) {
 | 
			
		||||
            this._location = location
 | 
			
		||||
            this._follow = undefined
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        submit() {
 | 
			
		||||
            this.cancel()
 | 
			
		||||
            if (this._follow && !this.interval) throw new Error(`enable follow entity but interval is ${this.interval}!`)
 | 
			
		||||
            this.taskManager.create(() => {
 | 
			
		||||
                this.task = this.taskManager
 | 
			
		||||
                    .create(() => {
 | 
			
		||||
                        try {
 | 
			
		||||
                            if (this._follow) {
 | 
			
		||||
                                if (!this._follow.isOnline()) return this.cancel()
 | 
			
		||||
                                this.particle.show(this._follow.getLocation().clone().add(0, 1, 0))
 | 
			
		||||
                            } else if (this._location) {
 | 
			
		||||
                                this.particle.show(this._location)
 | 
			
		||||
                            } else {
 | 
			
		||||
                                console.warn(`ParticleTask ${this.taskId} particle ${this.particle.getUUID()} cancel because entity and location both undefined!`)
 | 
			
		||||
                                this.task.cancel()
 | 
			
		||||
                            }
 | 
			
		||||
                        } catch (error) {
 | 
			
		||||
                            console.error(`§6插件 §a${this.owner.description.name} §c播放粒子发送异常 §4粒子播放任务已终止!`)
 | 
			
		||||
                            console.ex(error)
 | 
			
		||||
                            this.cancel()
 | 
			
		||||
                        }
 | 
			
		||||
                    }, this.owner)
 | 
			
		||||
                    .async(this.isAsync)
 | 
			
		||||
                    .timer(this.interval)
 | 
			
		||||
                    .submit()
 | 
			
		||||
            }, this.owner).later(2).submit()
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cancel() {
 | 
			
		||||
            if (this.task != null) {
 | 
			
		||||
                this.task.cancel()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export abstract class ParticleSpawner {
 | 
			
		||||
        abstract spawnParticle(location: any, particle: any, count: number)
 | 
			
		||||
        abstract spawn(location: any, particle: Particle)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user