-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathLuminousPulse.java
More file actions
232 lines (201 loc) · 8.12 KB
/
Copy pathLuminousPulse.java
File metadata and controls
232 lines (201 loc) · 8.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
* Authors: Jerry Li & Victor Jiang
* Date: June 13, 2025
* Description: This class operates the Luminous Pulse skill with visuals and radial damage. It also implements the Solar Fare
* super skill which stuns and damages all enemies on the screen every few seconds.
*/
import java.awt.*;
import java.awt.geom.*;
import java.util.List;
import java.util.Random;
public class LuminousPulse extends Skill {
public static final Color COL_CORE_INNER = new Color(40, 220, 255, 255);
public static final Color COL_CORE_OUTER = new Color(80, 180, 255, 255);
// Animation state
public float idlePhase;
public float intensityPhase;
public long lastPulse;
public boolean isSolarFlare = false;
public float solarFlareTimer = 0f;
public float solarFlareCooldown = 6f;
public int solarFlareDamage = 200;
public float flashAlpha = 0f;
public float flashFadeRate = 1f; // fade slightly slower for visibility
// Stats (populated via levelUp)
public int damage;
public float radius;
public int cooldown; // milliseconds
public int level = 0;
// Visual effect collection
public Player owner;
public Random rng = new Random();
public LuminousPulse(Player owner) {
super("luminous_pulse", owner);
this.owner = owner;
}
@Override
public void levelUp() {
SkillLevel data;
level++;
data = levels.get(level - 1);
this.damage = data.getDamage();
this.radius = data.getRadius();
this.cooldown = data.getCooldown();
lastPulse = System.currentTimeMillis();
if (data.isSolarFlare()) {
isSolarFlare = true;
solarFlareDamage = data.getSolarFlareDamage();
solarFlareCooldown = data.getSolarFlareCooldown();
solarFlareTimer = 0f;
}
}
public void update(float dt, List<Enemy> enemies) {
long now;
if (level == 0) {
return; // Skill not yet unlocked
}
// Update animation phases with more subtle, consistent speeds
idlePhase = (idlePhase + dt * 2.0f) % (float) (Math.PI * 2);
intensityPhase = (intensityPhase + dt * 3.5f) % (float) (Math.PI * 2);
now = System.currentTimeMillis();
if (now - lastPulse >= cooldown) {
applyDamage(owner.parent.spatialGrid);
lastPulse = now;
}
if (isSolarFlare) {
solarFlareTimer += dt;
if (solarFlareTimer >= solarFlareCooldown) {
triggerSolarFlare();
solarFlareTimer = 0f;
}
if (flashAlpha > 0f) {
flashAlpha -= dt * flashFadeRate;
if (flashAlpha < 0f)
flashAlpha = 0f;
}
}
}
public void applyDamage(SpatialHashGrid<Entity> grid) {
List<Entity> nearby = null;
int i = 0;
Entity ent = null;
nearby = grid.queryNearby((float) owner.getCenterX(), (float) owner.getCenterY(), radius);
for (i = 0; i < nearby.size(); i++) {
ent = nearby.get(i);
if (ent instanceof Enemy enemy && !enemy.isDead()) {
enemy.takeDamage(damage);
}
}
}
@Override
public void draw(Graphics2D g) {
Composite originalComposite = g.getComposite();
Stroke originalStroke = g.getStroke();
// Draw in proper order from bottom to top layers
drawPulseField(g); // Base field (actual damage radius)
drawDamageRadiusIndicator(g); // Clear indicator of damage radius
drawCoreAura(g); // Inner aura effect
if (isSolarFlare) {
drawFlash(g); // Full-screen flash effect
}
// Restore original graphics state
g.setComposite(originalComposite);
g.setStroke(originalStroke);
}
public void drawFlash(Graphics2D g) {
Composite orig;
if (flashAlpha > 0f) {
orig = g.getComposite();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, flashAlpha));
g.setColor(Color.WHITE);
g.fillRect(0, 0, GamePanel.GAME_WIDTH, GamePanel.GAME_HEIGHT);
g.setComposite(orig);
}
}
/**
* Draws a subtle but clear indicator of the exact damage radius
* to help players understand the skill's range accurately.
*/
public void drawDamageRadiusIndicator(Graphics2D g) {
float centerX = (float) owner.getCenterX();
float centerY = (float) owner.getCenterY();
Stroke damageRadiusStroke;
// Create a thin, subtle outline exactly at the damage radius
g.setComposite(AlphaComposite.SrcOver.derive(0.3f));
damageRadiusStroke = new BasicStroke(1.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND,
1.0f, new float[] { 5f, 5f }, 0f);
g.setStroke(damageRadiusStroke);
g.setColor(new Color(160, 220, 255, 150));
// Draw the exact damage radius circle
g.drawOval((int) (centerX - radius), (int) (centerY - radius),
(int) (radius * 2), (int) (radius * 2));
}
public void drawCoreAura(Graphics2D g) {
float centerX = (float) owner.getCenterX();
float centerY = (float) owner.getCenterY();
// Pulsing core aura with dynamic intensity - using a percentage of the actual
// radius
float pulseIntensity = 0.3f * (float) Math.sin(idlePhase) + 0.7f;
float breathe = 0.15f * (float) Math.sin(intensityPhase * 0.7f) + 0.85f;
// Core aura should be smaller than the actual damage radius (about 60% of it)
float finalRadius = radius * 0.6f * pulseIntensity * breathe;
int coreRadius;
// Multi-layered core effect
g.setComposite(AlphaComposite.SrcOver.derive(0.4f));
g.setPaint(new RadialGradientPaint(
new Point2D.Float(centerX, centerY),
finalRadius,
new float[] { 0f, 0.6f, 1f },
new Color[] { COL_CORE_INNER, COL_CORE_OUTER, new Color(120, 60, 200, 0) }));
coreRadius = (int) finalRadius;
g.fillOval((int) (centerX - coreRadius), (int) (centerY - coreRadius),
coreRadius * 2, coreRadius * 2);
}
public void drawPulseField(Graphics2D g) {
float centerX = (float) owner.getCenterX();
float centerY = (float) owner.getCenterY();
// Outer pulse field with subtle animation
// The field pulse should be subtle but stay within the actual damage radius
float fieldPulse = 0.05f * (float) Math.sin(idlePhase * 0.5f) + 0.95f;
// This is the actual damage radius (possibly with very slight animation)
float actualFieldRadius = radius * fieldPulse;
int fieldRadius;
g.setComposite(AlphaComposite.SrcOver.derive(0.15f));
g.setPaint(new RadialGradientPaint(
new Point2D.Float(centerX, centerY),
actualFieldRadius,
new float[] { 0f, 0.4f, 0.8f, 1f },
new Color[] {
new Color(140, 80, 200, 255),
new Color(100, 60, 160, 255),
new Color(80, 40, 120, 255),
new Color(80, 40, 120, 255)
}));
fieldRadius = (int) actualFieldRadius;
g.fillOval((int) (centerX - fieldRadius), (int) (centerY - fieldRadius),
fieldRadius * 2, fieldRadius * 2);
}
public void triggerSolarFlare() {
int i = 0;
Enemy e = null;
float dx = 0f;
float dy = 0f;
float dist = 0f;
flashAlpha = 1f; // full-screen flash
for (i = 0; i < owner.parent.enemies.size(); i++) {
e = owner.parent.enemies.get(i);
if (e.isDead())
continue;
e.takeDamage(solarFlareDamage);
e.applyStun(1f);
dx = (float) (e.getCenterX() - owner.getCenterX());
dy = (float) (e.getCenterY() - owner.getCenterY());
dist = (float) Math.sqrt(dx * dx + dy * dy);
if (dist != 0f) {
dx /= dist;
dy /= dist;
e.applyKnockback(dx * 50f, dy * 50f);
}
}
}
}