Skip to content

Commit fe73466

Browse files
CitralFloRollcziJakubk15
authored
GH-352 Add Arc Riders style flare - firework - indicating players death location (#352)
* Innit Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> * geminaj review Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> * Add try catch Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> * CR * CR 2 * Add flare damage cancel. Add info to config Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> * make it private Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> * Resolve ImDMK review Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> * CR * CR * adjust default configuration to vLucky's review Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> * Update eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/death/DeathFlareController.java Co-authored-by: Jakub Kędziora <77227023+Jakubk15@users.noreply.github.com> --------- Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com> Co-authored-by: Rollczi <ndejlich5@gmail.com> Co-authored-by: Jakub Kędziora <77227023+Jakubk15@users.noreply.github.com>
1 parent c299aed commit fe73466

9 files changed

Lines changed: 294 additions & 39 deletions

File tree

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,22 @@ fair, heart-pounding battles that keep players on their toes. Here’s a rundown
4343
- **Combat Logging**
4444
No more dodging fights by logging out! Once players are in combat, they’re committed until the showdown ends. Watch it
4545
in action:
46-
![Combat log anti logout feature](https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/combatlog.gif?raw=true)
46+
<img alt="Combat log anti logout feature" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/combatlog.gif?raw=true" width="420">
4747

4848
- **Customize combat experience**
4949
Add custom effects to players in combat or death. Everything should be configurable and user-friendly:
50-
![Lightning strikes when players die](https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/lightning.gif?raw=true)
50+
<img alt="Lightning strikes when players die" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/lightning.gif?raw=true" width="420">
51+
<img alt="Flare indicates players death location" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/flare.gif?raw=true" width="420">
5152

5253
- **Spawn Protection (Configurable)**
5354
Stop players from fleeing to safety! Block access to spawn or safe zones during combat – tweak it to fit your server’s
5455
rules. See how it works:
55-
![Border around protected region](https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/border.gif?raw=true)
56+
<img alt="Border around protected region" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/border.gif?raw=true" width="420">
5657

5758
- **Crystal PvP support**
5859
Engage in intense Crystal PvP battles without worrying about players logging out mid-fight! EternalCombat keeps
59-
everyone in the game until the last anchor hit. Check it out:
60-
![Crystal PvP showcase](https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/crystals.gif?raw=true)
60+
everyone in the game until the last anchor hit. Check it out:
61+
<img alt="Crystal PvP showcase" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/crystals.gif?raw=true" width="420">
6162

6263
- **Fully Customizable Combat**
6364
Tailor the combat experience to your liking with a ton of options! From disabling elytra to setting drop rates for

assets/flare.gif

6.37 MB
Loading

buildSrc/src/main/kotlin/Versions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ object Versions {
1919
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.13"
2020
const val OKAERI_CONFIGS_SERDES_BUKKIT = "5.0.13"
2121

22+
const val XSERIES = "13.6.0"
23+
2224
const val CAFFEINE = "3.2.3"
2325

2426
const val B_STATS_BUKKIT = "3.2.1"

eternalcombat-plugin/build.gradle.kts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ dependencies {
4444
implementation("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS_SERDES_COMMONS}")
4545
implementation("eu.okaeri:okaeri-configs-serdes-bukkit:${Versions.OKAERI_CONFIGS_SERDES_BUKKIT}")
4646

47+
// XSeries
48+
implementation("com.github.cryptomorin:XSeries:${Versions.XSERIES}")
49+
4750
// bstats
4851
implementation("org.bstats:bstats-bukkit:${Versions.B_STATS_BUKKIT}")
4952

@@ -126,7 +129,8 @@ tasks.shadowJar {
126129
"com.github.benmanes.caffeine",
127130
"com.eternalcode.commons",
128131
"com.eternalcode.multification",
129-
"io.papermc.lib"
132+
"com.github.cryptomorin",
133+
"io.papermc.lib",
130134
).forEach { pack ->
131135
relocate(pack, "$prefix.$pack")
132136
}

eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
import com.eternalcode.combat.fight.controller.FightBypassCreativeController;
1313
import com.eternalcode.combat.fight.controller.FightBypassPermissionController;
1414
import com.eternalcode.combat.fight.controller.FightInventoryController;
15-
import com.eternalcode.combat.fight.death.DeathEffectController;
15+
import com.eternalcode.combat.fight.death.DeathFlareController;
16+
import com.eternalcode.combat.fight.death.DeathLightningController;
1617
import com.eternalcode.combat.fight.drop.DropKeepInventoryService;
1718
import com.eternalcode.combat.fight.FightManager;
1819
import com.eternalcode.combat.fight.drop.DropService;
@@ -182,7 +183,8 @@ public void onEnable() {
182183
new FightBypassCreativeController(server, pluginConfig),
183184
new FightActionBlockerController(this.fightManager, noticeService, pluginConfig, server),
184185
new FightPearlController(pluginConfig.pearl, noticeService, this.fightManager, this.fightPearlService),
185-
new DeathEffectController(pluginConfig),
186+
new DeathFlareController(pluginConfig, server, scheduler, this),
187+
new DeathLightningController(pluginConfig, server),
186188
new UpdaterNotificationController(updaterService, pluginConfig, this.audienceProvider, miniMessage),
187189
new KnockbackRegionController(noticeService, this.regionProvider, this.fightManager, knockbackService, server),
188190
new FightEffectController(pluginConfig.effect, this.fightEffectService, this.fightManager, server),

eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/death/DeathEffectController.java

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package com.eternalcode.combat.fight.death;
2+
3+
import com.eternalcode.combat.config.implementation.PluginConfig;
4+
import com.eternalcode.combat.fight.event.CauseOfUnTag;
5+
import com.eternalcode.combat.fight.event.FightUntagEvent;
6+
import com.eternalcode.commons.scheduler.Scheduler;
7+
import java.time.Duration;
8+
import java.util.UUID;
9+
import org.bukkit.Color;
10+
import org.bukkit.FireworkEffect;
11+
import org.bukkit.Location;
12+
import org.bukkit.NamespacedKey;
13+
import org.bukkit.Server;
14+
import org.bukkit.World;
15+
import org.bukkit.entity.Firework;
16+
import org.bukkit.entity.Player;
17+
import org.bukkit.event.EventHandler;
18+
import org.bukkit.event.EventPriority;
19+
import org.bukkit.event.Listener;
20+
import org.bukkit.event.entity.EntityDamageByEntityEvent;
21+
import org.bukkit.event.entity.PlayerDeathEvent;
22+
import org.bukkit.inventory.meta.FireworkMeta;
23+
import org.bukkit.persistence.PersistentDataType;
24+
import org.bukkit.plugin.Plugin;
25+
26+
public class DeathFlareController implements Listener {
27+
28+
private final PluginConfig pluginConfig;
29+
private final Server server;
30+
private final Scheduler scheduler;
31+
private final NamespacedKey key;
32+
33+
public DeathFlareController(PluginConfig pluginConfig, Server server, Scheduler scheduler, Plugin plugin) {
34+
this.pluginConfig = pluginConfig;
35+
this.server = server;
36+
this.scheduler = scheduler;
37+
this.key = NamespacedKey.fromString("eternalcombat_firework", plugin);
38+
}
39+
40+
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
41+
public void onFightUntagEvent(FightUntagEvent event) {
42+
CauseOfUnTag cause = event.getCause();
43+
if (cause != CauseOfUnTag.DEATH && cause != CauseOfUnTag.DEATH_BY_PLAYER) {
44+
return;
45+
}
46+
47+
UUID uniqueId = event.getPlayer();
48+
Player player = this.server.getPlayer(uniqueId);
49+
50+
if (player == null) {
51+
return;
52+
}
53+
54+
if (this.pluginConfig.death.firework.inCombat && !this.pluginConfig.death.firework.afterEveryDeath) {
55+
this.spawnFlare(player);
56+
}
57+
}
58+
59+
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
60+
public void onPlayerDeathEventFlare(PlayerDeathEvent event) {
61+
Player player = event.getEntity();
62+
63+
if (this.pluginConfig.death.firework.afterEveryDeath) {
64+
this.spawnFlare(player);
65+
}
66+
}
67+
68+
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
69+
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
70+
if (event.getDamager() instanceof Firework firework && firework.getPersistentDataContainer().has(key, PersistentDataType.STRING)) {
71+
event.setCancelled(true);
72+
}
73+
}
74+
75+
private void spawnFlare(Player player) {
76+
Location deathLocation = player.getLocation();
77+
World world = deathLocation.getWorld();
78+
79+
Firework flare = world.spawn(deathLocation, Firework.class);
80+
flare.getPersistentDataContainer().set(key, PersistentDataType.STRING, "true");
81+
82+
FireworkMeta meta = flare.getFireworkMeta();
83+
84+
Color primaryColor = this.decodeColor(this.pluginConfig.death.firework.primaryColor, "primary");
85+
Color fadeColor = this.decodeColor(this.pluginConfig.death.firework.fadeColor, "fade");
86+
87+
FireworkEffect effect = FireworkEffect.builder()
88+
.with(this.pluginConfig.death.firework.fireworkType)
89+
.withColor(primaryColor)
90+
.withFade(fadeColor)
91+
.trail(true)
92+
.flicker(true)
93+
.build();
94+
95+
meta.addEffect(effect);
96+
meta.setPower(this.pluginConfig.death.firework.power);
97+
flare.setFireworkMeta(meta);
98+
99+
if (this.pluginConfig.death.firework.particlesEnabled) {
100+
scheduleParticles(flare, world);
101+
}
102+
}
103+
104+
private void scheduleParticles(Firework flare, World world) {
105+
this.scheduler.runLaterAsync(
106+
() -> {
107+
if (flare.isDead() || !flare.isValid()) {
108+
return;
109+
}
110+
this.spawnParticles(world, flare);
111+
this.scheduleParticles(flare, world);
112+
}, Duration.ofMillis(50)
113+
);
114+
}
115+
116+
private Color decodeColor(String firework, String name) {
117+
try {
118+
return Color.fromRGB(Integer.decode(firework));
119+
}
120+
catch (NumberFormatException exception) {
121+
throw new IllegalArgumentException(
122+
"Invalid " + name + " format in plugin configuration" + firework,
123+
exception
124+
);
125+
}
126+
}
127+
128+
private void spawnParticles(World world, Firework flare) {
129+
Location location = flare.getLocation();
130+
131+
world.spawnParticle(
132+
this.pluginConfig.death.firework.mainParticle.get(),
133+
location,
134+
this.pluginConfig.death.firework.mainParticleCount,
135+
0.05,
136+
0.05,
137+
0.05,
138+
0.01
139+
);
140+
141+
world.spawnParticle(
142+
this.pluginConfig.death.firework.secondaryParticle.get(),
143+
location,
144+
this.pluginConfig.death.firework.secondaryParticleCount,
145+
0.1,
146+
0.1,
147+
0.1,
148+
0.01
149+
);
150+
}
151+
152+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.eternalcode.combat.fight.death;
2+
3+
import com.eternalcode.combat.config.implementation.PluginConfig;
4+
import com.eternalcode.combat.fight.event.CauseOfUnTag;
5+
import com.eternalcode.combat.fight.event.FightUntagEvent;
6+
import java.util.UUID;
7+
import org.bukkit.Server;
8+
import org.bukkit.entity.Player;
9+
import org.bukkit.event.EventHandler;
10+
import org.bukkit.event.EventPriority;
11+
import org.bukkit.event.Listener;
12+
import org.bukkit.event.entity.PlayerDeathEvent;
13+
14+
public class DeathLightningController implements Listener {
15+
16+
private final PluginConfig pluginConfig;
17+
private final Server server;
18+
19+
public DeathLightningController(PluginConfig pluginConfig, Server server) {
20+
this.pluginConfig = pluginConfig;
21+
this.server = server;
22+
}
23+
24+
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
25+
public void onFightUntagEvent(FightUntagEvent event) {
26+
CauseOfUnTag cause = event.getCause();
27+
if (cause != CauseOfUnTag.DEATH && cause != CauseOfUnTag.DEATH_BY_PLAYER) {
28+
return;
29+
}
30+
31+
UUID uniqueId = event.getPlayer();
32+
Player player = this.server.getPlayer(uniqueId);
33+
34+
if (player == null) {
35+
return;
36+
}
37+
38+
if (this.pluginConfig.death.lightning.inCombat && !this.pluginConfig.death.lightning.afterEveryDeath) {
39+
this.lightningStrike(player);
40+
}
41+
}
42+
43+
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
44+
public void onPlayerDeathEventLightning(PlayerDeathEvent event) {
45+
Player player = event.getEntity();
46+
47+
if (this.pluginConfig.death.lightning.afterEveryDeath) {
48+
lightningStrike(player);
49+
}
50+
}
51+
52+
private void lightningStrike(Player player) {
53+
player.getWorld().strikeLightningEffect(player.getLocation());
54+
}
55+
56+
}
Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,77 @@
11
package com.eternalcode.combat.fight.death;
22

3+
import com.cryptomorin.xseries.particles.XParticle;
34
import eu.okaeri.configs.OkaeriConfig;
45
import eu.okaeri.configs.annotation.Comment;
6+
import org.bukkit.FireworkEffect;
57

68
public class DeathSettings extends OkaeriConfig {
79

8-
@Comment("Should lightning strike when a player dies")
9-
public boolean lightning = true;
10+
@Comment({
11+
"Settings related to lightning effect upon death",
12+
"Setting both afterEveryDeath and inCombat to false will disable this feature completely"
13+
})
14+
public LightningSettings lightning = new LightningSettings();
15+
16+
public static class LightningSettings extends OkaeriConfig {
17+
18+
@Comment("Should lightning spawn on every death?")
19+
public boolean afterEveryDeath = false;
20+
21+
@Comment("Should lightning spawn on ONLY deaths in combat?")
22+
public boolean inCombat = true;
23+
}
24+
25+
@Comment({
26+
"Settings for the Arc Raiders style flare (firework)",
27+
"Setting both afterEveryDeath and inCombat to false will disable this feature completely"
28+
})
29+
public FlareSettings firework = new FlareSettings();
30+
31+
public static class FlareSettings extends OkaeriConfig {
32+
33+
@Comment("Should firework (flare) spawn on every death?")
34+
public boolean afterEveryDeath = false;
35+
36+
@Comment("Should firework (flare) spawn on ONLY deaths in combat?")
37+
public boolean inCombat = false;
38+
39+
@Comment("Power of firework - how long till explosion (please enter positive number)")
40+
public int power = 2;
41+
42+
@Comment({
43+
"The firework (flare) effect type (BALL, BALL_LARGE, STAR, BURST, CREEPER)",
44+
"Reference: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/FireworkEffect.Type.html"
45+
})
46+
public FireworkEffect.Type fireworkType = FireworkEffect.Type.BALL;
47+
48+
@Comment("Hex color for the firework (flare)")
49+
public String primaryColor = "#a80022";
50+
51+
@Comment("Hex color for the fade of firework (flare)")
52+
public String fadeColor = "#0a0a0a";
53+
54+
@Comment("Toggle on/off additional particles spawned in the firework (flare) path")
55+
public boolean particlesEnabled = true;
56+
57+
@Comment({
58+
"The main trail particle (e.g., CAMPFIRE_COSY_SMOKE, SMOKE_LARGE)",
59+
"Reference: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Particle.html"
60+
})
61+
public XParticle mainParticle = XParticle.CAMPFIRE_COSY_SMOKE;
62+
63+
@Comment("Count of main particles spawned on each tick")
64+
public int mainParticleCount = 3;
65+
66+
@Comment({
67+
"The secondary trail particle (e.g., CAMPFIRE_COSY_SMOKE, SMOKE_LARGE)",
68+
"Reference: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Particle.html"
69+
})
70+
71+
public XParticle secondaryParticle = XParticle.SMALL_FLAME;
72+
73+
@Comment("Count of secondary particles spawned on each tick")
74+
public int secondaryParticleCount = 3;
75+
76+
}
1077
}

0 commit comments

Comments
 (0)