Skip to content

Commit

Permalink
Particles refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
neki-dev committed Nov 3, 2023
1 parent 8fd6101 commit b6bfdcb
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 281 deletions.
301 changes: 301 additions & 0 deletions src/game/scenes/world/effects/particles-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
import { ENEMY_SIZE_PARAMS, ENEMY_TEXTURE_SIZE } from '~const/world/entities/enemy';
import { Environment } from '~lib/environment';
import { GameFlag, GameSettings } from '~type/game';
import { IWorld } from '~type/world';
import { IParticlesParent, ParticlesTexture } from '~type/world/effects';
import { IParticlesManager } from '~type/world/effects/particles-manager';
import { IBuilding } from '~type/world/entities/building';
import { INPC } from '~type/world/entities/npc';
import { EnemyTexture, IEnemy } from '~type/world/entities/npc/enemy';
import { IPlayer } from '~type/world/entities/player';
import { ISprite } from '~type/world/entities/sprite';
import { PositionAtWorld } from '~type/world/level';

import { Particles } from './particles';

export class ParticlesManager implements IParticlesManager {
private scene: IWorld;

constructor(scene: IWorld) {
this.scene = scene;
}

public createDustEffect(parent: IPlayer) {
if (!this.isEffectsEnabled()) {
return null;
}

return new Particles(parent, {
key: 'dust',
texture: ParticlesTexture.BIT,
dynamic: true,
params: {
followOffset: {
x: 0,
y: -parent.gamut * parent.scaleY * 0.5,
},
lifespan: { min: 150, max: 300 },
scale: 0.6,
speed: 10,
frequency: 150,
alpha: { start: 1.0, end: 0.0 },
emitting: false,
},
});
}

public createBloodEffect(parent: ISprite) {
if (
!parent.active
|| !Environment.GetFlag(GameFlag.BLOOD)
|| !this.isEffectsEnabled()
|| ParticlesManager.IsExist(parent, 'blood')
) {
return null;
}

const scale = Math.min(2.0, parent.displayWidth / 22);

return new Particles(parent, {
key: 'blood',
texture: ParticlesTexture.BIT_SOFT,
dynamic: true,
params: {
duration: 250,
followOffset: parent.getBodyOffset(),
lifespan: { min: 100, max: 250 },
scale: { start: scale, end: scale * 0.25 },
speed: 60,
maxAliveParticles: 6,
tint: 0xdd1e1e,
},
});
}

public createFrozeEffect(parent: INPC) {
if (
!parent.active
|| !this.isEffectsEnabled()
|| ParticlesManager.IsExist(parent, 'froze')
) {
return null;
}

const lifespan = Math.min(400, parent.displayWidth * 8);

return new Particles(parent, {
key: 'froze',
texture: ParticlesTexture.BIT_SOFT,
dynamic: true,
params: {
duration: lifespan,
followOffset: parent.getBodyOffset(),
color: [0xffffff, 0x8cf9ff, 0x00f2ff],
colorEase: 'quad.out',
lifespan: { min: lifespan / 2, max: lifespan },
scale: { start: 1.0, end: 0.5 },
speed: 80,
},
});
}

public createFireEffect(parent: IEnemy) {
if (
!parent.active
|| !this.isEffectsEnabled()
|| ParticlesManager.IsExist(parent, 'fire')
) {
return null;
}

const lifespan = parent.displayWidth * 6;
const scale = Math.min(2.0, parent.displayWidth / 22);

return new Particles(parent, {
key: 'fire',
texture: ParticlesTexture.BIT_SOFT,
dynamic: true,
params: {
duration: lifespan,
followOffset: parent.getBodyOffset(),
color: [0xfacc22, 0xf89800, 0xf83600, 0x9f0404],
colorEase: 'quad.out',
lifespan: { min: lifespan / 2, max: lifespan },
scale: { start: scale, end: scale * 0.2 },
alpha: { start: 1.0, end: 0.0 },
speed: 80,
},
});
}

public createLongFireEffect(parent: IEnemy, params: { duration: number }) {
if (!parent.active || !this.isEffectsEnabled()) {
return null;
}

const lifespan = parent.displayWidth * 25;

return new Particles(parent, {
key: 'long-fire',
texture: ParticlesTexture.BIT_SOFT,
dynamic: true,
params: {
followOffset: parent.getBodyOffset(),
duration: params.duration,
color: [0xfacc22, 0xf89800, 0xf83600, 0x9f0404],
colorEase: 'quad.out',
lifespan: { min: lifespan / 2, max: lifespan },
alpha: { start: 1.0, end: 0.0 },
angle: { min: -100, max: -80 },
scale: {
start: parent.displayWidth / 20,
end: 1.0,
ease: 'sine.out',
},
speed: 40,
advance: 10,
},
});
}

public createLazerEffect(parent: IEnemy) {
if (
!parent.active
|| !this.isEffectsEnabled()
|| ParticlesManager.IsExist(parent, 'lazer')
) {
return null;
}

const lifespan = parent.displayWidth * 5;
const scale = Math.min(2.25, parent.displayWidth / 18);

return new Particles(parent, {
key: 'lazer',
texture: ParticlesTexture.BIT_SOFT,
dynamic: true,
params: {
duration: lifespan,
followOffset: parent.getBodyOffset(),
lifespan: { min: lifespan / 2, max: lifespan },
scale: { start: scale, end: scale * 0.2 },
alpha: { start: 1.0, end: 0.0 },
speed: 80,
tint: 0xb136ff,
},
});
}

public createGlowEffect(parent: IParticlesParent, params: { speed: number; color: number }) {
if (!parent.active || !this.isEffectsEnabled()) {
return null;
}

return new Particles(parent, {
key: 'glow',
texture: ParticlesTexture.GLOW,
dynamic: true,
params: {
scale: 0.2 * parent.scale,
alpha: { start: 1.0, end: 0.0 },
lifespan: 20000 / params.speed,
frequency: 10000 / params.speed,
tint: params.color,
blendMode: 'ADD',
},
});
}

public createSpawnEffect(parent: IEnemy) {
if (!this.isEffectsEnabled()) {
return null;
}

// Native body.center isn't working at current state
const size = ENEMY_SIZE_PARAMS[ENEMY_TEXTURE_SIZE[parent.texture.key as EnemyTexture]];
const position: PositionAtWorld = {
x: parent.x,
y: parent.y - size.height / 2,
};
const duration = Math.min(700, parent.displayHeight * 17);
const scale = parent.displayWidth / 16;

return new Particles(parent, {
key: 'spawn',
texture: ParticlesTexture.BIT_SOFT,
position,
params: {
duration,
lifespan: { min: duration / 2, max: duration },
scale: { start: scale, end: scale / 2 },
alpha: { start: 1.0, end: 0.0 },
speed: 40,
quantity: 1,
tint: 0x000000,
},
});
}

public createHealEffect(parent: ISprite, params: { duration: number }) {
if (
!this.isEffectsEnabled()
|| ParticlesManager.IsExist(parent, 'heal')
) {
return null;
}

return new Particles(parent, {
key: 'heal',
texture: ParticlesTexture.PLUS,
dynamic: true,
params: {
followOffset: {
x: 0,
y: -parent.displayHeight,
},
duration: params.duration,
lifespan: params.duration,
alpha: { start: 1.0, end: 0.0 },
angle: {
min: -110,
max: -70,
},
scale: {
start: 1.0,
end: 0.5,
},
speed: 20,
maxAliveParticles: 1,
},
});
}

public createGenerationEffect(parent: IBuilding) {
if (!this.isEffectsEnabled()) {
return null;
}

return new Particles(parent, {
key: 'generate',
texture: ParticlesTexture.BIT,
position: parent.getTopFace(),
params: {
duration: 300,
lifespan: { min: 100, max: 200 },
scale: { start: 1.0, end: 0.5 },
alpha: { start: 1.0, end: 0.0 },
speed: 60,
maxAliveParticles: 8,
tint: 0x2dffb2,
},
});
}

private isEffectsEnabled() {
return this.scene.game.isSettingEnabled(GameSettings.EFFECTS);
}

static IsExist(parent: IParticlesParent, key: string) {
return Boolean(parent.effects?.[key]);
}
}
12 changes: 3 additions & 9 deletions src/game/scenes/world/effects/particles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { v4 as uuidv4 } from 'uuid';

import { WORLD_DEPTH_EFFECT } from '~const/world';
import { Assets } from '~lib/assets';
import { IWorld } from '~type/world';
Expand All @@ -24,21 +22,17 @@ export class Particles implements IParticles {
constructor(
parent: IParticlesParent,
{
key, position, texture, params, dynamic, replay = false,
key, position, texture, params, dynamic,
}: ParticlesData,
) {
this.scene = parent.scene;
this.parent = parent;
this.key = key ?? uuidv4();
this.key = key;

if (!this.parent.effects) {
this.parent.effects = {};
} else if (this.parent.effects[this.key]) {
if (replay) {
this.parent.effects[this.key].destroy();
} else {
return;
}
this.parent.effects[this.key].destroy();
}

this.parent.effects[this.key] = this;
Expand Down
21 changes: 1 addition & 20 deletions src/game/scenes/world/entities/building/variants/generator.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { DIFFICULTY } from '~const/world/difficulty';
import { Building } from '~entity/building';
import { Tutorial } from '~lib/tutorial';
import { Particles } from '~scene/world/effects';
import { GameSettings } from '~type/game';
import { LangPhrase } from '~type/lang';
import { TutorialStep } from '~type/tutorial';
import { IWorld } from '~type/world';
import { ParticlesTexture } from '~type/world/effects';
import {
BuildingTexture,
BuildingVariant,
Expand Down Expand Up @@ -73,22 +70,6 @@ export class BuildingGenerator extends Building {

private generateResource() {
this.scene.player.giveResources(1);

if (this.scene.game.isSettingEnabled(GameSettings.EFFECTS)) {
new Particles(this, {
key: 'generate',
texture: ParticlesTexture.BIT,
position: this.getTopFace(),
params: {
duration: 300,
lifespan: { min: 100, max: 200 },
scale: { start: 1.0, end: 0.5 },
alpha: { start: 1.0, end: 0.0 },
speed: 60,
maxAliveParticles: 8,
tint: 0x2dffb2,
},
});
}
this.scene.particles.createGenerationEffect(this);
}
}
Loading

0 comments on commit b6bfdcb

Please sign in to comment.