Compare commits
10 Commits
2f63695ee9
...
c9f1759ef9
Author | SHA1 | Date | |
---|---|---|---|
c9f1759ef9 | |||
9b0bdfddc8 | |||
83e6139acc | |||
a299b167de | |||
f8b99d44bf | |||
99197d92bb | |||
9890936154 | |||
793b25251c | |||
a4b110773e | |||
75debe1905 |
5
.woodpecker.yml
Normal file
5
.woodpecker.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: maven:3-openjdk-18-slim
|
||||||
|
commands:
|
||||||
|
- mvn package
|
101
TODO.md
101
TODO.md
@ -2,19 +2,73 @@
|
|||||||
|
|
||||||
[X] Mobs spawn in waves
|
[X] Mobs spawn in waves
|
||||||
[X] Mobs move towards goal
|
[X] Mobs move towards goal
|
||||||
[ ] Mobs carry bomb to goal
|
[X] Mobs carry bomb to goal
|
||||||
[X] Mobs arm bomb
|
[X] Mobs arm bomb
|
||||||
[X] Bomb explodes
|
[X] Bomb explodes
|
||||||
|
|
||||||
|
# Malloc Beta
|
||||||
|
|
||||||
|
[X] One arena config
|
||||||
|
[X] Config reload
|
||||||
|
[X] Leave games
|
||||||
|
[X] Join games
|
||||||
|
[X] Drop back to lobby on game over
|
||||||
|
[X] Grist drops
|
||||||
|
[X] Chat colors + clickables
|
||||||
|
[X] Title colors
|
||||||
|
[X] Command tab completion
|
||||||
|
[X] Mob AI categories
|
||||||
|
[X] Never hungry
|
||||||
|
[X] Mobs don't drop bomb items
|
||||||
|
[ ] Execute commands on game/wave end
|
||||||
|
[X] Player Respawning
|
||||||
|
[ ] Difficulty
|
||||||
|
|
||||||
|
# QOL
|
||||||
|
|
||||||
|
[ ] Players can't pick up bomb items
|
||||||
|
[ ] "Get ready" nag
|
||||||
|
[ ] "Click here to leave" at end of game
|
||||||
|
[ ] Hidden armor stands
|
||||||
|
[ ] Small/nonexistent prop collision boxes
|
||||||
|
[ ] Leave game when leaving game world
|
||||||
|
[ ] Bomb and target glow different colors
|
||||||
|
[ ] Play sound once bomb is close to / at target
|
||||||
|
[X] Expose coins under vault API
|
||||||
|
[ ] Animations framework
|
||||||
|
|
||||||
|
# Malloc beta map
|
||||||
|
|
||||||
|
[ ] Lobby with instructions
|
||||||
|
[X] Item shoppes
|
||||||
|
[ ] Indestructible weapons/armor
|
||||||
|
|
||||||
|
# Scaled waves
|
||||||
|
|
||||||
|
[X] Limit ranged mobs and Ravagers to non-bomb-carrier state
|
||||||
|
[X] Weaker mobs, more of them
|
||||||
|
[X] Mob AI categories
|
||||||
|
|
||||||
# UX
|
# UX
|
||||||
|
|
||||||
[X] Wave boss bar
|
[X] Wave boss bar
|
||||||
[X] Mob count boss bar
|
[X] Mob count boss bar
|
||||||
[X] Stage titles
|
[X] Stage titles
|
||||||
[ ] EXPLOSIONS
|
[X] EXPLOSIONS
|
||||||
|
[X] Bomb model
|
||||||
|
[X] "Player $X is ready" message in chat
|
||||||
|
[X] Clickable /ready in chat
|
||||||
|
[ ] Post-Round summary in chat
|
||||||
|
[X] Clickable join links in /list
|
||||||
|
[X] Sidebar
|
||||||
|
[X] Coin pickup status in sidebar
|
||||||
|
[ ] Target catches on fire more it is lit
|
||||||
[ ] Colored titles
|
[ ] Colored titles
|
||||||
[ ] Clickable /ready in chat
|
[ ] Clickable /leave action
|
||||||
[ ] Sidebar
|
[ ] Countdown while in warmup
|
||||||
|
[ ] Countdown shrinks w/ every /ready player
|
||||||
|
[ ] List of mobs in next wave
|
||||||
|
[ ] Pretty bomb model
|
||||||
|
|
||||||
# Social
|
# Social
|
||||||
|
|
||||||
@ -23,13 +77,16 @@
|
|||||||
[ ] Plan stats
|
[ ] Plan stats
|
||||||
[ ] /invite friends to games
|
[ ] /invite friends to games
|
||||||
[ ] /voterestart
|
[ ] /voterestart
|
||||||
|
[ ] Medals/awards/scoreboards/cosmetics
|
||||||
|
|
||||||
# Mechanics
|
# Mechanics
|
||||||
|
|
||||||
[ ] Coin drops
|
[X] Mob tracking should prioritize bomb
|
||||||
[ ] Mob tracking should prioritize bomb
|
[X] Mobs recover dropped bombs
|
||||||
[ ] Mobs recover dropped bombs
|
[X] Bomb carriers are slower
|
||||||
[ ] Bomb carriers are slower
|
[X] Coin drops
|
||||||
|
[X] Mob categories
|
||||||
|
[X] Mobs split between bomb and player priorities
|
||||||
[ ] Bonus coins for complete coin pickup
|
[ ] Bonus coins for complete coin pickup
|
||||||
[ ] Infinite weapons + armor
|
[ ] Infinite weapons + armor
|
||||||
[ ] Ammo/health spawns
|
[ ] Ammo/health spawns
|
||||||
@ -40,39 +97,49 @@
|
|||||||
[X] Randomized spawn locations
|
[X] Randomized spawn locations
|
||||||
[X] Weighted distributions
|
[X] Weighted distributions
|
||||||
[X] Batch overlap
|
[X] Batch overlap
|
||||||
|
[X] Spawnpoint categories
|
||||||
[ ] Scripted batch overlap/timings
|
[ ] Scripted batch overlap/timings
|
||||||
[ ] Scripted spawn locations
|
[ ] Scripted spawn locations
|
||||||
|
[ ] Scripted waypoint paths
|
||||||
|
[ ] Execute commands on game/wave end
|
||||||
|
[ ] Coin reward curve
|
||||||
|
[ ] Mob number curves with player count
|
||||||
[ ] Bosses
|
[ ] Bosses
|
||||||
|
|
||||||
# Mapping
|
# Mapping
|
||||||
|
|
||||||
[ ] Load arenas from config file
|
[X] Load arenas from config file
|
||||||
[ ] Live map editing
|
[ ] Live map editing
|
||||||
|
|
||||||
# Game lifecycle
|
# Game lifecycle
|
||||||
|
|
||||||
[ ] /list arenas and games
|
[X] /list arenas and games
|
||||||
[ ] /start a game on an arena
|
[X] /join games
|
||||||
[ ] /join games
|
|
||||||
[X] /ready
|
[X] /ready
|
||||||
[ ] /leave games
|
[X] /leave games
|
||||||
[ ] /restart games
|
|
||||||
[X] Spectator mode on death
|
[X] Spectator mode on death
|
||||||
[X] Player readiness starts countdown
|
[X] Player readiness starts countdown
|
||||||
[ ] Game is automatically closed some time after game over
|
[X] Game is automatically closed some time after game over
|
||||||
[ ] Return to lobby on leave/close
|
[X] Return to lobby on leave/close
|
||||||
|
[X] Restore health+hunger on respawn/game start
|
||||||
|
[ ] /restart games
|
||||||
[ ] Instancing
|
[ ] Instancing
|
||||||
|
[X] Respawn during games
|
||||||
|
[ ] Player revival items
|
||||||
|
[X] Clear inventory on join/leave
|
||||||
|
|
||||||
# Powerups
|
# Powerups
|
||||||
|
|
||||||
|
[ ] Enchantments
|
||||||
|
[ ] Better items
|
||||||
[ ] Coin pickup range
|
[ ] Coin pickup range
|
||||||
[ ] Coin boost
|
[ ] Coin boost
|
||||||
[ ] Knockback on weapons
|
[ ] Knockback on weapons
|
||||||
[ ] Damage boost
|
[ ] Damage boost
|
||||||
[ ] Speed boost
|
[ ] Speed boost
|
||||||
[ ] Health boost
|
[ ] Health boost
|
||||||
[ ] Repair barriers
|
|
||||||
|
|
||||||
# Fantasy
|
# Fantasy
|
||||||
|
|
||||||
[ ] Totems/turrets/stationary weapons
|
[ ] Totems/turrets/stationary weapons
|
||||||
|
[ ] Repair barriers
|
||||||
|
12
pom.xml
12
pom.xml
@ -37,6 +37,18 @@
|
|||||||
<version>1.18.2-R0.1-SNAPSHOT</version>
|
<version>1.18.2-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.tr7zw</groupId>
|
||||||
|
<artifactId>item-nbt-api-plugin</artifactId>
|
||||||
|
<version>2.9.2</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.MilkBowl</groupId>
|
||||||
|
<artifactId>VaultAPI</artifactId>
|
||||||
|
<version>1.7</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
198
src/main/java/gg/malloc/defense/GameEconomy.java
Normal file
198
src/main/java/gg/malloc/defense/GameEconomy.java
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
package gg.malloc.defense;
|
||||||
|
|
||||||
|
import net.milkbowl.vault.economy.AbstractEconomy;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse.ResponseType;
|
||||||
|
|
||||||
|
import org.bukkit.plugin.ServicesManager;
|
||||||
|
import org.bukkit.plugin.ServicePriority;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class GameEconomy extends AbstractEconomy {
|
||||||
|
|
||||||
|
Plugin m_plugin;
|
||||||
|
|
||||||
|
public GameEconomy(Plugin plugin) {
|
||||||
|
m_plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register() {
|
||||||
|
ServicesManager manager = m_plugin.getServer().getServicesManager();
|
||||||
|
manager.register(Economy.class, this, m_plugin, ServicePriority.Highest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse withdrawPlayer(String playerName, double amount) {
|
||||||
|
Instance runner = m_plugin.getRunnerForPlayer(playerName);
|
||||||
|
if (runner != null) {
|
||||||
|
Player p = m_plugin.getServer().getPlayer(playerName);
|
||||||
|
if (!has(playerName, amount)) {
|
||||||
|
int newBalance = runner.getState().getPlayerBalance(p);
|
||||||
|
return new EconomyResponse(amount, newBalance, ResponseType.FAILURE, "Not enough money!");
|
||||||
|
} else {
|
||||||
|
runner.spendMoney(p, (int)Math.floor(amount));
|
||||||
|
int newBalance = runner.getState().getPlayerBalance(p);
|
||||||
|
return new EconomyResponse(amount, newBalance, ResponseType.SUCCESS, "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new EconomyResponse(0, 0, ResponseType.FAILURE, "Not in a game!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse depositPlayer(String playerName, double amount) {
|
||||||
|
Instance runner = m_plugin.getRunnerForPlayer(playerName);
|
||||||
|
if (runner != null) {
|
||||||
|
Player p = m_plugin.getServer().getPlayer(playerName);
|
||||||
|
runner.spendMoney(p, -(int)Math.floor(amount));
|
||||||
|
int newBalance = runner.getState().getPlayerBalance(p);
|
||||||
|
return new EconomyResponse(amount, newBalance, ResponseType.SUCCESS, "");
|
||||||
|
} else {
|
||||||
|
return new EconomyResponse(0, 0, ResponseType.FAILURE, "Not in a game!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getBalance(String playerName) {
|
||||||
|
Instance runner = m_plugin.getRunnerForPlayer(playerName);
|
||||||
|
if (runner != null) {
|
||||||
|
Player p = m_plugin.getServer().getPlayer(playerName);
|
||||||
|
return runner.getState().getPlayerBalance(p);
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getBalance(String playerName, String worldName) {
|
||||||
|
return getBalance(playerName);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Malloc Defense";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasBankSupport() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int fractionalDigits() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(double amount) {
|
||||||
|
return amount + " grist";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String currencyNamePlural() {
|
||||||
|
return "Grist";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String currencyNameSingular() {
|
||||||
|
return "Grist";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAccount(String playerName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAccount(String playerName, String worldName) {
|
||||||
|
return hasAccount(playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(String playerName, double amount) {
|
||||||
|
return getBalance(playerName) >= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(String playerName, String worldName, double amount) {
|
||||||
|
return has(playerName, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse withdrawPlayer(String playerName, String worldName, double amount) {
|
||||||
|
return withdrawPlayer(playerName, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse depositPlayer(String playerName, String worldName, double amount) {
|
||||||
|
return depositPlayer(playerName, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final EconomyResponse NO_IMPL_RESPONSE = new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, "Not implemented.");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse createBank(String name, String player) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse deleteBank(String name) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankBalance(String name) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankHas(String name, double amount) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankWithdraw(String name, double amount) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse bankDeposit(String name, double amount) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse isBankOwner(String name, String player) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EconomyResponse isBankMember(String name, String player) {
|
||||||
|
return NO_IMPL_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createPlayerAccount(String name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createPlayerAccount(String name, String worldName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getBanks() {
|
||||||
|
return new ArrayList<String>();
|
||||||
|
}
|
||||||
|
}
|
@ -2,30 +2,55 @@ package gg.malloc.defense;
|
|||||||
|
|
||||||
import gg.malloc.defense.engine.GameRunner;
|
import gg.malloc.defense.engine.GameRunner;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.entity.EntityDeathEvent;
|
import org.bukkit.event.entity.EntityDeathEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityCombustEvent;
|
import org.bukkit.event.entity.EntityCombustEvent;
|
||||||
import org.bukkit.event.entity.EntityTargetEvent;
|
import org.bukkit.event.entity.EntityTargetEvent;
|
||||||
|
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.event.player.PlayerPickupItemEvent;
|
||||||
|
import org.bukkit.event.world.LootGenerateEvent;
|
||||||
|
import org.bukkit.Sound;
|
||||||
|
import org.bukkit.SoundCategory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public class GameEventHandler implements Listener {
|
import de.tr7zw.nbtapi.NBTItem;
|
||||||
GameRunner m_runner;
|
import de.tr7zw.nbtapi.NBTCompound;
|
||||||
|
|
||||||
public GameEventHandler(GameRunner runner) {
|
public class GameEventHandler implements Listener {
|
||||||
m_runner = runner;
|
Collection<GameRunner> m_runners;
|
||||||
|
|
||||||
|
public GameEventHandler() {
|
||||||
|
m_runners = new HashSet<GameRunner>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRunner(GameRunner runner) {
|
||||||
|
m_runners.add(runner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
m_runners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityDeath(EntityDeathEvent evt) {
|
public void onEntityDeath(EntityDeathEvent evt) {
|
||||||
m_runner.handleEntityDeath(evt.getEntity());
|
for(GameRunner r : m_runners) {
|
||||||
|
r.handleEntityDeath(evt.getEntity());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerQuit(PlayerQuitEvent evt) {
|
public void onPlayerQuit(PlayerQuitEvent evt) {
|
||||||
m_runner.removePlayer(evt.getPlayer());
|
for(GameRunner r : m_runners) {
|
||||||
|
r.removePlayer(evt.getPlayer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@ -35,19 +60,49 @@ public class GameEventHandler implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityTarget(EntityTargetEvent evt) {
|
public void onEntityTarget(EntityTargetEvent evt) {
|
||||||
m_runner.handleEntityRetargeting(evt);
|
for(GameRunner r : m_runners) {
|
||||||
|
r.handleEntityRetargeting(evt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityDamage(EntityDamageEvent evt) {
|
public void onEntityDamage(EntityDamageEvent evt) {
|
||||||
if (evt.getEntity() instanceof Player) {
|
for(GameRunner r : m_runners) {
|
||||||
Player player = (Player)evt.getEntity();
|
r.handleEntityDamage(evt);
|
||||||
if (player.getHealth() - evt.getFinalDamage() <= 0) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onLootGenerated(LootGenerateEvent evt) {
|
||||||
|
if (evt.getEntity() != null) {
|
||||||
|
ArrayList<ItemStack> emptyLoot = new ArrayList<>();
|
||||||
|
evt.setLoot(emptyLoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onFoodLevelChange(FoodLevelChangeEvent evt) {
|
||||||
evt.setCancelled(true);
|
evt.setCancelled(true);
|
||||||
m_runner.handlePlayerDeath(player);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
m_runner.handleEntityDamage(evt);
|
@EventHandler
|
||||||
|
public void onItemPickup(PlayerPickupItemEvent evt) {
|
||||||
|
NBTItem nbt = new NBTItem(evt.getItem().getItemStack());
|
||||||
|
if (nbt.hasKey("malloc")) {
|
||||||
|
NBTCompound mallocData = nbt.getCompound("malloc");
|
||||||
|
if (mallocData.hasKey("coinValue")) {
|
||||||
|
evt.setCancelled(true);
|
||||||
|
Player player = evt.getPlayer();
|
||||||
|
int coinValue = mallocData.getInteger("coinValue") * evt.getItem().getItemStack().getAmount();
|
||||||
|
player.getWorld().playSound(evt.getPlayer(), Sound.BLOCK_CHAIN_PLACE, SoundCategory.PLAYERS, (float)1.0, (float)1.0);
|
||||||
|
evt.getItem().remove();
|
||||||
|
for(GameRunner runner : m_runners) {
|
||||||
|
if (runner.getPlayers().contains(player)) {
|
||||||
|
runner.depositCoins(coinValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package gg.malloc.defense;
|
|
||||||
|
|
||||||
import gg.malloc.defense.model.Arena;
|
|
||||||
import gg.malloc.defense.model.Spawnpoint;
|
|
||||||
|
|
||||||
import org.bukkit.World;
|
|
||||||
|
|
||||||
public class MemoryArena implements Arena {
|
|
||||||
|
|
||||||
Spawnpoint[] m_spawnpoints;
|
|
||||||
Spawnpoint m_bombTarget;
|
|
||||||
World m_world;
|
|
||||||
String m_name;
|
|
||||||
|
|
||||||
public MemoryArena(String name, World world, Spawnpoint[] spawnpoints, Spawnpoint bombTarget) {
|
|
||||||
m_world = world;
|
|
||||||
m_spawnpoints = spawnpoints;
|
|
||||||
m_name = name;
|
|
||||||
m_bombTarget = bombTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String name() {
|
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Spawnpoint[] spawnpoints() {
|
|
||||||
return m_spawnpoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public World getWorld() {
|
|
||||||
return m_world;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Spawnpoint bombTarget() {
|
|
||||||
return m_bombTarget;
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,54 +5,62 @@ import org.bukkit.plugin.RegisteredServiceProvider;
|
|||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.WorldCreator;
|
import org.bukkit.WorldCreator;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import gg.malloc.defense.model.Arena;
|
import gg.malloc.defense.model.Arena;
|
||||||
import gg.malloc.defense.model.Spawnpoint;
|
|
||||||
import gg.malloc.defense.model.Game;
|
import gg.malloc.defense.model.Game;
|
||||||
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
import gg.malloc.defense.games.LinearGame;
|
import gg.malloc.defense.games.LinearGame;
|
||||||
import gg.malloc.defense.games.ScaledWaves;
|
import gg.malloc.defense.games.ScaledWaves;
|
||||||
|
|
||||||
import gg.malloc.defense.engine.GameRunner;
|
import gg.malloc.defense.engine.GameRunner;
|
||||||
|
|
||||||
|
import gg.malloc.defense.config.Configuration;
|
||||||
|
|
||||||
import gg.malloc.defense.commands.AddPlayerCommand;
|
import gg.malloc.defense.commands.AddPlayerCommand;
|
||||||
import gg.malloc.defense.commands.SetStageCommand;
|
import gg.malloc.defense.commands.SetStageCommand;
|
||||||
import gg.malloc.defense.commands.PlayerReadyCommand;
|
import gg.malloc.defense.commands.PlayerReadyCommand;
|
||||||
|
import gg.malloc.defense.commands.ListGamesCommand;
|
||||||
|
import gg.malloc.defense.commands.JoinGameCommand;
|
||||||
|
import gg.malloc.defense.commands.LeaveGameCommand;
|
||||||
|
import gg.malloc.defense.commands.ReloadCommand;
|
||||||
|
|
||||||
public class Plugin extends JavaPlugin {
|
public class Plugin extends JavaPlugin {
|
||||||
ArrayList<Arena> m_arenas = new ArrayList<>();
|
HashMap<String, Arena> m_arenas = new HashMap<>();
|
||||||
ArrayList<Game> m_games = new ArrayList<>();
|
ArrayList<Game> m_games = new ArrayList<>();
|
||||||
HashMap<World, GameRunner> m_runningGames = new HashMap<>();
|
HashMap<String, GameRunner> m_runningGames = new HashMap<>();
|
||||||
|
HashMap<Player, GameRunner> m_playerGames = new HashMap<>();
|
||||||
|
GameEventHandler m_handler = new GameEventHandler();
|
||||||
|
GameEconomy m_economy = new GameEconomy(this);
|
||||||
|
|
||||||
private class TestSpawn implements Spawnpoint {
|
public Collection<Instance> getInstances() {
|
||||||
Location m_location;
|
ArrayList<Instance> ret = new ArrayList<>();
|
||||||
|
for(GameRunner runner : m_runningGames.values()) {
|
||||||
public TestSpawn(Location location) {
|
ret.add(runner);
|
||||||
m_location = location;
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Collection<String> arenaNames() {
|
||||||
public Location getLocation() {
|
return m_arenas.keySet();
|
||||||
return m_location;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Mob Spawner";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getID() {
|
|
||||||
return "mob-spawner";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void debuginfo() {
|
public void debuginfo() {
|
||||||
@ -63,37 +71,120 @@ public class Plugin extends JavaPlugin {
|
|||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
getLogger().info("Malloc Defense registered");
|
getLogger().info("Malloc Defense registered");
|
||||||
getLogger().setLevel(Level.FINEST);
|
getLogger().setLevel(Level.FINEST);
|
||||||
setupDemoGame();
|
m_games.add(new ScaledWaves());
|
||||||
getCommand("setstage").setExecutor(new SetStageCommand(this));
|
reloadArenas();
|
||||||
getCommand("addplayer").setExecutor(new AddPlayerCommand(this));
|
getCommand("join").setExecutor(new JoinGameCommand(this));
|
||||||
getCommand("debuginfo").setExecutor(new DebuginfoCommand(this));
|
getCommand("leave").setExecutor(new LeaveGameCommand(this));
|
||||||
getCommand("ready").setExecutor(new PlayerReadyCommand(this));
|
getCommand("ready").setExecutor(new PlayerReadyCommand(this));
|
||||||
|
getCommand("list").setExecutor(new ListGamesCommand(this));
|
||||||
|
|
||||||
|
getCommand("addplayer").setExecutor(new AddPlayerCommand(this));
|
||||||
|
getCommand("setstage").setExecutor(new SetStageCommand(this));
|
||||||
|
getCommand("debuginfo").setExecutor(new DebuginfoCommand(this));
|
||||||
|
getCommand("reload").setExecutor(new ReloadCommand(this));
|
||||||
|
|
||||||
|
getServer().getPluginManager().registerEvents(new PlayerQuitHandler(), this);
|
||||||
|
getServer().getPluginManager().registerEvents(m_handler, this);
|
||||||
|
|
||||||
|
getLogger().info("Registering economy");
|
||||||
|
m_economy.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameRunner getRunnerForWorld(World world) {
|
public void onDisable() {
|
||||||
GameRunner ret;
|
getLogger().info("Unloading games...");
|
||||||
if (m_runningGames.containsKey(world)) {
|
for(GameRunner runner : m_runningGames.values()) {
|
||||||
ret = m_runningGames.get(world);
|
runner.requestTransition(GameRunner.Stage.Idle);
|
||||||
} else {
|
}
|
||||||
ret = new GameRunner(this, m_games.get(0), m_arenas.get(0));
|
for(Player player : m_playerGames.keySet()) {
|
||||||
m_runningGames.put(world, ret);
|
m_playerGames.get(player).removePlayer(player);
|
||||||
getServer().getPluginManager().registerEvents(new GameEventHandler(ret), this);
|
returnPlayerToLobby(player);
|
||||||
|
}
|
||||||
|
m_runningGames.clear();
|
||||||
|
m_playerGames.clear();
|
||||||
|
m_handler.clear();
|
||||||
|
m_arenas.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadArenas() {
|
||||||
|
getLogger().info("Loading arenas...");
|
||||||
|
saveDefaultConfig();
|
||||||
|
reloadConfig();
|
||||||
|
Configuration config = new Configuration(getConfig());
|
||||||
|
|
||||||
|
for(GameRunner runner : m_runningGames.values()) {
|
||||||
|
runner.requestTransition(GameRunner.Stage.Idle);
|
||||||
|
}
|
||||||
|
for(Player player : m_playerGames.keySet()) {
|
||||||
|
m_playerGames.get(player).removePlayer(player);
|
||||||
|
returnPlayerToLobby(player);
|
||||||
|
}
|
||||||
|
m_runningGames.clear();
|
||||||
|
m_playerGames.clear();
|
||||||
|
m_handler.clear();
|
||||||
|
m_arenas.clear();
|
||||||
|
for(String mapName : config.getMapNames()) {
|
||||||
|
m_arenas.put(mapName, config.getArenaForMap(mapName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasRunnerForArenaName(String arenaName) {
|
||||||
|
return m_runningGames.containsKey(arenaName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayerToArena(String arenaName, Player player) {
|
||||||
|
GameRunner runner = getRunnerForArenaName(arenaName);
|
||||||
|
runner.addPlayer(player);
|
||||||
|
m_playerGames.put(player, runner);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlayerQuitHandler implements Listener {
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent evt) {
|
||||||
|
m_playerGames.remove(evt.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRunner getRunnerForArenaName(String arenaName) {
|
||||||
|
GameRunner ret = null;
|
||||||
|
if (m_runningGames.containsKey(arenaName)) {
|
||||||
|
ret = m_runningGames.get(arenaName);
|
||||||
|
} else if (m_arenas.containsKey(arenaName)) {
|
||||||
|
getLogger().info("Loading game world " + arenaName);
|
||||||
|
World gameWorld = getServer().getWorld(arenaName);
|
||||||
|
if (gameWorld == null) {
|
||||||
|
getLogger().info("Creating game world " + arenaName);
|
||||||
|
gameWorld = new WorldCreator(arenaName).generateStructures(false).createWorld();
|
||||||
|
}
|
||||||
|
ret = new GameRunner(this, m_games.get(0), m_arenas.get(arenaName), gameWorld);
|
||||||
|
m_handler.addRunner(ret);
|
||||||
|
m_runningGames.put(arenaName, ret);
|
||||||
|
getLogger().info("Game ready for " + arenaName);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDemoGame() {
|
public void returnPlayerToLobby(Player p) {
|
||||||
getLogger().info("Setting up demo data'");
|
Configuration config = new Configuration(getConfig());
|
||||||
World testWorld = getServer().getWorld("quarry");
|
World lobby = getServer().getWorld(config.getLobbyName());
|
||||||
if (testWorld == null) {
|
if (lobby == null) {
|
||||||
testWorld = new WorldCreator("quarry").generateStructures(false).createWorld();
|
lobby = new WorldCreator(config.getLobbyName()).generateStructures(false).createWorld();
|
||||||
}
|
}
|
||||||
Spawnpoint[] spawnpoints = new Spawnpoint[3];
|
p.teleport(lobby.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
spawnpoints[0] = new TestSpawn(new Location(testWorld, -15, 80, -46));
|
}
|
||||||
spawnpoints[1] = new TestSpawn(new Location(testWorld, -1, 80, -45));
|
|
||||||
spawnpoints[2] = new TestSpawn(new Location(testWorld, 12, 81, -42));
|
public GameRunner getRunnerForPlayer(String playerName) {
|
||||||
Spawnpoint bombTarget = new TestSpawn(new Location(testWorld, -20, 80, 31));
|
return getRunnerForPlayer(getServer().getPlayer(playerName));
|
||||||
m_arenas.add(new MemoryArena("Test Arena", testWorld, spawnpoints, bombTarget));
|
}
|
||||||
m_games.add(new ScaledWaves());
|
|
||||||
|
public void removePlayerFromGames(Player player) {
|
||||||
|
m_playerGames.get(player).removePlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRunner getRunnerForPlayer(Player p) {
|
||||||
|
GameRunner ret = null;
|
||||||
|
if (m_playerGames.containsKey(p)) {
|
||||||
|
ret = m_playerGames.get(p);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import org.bukkit.command.CommandExecutor;
|
|||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import gg.malloc.defense.engine.GameRunner;
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
import gg.malloc.defense.Plugin;
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
@ -18,8 +18,12 @@ public class AddPlayerCommand implements CommandExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
String worldName = args[0];
|
String arenaName = args[0];
|
||||||
GameRunner runner = m_plugin.getRunnerForWorld(m_plugin.getServer().getWorld(worldName));
|
Instance runner = m_plugin.getRunnerForArenaName(arenaName);
|
||||||
|
if (runner == null) {
|
||||||
|
sender.sendMessage("No such arena '" + arenaName + "'");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Player player = m_plugin.getServer().getPlayer(args[1]);
|
Player player = m_plugin.getServer().getPlayer(args[1]);
|
||||||
runner.addPlayer(player);
|
runner.addPlayer(player);
|
||||||
return true;
|
return true;
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package gg.malloc.defense.commands;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.TabExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JoinGameCommand implements TabExecutor {
|
||||||
|
Plugin m_plugin;
|
||||||
|
|
||||||
|
public JoinGameCommand(Plugin plugin) {
|
||||||
|
m_plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
ArrayList<String> ret = new ArrayList<>();
|
||||||
|
if (args.length == 1) {
|
||||||
|
String proposal = args[0].toLowerCase();
|
||||||
|
for(String arena : m_plugin.arenaNames()) {
|
||||||
|
if (arena.toLowerCase().startsWith(proposal.toLowerCase())) {
|
||||||
|
ret.add(arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (args.length == 0) {
|
||||||
|
for(String arena : m_plugin.arenaNames()) {
|
||||||
|
ret.add(arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
|
if (args.length != 1) {
|
||||||
|
sender.sendMessage("Usage: join <game>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (sender instanceof Player) {
|
||||||
|
Player player = (Player)sender;
|
||||||
|
String arenaName = args[0];
|
||||||
|
sender.sendMessage("Joining arena '" + arenaName + "'...");
|
||||||
|
Instance runner = m_plugin.getRunnerForArenaName(arenaName);
|
||||||
|
if (runner == null) {
|
||||||
|
sender.sendMessage("No such arena '" + arenaName + "'");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_plugin.addPlayerToArena(arenaName, player);
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("Only players may use this command.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package gg.malloc.defense.commands;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
|
public class LeaveGameCommand implements CommandExecutor {
|
||||||
|
Plugin m_plugin;
|
||||||
|
|
||||||
|
public LeaveGameCommand(Plugin plugin) {
|
||||||
|
m_plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
|
if (sender instanceof Player) {
|
||||||
|
Player player = (Player)sender;
|
||||||
|
Instance runner = m_plugin.getRunnerForPlayer(player);
|
||||||
|
if (runner == null) {
|
||||||
|
sender.sendMessage("You are not currently in a game.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
runner.removePlayer(player);
|
||||||
|
m_plugin.removePlayerFromGames(player);
|
||||||
|
m_plugin.returnPlayerToLobby(player);
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("Only players may use this command.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
|||||||
|
package gg.malloc.defense.commands;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
|
public class ListGamesCommand implements CommandExecutor {
|
||||||
|
Plugin m_plugin;
|
||||||
|
|
||||||
|
public ListGamesCommand(Plugin plugin) {
|
||||||
|
m_plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
|
sender.sendMessage("Available games:");
|
||||||
|
for(String arenaName : m_plugin.arenaNames()) {
|
||||||
|
String stageName = "Ready to play";
|
||||||
|
ChatColor stageColor = ChatColor.WHITE;
|
||||||
|
if (m_plugin.hasRunnerForArenaName(arenaName)) {
|
||||||
|
Instance runner = m_plugin.getRunnerForArenaName(arenaName);
|
||||||
|
switch(runner.getStage()) {
|
||||||
|
case Idle: stageColor = ChatColor.WHITE;stageName = "Open";break;
|
||||||
|
case Warmup: stageColor = ChatColor.YELLOW;stageName = "" + runner.getPlayers().size() + " players warming up";break;
|
||||||
|
case Countdown: stageColor = ChatColor.YELLOW;stageName = "" + runner.getPlayers().size() + " players warming up";break;
|
||||||
|
case Playing: stageColor = ChatColor.GOLD;stageName = "" + runner.getPlayers().size() + " players";break;
|
||||||
|
case GameOver: stageColor = ChatColor.RED;stageName = "Cleaning up...";break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append(" " + arenaName).color(ChatColor.LIGHT_PURPLE).bold(true)
|
||||||
|
.append(" - ").color(ChatColor.WHITE)
|
||||||
|
.append(stageName).color(stageColor).italic(true)
|
||||||
|
.append(" [Join] ").color(ChatColor.GOLD).bold(true)
|
||||||
|
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/malloc-defense:join " + arenaName))
|
||||||
|
.create();
|
||||||
|
sender.spigot().sendMessage(message);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import org.bukkit.command.CommandExecutor;
|
|||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import gg.malloc.defense.engine.GameRunner;
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
import gg.malloc.defense.Plugin;
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
@ -20,9 +20,12 @@ public class PlayerReadyCommand implements CommandExecutor {
|
|||||||
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
if (sender instanceof Player) {
|
if (sender instanceof Player) {
|
||||||
Player player = (Player)sender;
|
Player player = (Player)sender;
|
||||||
GameRunner runner = m_plugin.getRunnerForWorld(player.getLocation().getWorld());
|
Instance runner = m_plugin.getRunnerForPlayer(player);
|
||||||
runner.addPlayer(player);
|
if (runner != null) {
|
||||||
runner.togglePlayerReady(player);
|
runner.togglePlayerReady(player);
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("You are not part of any game.");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
sender.sendMessage("You must be a player to use this command.");
|
sender.sendMessage("You must be a player to use this command.");
|
||||||
|
22
src/main/java/gg/malloc/defense/commands/ReloadCommand.java
Normal file
22
src/main/java/gg/malloc/defense/commands/ReloadCommand.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package gg.malloc.defense.commands;
|
||||||
|
|
||||||
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
public class ReloadCommand implements CommandExecutor {
|
||||||
|
Plugin m_plugin;
|
||||||
|
|
||||||
|
public ReloadCommand(Plugin plugin) {
|
||||||
|
m_plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
|
sender.sendMessage("Reloading arenas...");
|
||||||
|
m_plugin.reloadArenas();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +1,80 @@
|
|||||||
package gg.malloc.defense.commands;
|
package gg.malloc.defense.commands;
|
||||||
|
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabExecutor;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
||||||
import gg.malloc.defense.engine.GameRunner;
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
import gg.malloc.defense.Plugin;
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
public class SetStageCommand implements CommandExecutor {
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SetStageCommand implements TabExecutor {
|
||||||
Plugin m_plugin;
|
Plugin m_plugin;
|
||||||
|
|
||||||
public SetStageCommand(Plugin plugin) {
|
public SetStageCommand(Plugin plugin) {
|
||||||
m_plugin = plugin;
|
m_plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
ArrayList<String> ret = new ArrayList<>();
|
||||||
|
if (args.length == 1) {
|
||||||
|
String proposal = args[0].toLowerCase();
|
||||||
|
Instance.Stage stages[] = Instance.Stage.Idle.getDeclaringClass().getEnumConstants();
|
||||||
|
for(Instance.Stage stage : stages) {
|
||||||
|
if (stage.toString().toLowerCase().startsWith(proposal)) {
|
||||||
|
ret.add(stage.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (args.length == 2) {
|
||||||
|
String proposal = args[1].toLowerCase();
|
||||||
|
for(String arena : m_plugin.arenaNames()) {
|
||||||
|
if (arena.toLowerCase().startsWith(proposal)) {
|
||||||
|
ret.add(arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (args.length == 0) {
|
||||||
|
for(String arena : m_plugin.arenaNames()) {
|
||||||
|
ret.add(arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
|
||||||
World world = m_plugin.getServer().getWorld(args[1]);
|
if (args.length != 2) {
|
||||||
GameRunner runner = m_plugin.getRunnerForWorld(world);
|
sender.sendMessage("Usage: setstage <stage> <arena>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Instance runner = m_plugin.getRunnerForArenaName(args[1]);
|
||||||
|
if (runner == null) {
|
||||||
|
sender.sendMessage("Unknown arena " + args[1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
String stateName = args[0].toLowerCase();
|
String stateName = args[0].toLowerCase();
|
||||||
boolean ret = false;
|
boolean ret = false;
|
||||||
if (stateName.equals("idle")) {
|
Instance.Stage decodedStage = null;
|
||||||
ret = runner.requestTransition(GameRunner.Stage.Idle);
|
Instance.Stage stages[] = Instance.Stage.Idle.getDeclaringClass().getEnumConstants();
|
||||||
} else if (stateName.equals("warmup")) {
|
for(Instance.Stage stage : stages) {
|
||||||
ret = runner.requestTransition(GameRunner.Stage.Warmup);
|
if (stage.toString().toLowerCase().equals(stateName)) {
|
||||||
} else if (stateName.equals("playing")) {
|
decodedStage = stage;
|
||||||
ret = runner.requestTransition(GameRunner.Stage.Playing);
|
break;
|
||||||
} else if (stateName.equals("gameover")) {
|
}
|
||||||
ret = runner.requestTransition(GameRunner.Stage.GameOver);
|
}
|
||||||
|
if (decodedStage != null) {
|
||||||
|
sender.sendMessage("Requesting transition to " + decodedStage);
|
||||||
|
if (!runner.requestTransition(decodedStage)) {
|
||||||
|
sender.sendMessage("Could not transition to " + decodedStage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
sender.sendMessage("Unknown state " + stateName);
|
sender.sendMessage("Unknown state " + stateName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ret) {
|
|
||||||
sender.sendMessage("Could not set state to " + stateName);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
src/main/java/gg/malloc/defense/config/Configuration.java
Normal file
57
src/main/java/gg/malloc/defense/config/Configuration.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package gg.malloc.defense.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Arena;
|
||||||
|
import gg.malloc.defense.model.Waypoint;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
|
public class Configuration {
|
||||||
|
|
||||||
|
ConfigurationSection m_root;
|
||||||
|
|
||||||
|
public Configuration(ConfigurationSection rootConfig) {
|
||||||
|
m_root = rootConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLobbyName() {
|
||||||
|
ConfigurationSection lobbyConf = m_root.getConfigurationSection("lobby");
|
||||||
|
return lobbyConf.getString("world");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getMapNames() {
|
||||||
|
ConfigurationSection mapList = m_root.getConfigurationSection("maps");
|
||||||
|
return mapList.getKeys(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Arena getArenaForMap(String name) {
|
||||||
|
ConfigurationSection mapList = m_root.getConfigurationSection("maps");
|
||||||
|
return makeArena(mapList.getConfigurationSection(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryArena makeArena(ConfigurationSection mapConfig) {
|
||||||
|
List<Map<?, ?>> spawnpointList = mapConfig.getMapList("spawnpoints");
|
||||||
|
ArrayList<Waypoint> spawnpoints = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Map<?, ?> spawnerObj : spawnpointList) {
|
||||||
|
Map<String, Double> thisSpawner = (Map<String, Double>)spawnerObj;
|
||||||
|
double x = thisSpawner.get("x");
|
||||||
|
double y = thisSpawner.get("y");
|
||||||
|
double z = thisSpawner.get("z");
|
||||||
|
spawnpoints.add(new Waypoint(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurationSection targetConfig = mapConfig.getConfigurationSection("target");
|
||||||
|
double x = targetConfig.getDouble("x");
|
||||||
|
double y = targetConfig.getDouble("y");
|
||||||
|
double z = targetConfig.getDouble("z");
|
||||||
|
Waypoint bombTarget = new Waypoint(x, y, z);
|
||||||
|
Waypoint[] spawnArray = new Waypoint[spawnpoints.size()];
|
||||||
|
spawnpoints.toArray(spawnArray);
|
||||||
|
return new MemoryArena(spawnArray, bombTarget);
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/gg/malloc/defense/config/MemoryArena.java
Normal file
27
src/main/java/gg/malloc/defense/config/MemoryArena.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package gg.malloc.defense.config;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Arena;
|
||||||
|
import gg.malloc.defense.model.Waypoint;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
|
public class MemoryArena implements Arena {
|
||||||
|
|
||||||
|
Waypoint[] m_spawnpoints;
|
||||||
|
Waypoint m_bombTarget;
|
||||||
|
|
||||||
|
public MemoryArena(Waypoint[] spawnpoints, Waypoint bombTarget) {
|
||||||
|
m_spawnpoints = spawnpoints;
|
||||||
|
m_bombTarget = bombTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Waypoint[] spawnpoints() {
|
||||||
|
return m_spawnpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Waypoint bombTarget() {
|
||||||
|
return m_bombTarget;
|
||||||
|
}
|
||||||
|
}
|
40
src/main/java/gg/malloc/defense/engine/BombFuse.java
Normal file
40
src/main/java/gg/malloc/defense/engine/BombFuse.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Progress;
|
||||||
|
|
||||||
|
public class BombFuse implements Progress {
|
||||||
|
int m_bombFuseCount = 0;
|
||||||
|
int m_bombFuseTarget = 10;
|
||||||
|
|
||||||
|
public double toDouble() {
|
||||||
|
return Math.max(0.0, Math.min(1.0, (double)m_bombFuseCount / (double)m_bombFuseTarget));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLit() {
|
||||||
|
return m_bombFuseCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExploded() {
|
||||||
|
return m_bombFuseCount > m_bombFuseTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
m_bombFuseCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickLit() {
|
||||||
|
m_bombFuseCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickDecay() {
|
||||||
|
m_bombFuseCount -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value() {
|
||||||
|
return m_bombFuseCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int maximum() {
|
||||||
|
return m_bombFuseTarget;
|
||||||
|
}
|
||||||
|
}
|
@ -1,193 +1,289 @@
|
|||||||
package gg.malloc.defense.engine;
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
import gg.malloc.defense.model.Game;
|
|
||||||
import gg.malloc.defense.model.Arena;
|
import gg.malloc.defense.model.Arena;
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
import gg.malloc.defense.model.Spawner;
|
import gg.malloc.defense.model.Spawner;
|
||||||
import gg.malloc.defense.model.Wave;
|
import gg.malloc.defense.model.Waypoint;
|
||||||
|
import gg.malloc.defense.model.Instance;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
|
||||||
|
import gg.malloc.defense.ui.BossBars;
|
||||||
|
import gg.malloc.defense.ui.Items;
|
||||||
|
import gg.malloc.defense.ui.Sidebars;
|
||||||
|
|
||||||
import gg.malloc.defense.Plugin;
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.GameRule;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Particle;
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
|
import org.bukkit.SoundCategory;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.boss.BarColor;
|
|
||||||
import org.bukkit.boss.BarStyle;
|
|
||||||
import org.bukkit.boss.BossBar;
|
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
|
||||||
import org.bukkit.event.entity.EntityTargetEvent;
|
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent;
|
||||||
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.LivingEntity;
|
|
||||||
import org.bukkit.entity.ArmorStand;
|
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.entity.Projectile;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
public class GameRunner implements Instance {
|
||||||
|
GameState m_state;
|
||||||
|
|
||||||
public class GameRunner {
|
|
||||||
Arena m_arena;
|
Arena m_arena;
|
||||||
Game m_game;
|
Game m_game;
|
||||||
Stage m_stage;
|
Stage m_stage;
|
||||||
Plugin m_plugin;
|
Plugin m_plugin;
|
||||||
|
|
||||||
BossBar m_gameBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID);
|
|
||||||
BossBar m_waveBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID);
|
|
||||||
BossBar m_bombBar = Bukkit.createBossBar("Bomb HP", BarColor.RED, BarStyle.SOLID);
|
|
||||||
|
|
||||||
BukkitTask m_countdownTask;
|
|
||||||
|
|
||||||
MobManager m_mobs;
|
MobManager m_mobs;
|
||||||
WaveManager m_waves;
|
WaveManager m_waves;
|
||||||
PlayerManager m_players;
|
PlayerManager m_players;
|
||||||
|
|
||||||
LivingEntity m_bombTarget = null;
|
BombFuse m_bombFuse;
|
||||||
int m_bombHP = 100;
|
|
||||||
|
|
||||||
Logger m_log;
|
Logger m_log;
|
||||||
|
|
||||||
public enum Stage {
|
TickTask m_fuseTask;
|
||||||
Idle,
|
TickTask m_countdownTask;
|
||||||
Warmup,
|
TickTask m_lobbyReturnTask;
|
||||||
Countdown,
|
TickTask m_bombSmokeTask;
|
||||||
Playing,
|
TickTask m_bombCrackleTask;
|
||||||
GameOver
|
TickTask m_playerRespawnTask;
|
||||||
|
|
||||||
|
BossBars m_bars;
|
||||||
|
Sidebars m_sidebars;
|
||||||
|
|
||||||
|
World m_world;
|
||||||
|
|
||||||
|
Location getLocation(Waypoint waypoint) {
|
||||||
|
return new Location(m_world, waypoint.getX(), waypoint.getY(), waypoint.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
class WaveManager {
|
void validateGameRule(GameRule rule, boolean value) {
|
||||||
int m_currentWaveNum = 0;
|
if (!m_world.getGameRuleValue(rule).equals(value)) {
|
||||||
int m_currentBatch = 0;
|
m_log.warning("Game rule " + rule + " is not " + value);
|
||||||
Wave m_currentWave = null;
|
|
||||||
Game m_game;
|
|
||||||
|
|
||||||
WaveManager(Game game) {
|
|
||||||
m_game = game;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
m_currentWaveNum = 0;
|
|
||||||
m_currentBatch = 0;
|
|
||||||
m_currentWave = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wave currentWave() {
|
|
||||||
return m_currentWave;
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentWaveNum() {
|
|
||||||
return m_currentWaveNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentBatchNum() {
|
|
||||||
return m_currentBatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
double progress() {
|
|
||||||
return (double)m_currentWaveNum / (double)m_game.getWaveCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isLastWave() {
|
|
||||||
return m_currentWaveNum >= m_game.getWaveCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isLastBatch() {
|
|
||||||
return m_currentBatch >= m_currentWave.batchCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nextBatch() {
|
|
||||||
m_currentBatch += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void next() {
|
|
||||||
m_currentWaveNum += 1;
|
|
||||||
m_currentBatch = 0;
|
|
||||||
m_currentWave = m_game.getWave(m_currentWaveNum);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validateGameRules() {
|
||||||
|
validateGameRule(GameRule.DO_MOB_LOOT, false);
|
||||||
|
validateGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false);
|
||||||
|
validateGameRule(GameRule.DISABLE_RAIDS, true);
|
||||||
|
validateGameRule(GameRule.DO_DAYLIGHT_CYCLE, false);
|
||||||
|
validateGameRule(GameRule.DO_ENTITY_DROPS, false);
|
||||||
|
validateGameRule(GameRule.DO_FIRE_TICK, false);
|
||||||
|
validateGameRule(GameRule.DO_INSOMNIA, false);
|
||||||
|
validateGameRule(GameRule.DO_WEATHER_CYCLE, false);
|
||||||
|
validateGameRule(GameRule.SPECTATORS_GENERATE_CHUNKS, false);
|
||||||
|
}
|
||||||
|
|
||||||
public GameRunner(Plugin plugin, Game game, Arena arena) {
|
public GameState getState() {
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRunner(Plugin plugin, Game game, Arena arena, World world) {
|
||||||
m_plugin = plugin;
|
m_plugin = plugin;
|
||||||
m_game = game;
|
m_game = game;
|
||||||
|
m_world = world;
|
||||||
m_arena = arena;
|
m_arena = arena;
|
||||||
m_stage = Stage.Idle;
|
m_stage = Stage.Idle;
|
||||||
m_gameBar.setVisible(true);
|
m_mobs = new MobManager();
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
m_mobs = new MobManager(m_game);
|
|
||||||
m_waves = new WaveManager(m_game);
|
m_waves = new WaveManager(m_game);
|
||||||
m_players = new PlayerManager();
|
m_players = new PlayerManager();
|
||||||
|
m_bombFuse = new BombFuse();
|
||||||
|
|
||||||
|
m_state = new GameState(this, m_waves, m_game, m_mobs, m_bombFuse, m_players);
|
||||||
|
|
||||||
|
m_bars = new BossBars(m_state);
|
||||||
|
m_sidebars = new Sidebars(m_state, m_plugin.getServer().getScoreboardManager());
|
||||||
|
|
||||||
m_log = m_plugin.getLogger();
|
m_log = m_plugin.getLogger();
|
||||||
}
|
|
||||||
|
|
||||||
int m_warmupCountdown = 0;
|
m_fuseTask = new TickTask(m_plugin, () -> {
|
||||||
|
if (m_bombFuse.isLit()) {
|
||||||
|
m_bombFuse.tickDecay();
|
||||||
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
|
}
|
||||||
|
}, 80);
|
||||||
|
|
||||||
public void handleEntityRetargeting(EntityTargetEvent evt) {
|
m_playerRespawnTask = new TickTask(m_plugin, () -> {
|
||||||
if (m_mobs.contains(evt.getEntity()) && evt.getReason() == EntityTargetEvent.TargetReason.FORGOT_TARGET) {
|
m_players.tickRespawnCounters();
|
||||||
evt.setTarget(m_bombTarget);
|
for (Player p : m_players.getPlayers()) {
|
||||||
|
if (m_players.readyToRespawn(p)) {
|
||||||
|
if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
|
||||||
|
sendRespawnTitle(p);
|
||||||
|
p.teleport(m_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
m_bars.update();
|
||||||
|
}, 20);
|
||||||
|
|
||||||
public void handleEntityDamage(EntityDamageEvent evt) {
|
m_countdownTask = new TickTask(m_plugin, () -> {
|
||||||
if (evt.getEntity() == m_bombTarget && evt instanceof EntityDamageByEntityEvent) {
|
|
||||||
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
|
||||||
if (m_mobs.contains(entityEvt.getDamager())) {
|
|
||||||
entityEvt.getDamager().setGlowing(true);
|
|
||||||
m_bombHP -= 1;
|
|
||||||
broadcastMessage("The bomb has been struck!");
|
|
||||||
m_arena.getWorld().playSound(m_bombTarget.getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1.5f, 0.9f);
|
|
||||||
updateMobBars();
|
|
||||||
if (m_bombHP <= 0) {
|
|
||||||
m_arena.getWorld().strikeLightningEffect(m_bombTarget.getLocation());
|
|
||||||
requestTransition(Stage.GameOver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evt.setCancelled(true);
|
|
||||||
} else {
|
|
||||||
m_mobs.handleEntityDamage(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void countdownTick() {
|
|
||||||
if (m_warmupCountdown == 0) {
|
if (m_warmupCountdown == 0) {
|
||||||
requestTransition(Stage.Playing);
|
requestTransition(Stage.Playing);
|
||||||
} else {
|
} else {
|
||||||
updateMobBars();
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
broadcastMessage("Starting game in " + m_warmupCountdown);
|
.append("Starting game in ").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.append("" + m_warmupCountdown).color(ChatColor.AQUA).bold(true)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
m_warmupCountdown--;
|
m_warmupCountdown--;
|
||||||
m_countdownTask = m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
m_bars.setCountdownProgress((double)m_warmupCountdown / 10.0);
|
||||||
countdownTick();
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
|
}
|
||||||
}, 20);
|
}, 20);
|
||||||
|
|
||||||
|
m_lobbyReturnTask = new TickTask(m_plugin, () -> {
|
||||||
|
if (m_gameEndCountdown == 0) {
|
||||||
|
for(Player p : new ArrayList<Player>(m_players.getPlayers())) {
|
||||||
|
removePlayer(p);
|
||||||
|
m_plugin.returnPlayerToLobby(p);
|
||||||
|
}
|
||||||
|
requestTransition(Stage.Idle);
|
||||||
|
} else {
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append("Game is ending in ").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.append("" + m_gameEndCountdown).color(ChatColor.RED).bold(true)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
|
m_gameEndCountdown--;
|
||||||
|
m_bars.setCountdownProgress((double)m_gameEndCountdown / 60.0);
|
||||||
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
|
}
|
||||||
|
}, 20);
|
||||||
|
|
||||||
|
m_bombSmokeTask = new TickTask(m_plugin, () -> {
|
||||||
|
Location targetLoc = getLocation(m_arena.bombTarget());
|
||||||
|
m_world.spawnParticle(Particle.SMOKE_LARGE, targetLoc, 135, 4, 2, 4, 1);
|
||||||
|
m_world.spawnParticle(Particle.SMALL_FLAME, targetLoc, 130, 3, 2, 3, 1);
|
||||||
|
}, 5);
|
||||||
|
m_bombCrackleTask = new TickTask(m_plugin, () -> {
|
||||||
|
Location targetLoc = getLocation(m_arena.bombTarget());
|
||||||
|
m_world.playSound(targetLoc, Sound.BLOCK_CAMPFIRE_CRACKLE, SoundCategory.NEUTRAL, 1.0f, 1.0f);
|
||||||
|
}, 35);
|
||||||
|
|
||||||
|
validateGameRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_warmupCountdown = 0;
|
||||||
|
int m_gameEndCountdown = 0;
|
||||||
|
|
||||||
|
public void handleEntityRetargeting(EntityTargetEvent evt) {
|
||||||
|
m_mobs.handleEntityRetarget(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spendMoney(Player p, int amount) {
|
||||||
|
m_players.addPlayerExpenses(p, amount);
|
||||||
|
m_sidebars.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void depositCoins(int amount) {
|
||||||
|
m_state.addPickedUpCoins(amount);
|
||||||
|
m_sidebars.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleEntityDamage(EntityDamageEvent evt) {
|
||||||
|
m_mobs.handleEntityDamage(evt);
|
||||||
|
if (m_mobs.bombWasHit() && !m_bombFuse.isExploded()) {
|
||||||
|
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
||||||
|
m_log.info("Target attacked!");
|
||||||
|
entityEvt.getDamager().setGlowing(true);
|
||||||
|
m_bombFuse.tickLit();
|
||||||
|
m_world.playSound(getLocation(m_arena.bombTarget()), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1.5f, 0.9f);
|
||||||
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
|
if (m_bombFuse.isExploded()) {
|
||||||
|
m_world.strikeLightningEffect(getLocation(m_arena.bombTarget()));
|
||||||
|
m_world.playSound(getLocation(m_arena.bombTarget()), Sound.ENTITY_GENERIC_EXPLODE, SoundCategory.NEUTRAL, 1.3f, 1.0f);
|
||||||
|
m_world.spawnParticle(Particle.EXPLOSION_HUGE, getLocation(m_arena.bombTarget()), 8, 5, 2, 5);
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append("The bomb blew up!").color(ChatColor.RED).bold(true)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
|
requestTransition(Stage.GameOver);
|
||||||
|
m_bombSmokeTask.start();
|
||||||
|
m_bombCrackleTask.start();
|
||||||
|
}
|
||||||
|
} else if (evt.getEntity() instanceof Player) {
|
||||||
|
Player player = (Player)evt.getEntity();
|
||||||
|
if (evt instanceof EntityDamageByEntityEvent) {
|
||||||
|
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
||||||
|
if (entityEvt.getDamager() instanceof Player) {
|
||||||
|
evt.setCancelled(true);
|
||||||
|
return;
|
||||||
|
} else if (entityEvt.getDamager() instanceof Projectile) {
|
||||||
|
Projectile asProjectile = (Projectile)entityEvt.getDamager();
|
||||||
|
if (asProjectile.getShooter() instanceof Player) {
|
||||||
|
evt.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_players.contains(player) && player.getHealth() - evt.getFinalDamage() <= 0 && m_stage == Stage.Playing) {
|
||||||
|
evt.setCancelled(true);
|
||||||
|
handlePlayerDeath(player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterIdle() {
|
private boolean enterIdle() {
|
||||||
m_bombHP = 100;
|
m_bombFuse.reset();
|
||||||
m_waves.reset();
|
m_waves.reset();
|
||||||
m_mobs.clear();
|
m_mobs.clear();
|
||||||
|
m_bombSmokeTask.stop();
|
||||||
|
m_bombCrackleTask.stop();
|
||||||
m_players.requestTransitionForAll(PlayerManager.State.Idle);
|
m_players.requestTransitionForAll(PlayerManager.State.Idle);
|
||||||
if (m_bombTarget != null) {
|
m_fuseTask.stop();
|
||||||
m_bombTarget.remove();
|
m_countdownTask.stop();
|
||||||
m_bombTarget = null;
|
m_lobbyReturnTask.stop();
|
||||||
}
|
m_playerRespawnTask.stop();
|
||||||
if (m_countdownTask != null) {
|
m_state.resetBalances();
|
||||||
m_countdownTask.cancel();
|
|
||||||
m_countdownTask = null;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UUID m_id = UUID.randomUUID();
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Player> getPlayers() {
|
||||||
|
return m_players.getPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean enterWarmup() {
|
private boolean enterWarmup() {
|
||||||
m_waves.next();
|
m_waves.next();
|
||||||
for(Player p : m_players.getPlayers()) {
|
for(Player p : m_players.getPlayers()) {
|
||||||
if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
|
if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
|
||||||
p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
p.teleport(m_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
}
|
}
|
||||||
m_players.setReady(p, false);
|
m_players.setReady(p, false);
|
||||||
|
m_players.healPlayer(p);
|
||||||
}
|
}
|
||||||
broadcastTitle("Warmup", "Prepare yourself for wave " + m_waves.currentWaveNum());
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append("Click").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.append(" [Here] ").color(ChatColor.GOLD).bold(true)
|
||||||
|
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/malloc-defense:ready"))
|
||||||
|
.append("when ready.").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
m_mobs.clear();
|
m_mobs.clear();
|
||||||
|
m_playerRespawnTask.start();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,8 +291,25 @@ public class GameRunner {
|
|||||||
setPlayerReady(p, !m_players.isReady(p));
|
setPlayerReady(p, !m_players.isReady(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearReadyState() {
|
||||||
|
for(Player p : m_players.getPlayers()) {
|
||||||
|
setPlayerReady(p, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setPlayerReady(Player p, boolean isReady) {
|
public void setPlayerReady(Player p, boolean isReady) {
|
||||||
|
if (isReady != m_players.isReady(p)) {
|
||||||
|
if (isReady) {
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append(p.getName()).bold(true)
|
||||||
|
.append(" is ready!").color(ChatColor.AQUA).italic(true)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
|
}
|
||||||
m_players.setReady(p, isReady);
|
m_players.setReady(p, isReady);
|
||||||
|
}
|
||||||
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
if (m_players.isEveryoneReady()) {
|
if (m_players.isEveryoneReady()) {
|
||||||
requestTransition(Stage.Countdown);
|
requestTransition(Stage.Countdown);
|
||||||
}
|
}
|
||||||
@ -204,125 +317,99 @@ public class GameRunner {
|
|||||||
|
|
||||||
private boolean enterCountdown() {
|
private boolean enterCountdown() {
|
||||||
m_warmupCountdown = 10;
|
m_warmupCountdown = 10;
|
||||||
countdownTick();
|
m_bars.setCountdownProgress(1.0);
|
||||||
|
m_countdownTask.start();
|
||||||
|
m_playerRespawnTask.start();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterPlaying() {
|
private boolean enterPlaying() {
|
||||||
m_log.info("Starting wave " + m_waves.currentWaveNum());
|
m_log.info("Starting wave " + m_waves.currentWaveNum());
|
||||||
if (m_bombTarget == null) {
|
m_mobs.spawnTarget(getLocation(m_arena.bombTarget()));
|
||||||
m_bombTarget = (LivingEntity)m_arena.getWorld().spawnEntity(m_arena.bombTarget().getLocation(), EntityType.ARMOR_STAND);
|
// TODO: Set helmet with custom model data
|
||||||
}
|
|
||||||
ArmorStand bombStand = (ArmorStand)m_bombTarget;
|
|
||||||
bombStand.setCustomName("Bomb Target");
|
|
||||||
bombStand.setVisible(true);
|
|
||||||
bombStand.setCustomNameVisible(true);
|
|
||||||
bombStand.setGlowing(true);
|
|
||||||
for(Player p : m_players.getPlayers()) {
|
for(Player p : m_players.getPlayers()) {
|
||||||
if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
|
if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
|
||||||
p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
p.teleport(m_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_countdownTask.stop();
|
||||||
|
m_fuseTask.start();
|
||||||
|
m_playerRespawnTask.start();
|
||||||
spawnNextBatch();
|
spawnNextBatch();
|
||||||
broadcastTitle("Wave " + m_waves.currentWaveNum());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterGameOver() {
|
private boolean enterGameOver() {
|
||||||
broadcastTitle("Game Over!");
|
m_gameEndCountdown = 60;
|
||||||
if (m_countdownTask != null) {
|
m_lobbyReturnTask.start();
|
||||||
m_countdownTask.cancel();
|
m_countdownTask.stop();
|
||||||
m_countdownTask = null;
|
m_fuseTask.stop();
|
||||||
}
|
m_playerRespawnTask.stop();
|
||||||
m_mobs.clear();
|
|
||||||
for(Player p : m_players.getPlayers()) {
|
|
||||||
removePlayer(p);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMobBars() {
|
public Stage getStage() {
|
||||||
m_gameBar.setVisible(true);
|
return m_stage;
|
||||||
switch(m_stage) {
|
|
||||||
case Idle:
|
|
||||||
m_gameBar.setProgress(1.0);
|
|
||||||
m_gameBar.setTitle("Waiting for players...");
|
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
break;
|
|
||||||
case Warmup:
|
|
||||||
m_gameBar.setProgress(m_waves.progress());
|
|
||||||
m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount());
|
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
|
||||||
m_waveBar.setVisible(true);
|
|
||||||
m_waveBar.setColor(BarColor.BLUE);
|
|
||||||
m_waveBar.setTitle("Warmup - Waiting for players to get ready...");
|
|
||||||
m_waveBar.setProgress(m_players.readyProgress());
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
break;
|
|
||||||
case Countdown:
|
|
||||||
m_gameBar.setProgress(m_waves.progress());
|
|
||||||
m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount());
|
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
|
||||||
m_waveBar.setVisible(true);
|
|
||||||
m_waveBar.setColor(BarColor.YELLOW);
|
|
||||||
m_waveBar.setTitle("Wave starting!");
|
|
||||||
m_waveBar.setProgress((double)m_warmupCountdown / (double)10);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
break;
|
|
||||||
case Playing:
|
|
||||||
m_gameBar.setProgress(m_waves.progress());
|
|
||||||
m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount());
|
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
|
||||||
if (m_mobs.createdMobs() > 0) {
|
|
||||||
m_waveBar.setVisible(true);
|
|
||||||
m_waveBar.setColor(BarColor.GREEN);
|
|
||||||
m_waveBar.setTitle("Mobs remaining: " + m_mobs.remainingMobs());
|
|
||||||
m_waveBar.setProgress(m_mobs.progress());
|
|
||||||
} else {
|
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
}
|
|
||||||
m_bombBar.setVisible(true);
|
|
||||||
m_bombBar.setProgress((double)m_bombHP / (double)100);
|
|
||||||
break;
|
|
||||||
case GameOver:
|
|
||||||
m_gameBar.setColor(BarColor.RED);
|
|
||||||
m_gameBar.setProgress(1.0);
|
|
||||||
m_gameBar.setTitle("Game Over!");
|
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnNextBatch() {
|
private void spawnNextBatch() {
|
||||||
broadcastMessage("Spawning batch " + m_waves.currentBatchNum());
|
m_log.info("Spawning batch " + m_waves.currentBatchNum());
|
||||||
Spawner spawner = new GameSpawner(m_arena, m_mobs, m_players, m_bombTarget);
|
Spawner spawner = new GameSpawner(m_world, m_arena, m_mobs);
|
||||||
m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum());
|
m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum());
|
||||||
updateMobBars();
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handlePlayerDeath(Player player) {
|
private void handlePlayerDeath(Player player) {
|
||||||
if (m_players.requestTransition(player, PlayerManager.State.Dead)) {
|
if (m_players.requestTransition(player, PlayerManager.State.Dead)) {
|
||||||
m_arena.getWorld().strikeLightningEffect(player.getLocation());
|
m_world.strikeLightningEffect(player.getLocation());
|
||||||
if (!m_players.isAnyoneAlive()) {
|
//if (!m_players.isAnyoneAlive()) {
|
||||||
broadcastMessage("Everyone is dead :(");
|
if (false) {
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append("Everyone is ").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.append("DEAD").color(ChatColor.RED).bold(true)
|
||||||
|
.append(" :(").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
requestTransition(Stage.GameOver);
|
requestTransition(Stage.GameOver);
|
||||||
} else {
|
} else {
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append(player.getName())
|
||||||
|
.append(" has ").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.append("DIED!").color(ChatColor.RED).bold(true)
|
||||||
|
.append(" :(").color(ChatColor.LIGHT_PURPLE)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
|
sendDeathTitle(player);
|
||||||
m_log.info("Remaining players " + m_players.remainingPlayers());
|
m_log.info("Remaining players " + m_players.remainingPlayers());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleEntityDeath(Entity entity) {
|
public void handleEntityDeath(Entity entity) {
|
||||||
|
boolean wasCarrier = m_mobs.isBombCarrier(entity);
|
||||||
if (m_mobs.killMob(entity)) {
|
if (m_mobs.killMob(entity)) {
|
||||||
broadcastMessage("Killed game entity " + entity);
|
int COIN_STACK_SIZE = 5;
|
||||||
updateMobBars();
|
int coinsToDrop = 60;
|
||||||
|
while(coinsToDrop > 0) {
|
||||||
|
int droppedCoins = Math.min(coinsToDrop, COIN_STACK_SIZE);
|
||||||
|
ItemStack coins = Items.makeCoins();
|
||||||
|
coins.setAmount(droppedCoins);
|
||||||
|
coinsToDrop -= droppedCoins;
|
||||||
|
m_state.addDroppedCoins(droppedCoins);
|
||||||
|
m_sidebars.update();
|
||||||
|
m_world.dropItem(entity.getLocation(), coins);
|
||||||
|
}
|
||||||
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
if (m_mobs.remainingMobs() <= 3) {
|
if (m_mobs.remainingMobs() <= 3) {
|
||||||
m_log.info("Starting next batch!");
|
m_log.info("Starting next batch!");
|
||||||
if (m_waves.isLastBatch()) {
|
if (m_waves.isLastBatch()) {
|
||||||
if (m_waves.isLastWave()) {
|
if (m_waves.isLastWave()) {
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append("You Won!").color(ChatColor.LIGHT_PURPLE).bold(true)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
requestTransition(Stage.GameOver);
|
requestTransition(Stage.GameOver);
|
||||||
} else if (m_mobs.empty()) {
|
} else if (m_mobs.empty()) {
|
||||||
requestTransition(Stage.Warmup);
|
requestTransition(Stage.Warmup);
|
||||||
@ -341,7 +428,6 @@ public class GameRunner {
|
|||||||
if (m_stage == stage) {
|
if (m_stage == stage) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_log.info("Game state: " + stage);
|
|
||||||
switch(stage) {
|
switch(stage) {
|
||||||
case Idle:
|
case Idle:
|
||||||
return enterIdle();
|
return enterIdle();
|
||||||
@ -360,11 +446,11 @@ public class GameRunner {
|
|||||||
private boolean validateTransition(Stage from, Stage to) {
|
private boolean validateTransition(Stage from, Stage to) {
|
||||||
switch(from) {
|
switch(from) {
|
||||||
case Idle:
|
case Idle:
|
||||||
return to == Stage.Warmup;
|
return !m_players.isEmpty() && to == Stage.Warmup;
|
||||||
case Warmup:
|
case Warmup:
|
||||||
return to == Stage.Playing || to == Stage.Idle || to == Stage.Countdown ;
|
return to == Stage.Playing || to == Stage.Idle || to == Stage.Countdown || to == Stage.GameOver;
|
||||||
case Countdown:
|
case Countdown:
|
||||||
return to == Stage.Playing || to == Stage.Idle || to == Stage.Warmup;
|
return to == Stage.Playing || to == Stage.Idle || to == Stage.Warmup || to == Stage.GameOver;
|
||||||
case Playing:
|
case Playing:
|
||||||
return to == Stage.Warmup || to == Stage.GameOver || to == Stage.Idle;
|
return to == Stage.Warmup || to == Stage.GameOver || to == Stage.Idle;
|
||||||
case GameOver:
|
case GameOver:
|
||||||
@ -381,23 +467,56 @@ public class GameRunner {
|
|||||||
if (attemptTransition(stage)) {
|
if (attemptTransition(stage)) {
|
||||||
m_log.info("Game transition: " + m_stage + " -> " + stage);
|
m_log.info("Game transition: " + m_stage + " -> " + stage);
|
||||||
m_stage = stage;
|
m_stage = stage;
|
||||||
updateMobBars();
|
m_bars.update();
|
||||||
|
m_sidebars.update();
|
||||||
|
for(Player p : m_players.getPlayers()) {
|
||||||
|
sendStageTitle(p);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
m_log.severe("Failed to complete transition: " + m_stage + " -> " + stage);
|
m_log.severe("Failed to complete transition: " + m_stage + " -> " + stage);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendDeathTitle(Player p) {
|
||||||
|
p.sendTitle(ChatColor.RED.toString() + "You died!", "Wait to respawn...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendRespawnTitle(Player p) {
|
||||||
|
p.sendTitle(ChatColor.AQUA.toString() + "Respawn", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendStageTitle(Player p) {
|
||||||
|
switch(m_stage) {
|
||||||
|
case Warmup:
|
||||||
|
p.sendTitle(ChatColor.YELLOW.toString() + "Warmup", ChatColor.LIGHT_PURPLE.toString() + "Prepare yourself for wave " + m_waves.currentWaveNum());
|
||||||
|
break;
|
||||||
|
case Playing:
|
||||||
|
p.sendTitle(ChatColor.AQUA.toString() + "Wave " + m_waves.currentWaveNum(), "");
|
||||||
|
break;
|
||||||
|
case GameOver:
|
||||||
|
p.sendTitle(ChatColor.RED.toString() + "Game over!", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addPlayer(Player p) {
|
public void addPlayer(Player p) {
|
||||||
|
if (m_players.contains(p)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p.getInventory().clear();
|
||||||
|
m_sidebars.addPlayer(p);
|
||||||
m_players.addPlayer(p);
|
m_players.addPlayer(p);
|
||||||
m_gameBar.addPlayer(p);
|
m_bars.addPlayer(p);
|
||||||
m_waveBar.addPlayer(p);
|
|
||||||
m_bombBar.addPlayer(p);
|
|
||||||
if (m_stage == Stage.Idle || m_stage == Stage.Warmup) {
|
if (m_stage == Stage.Idle || m_stage == Stage.Warmup) {
|
||||||
if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
|
if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
|
||||||
p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
p.teleport(m_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
}
|
}
|
||||||
broadcastMessage(p.getName() + " has joined the game");
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append(p.getName()).bold(true)
|
||||||
|
.append(" has joined the game").color(ChatColor.AQUA)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
|
sendStageTitle(p);
|
||||||
if (m_stage == Stage.Idle) {
|
if (m_stage == Stage.Idle) {
|
||||||
requestTransition(Stage.Warmup);
|
requestTransition(Stage.Warmup);
|
||||||
}
|
}
|
||||||
@ -405,11 +524,18 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removePlayer(Player p) {
|
public void removePlayer(Player p) {
|
||||||
m_gameBar.removePlayer(p);
|
p.getInventory().clear();
|
||||||
m_waveBar.removePlayer(p);
|
m_sidebars.removePlayer(p);
|
||||||
|
m_bars.removePlayer(p);
|
||||||
m_players.removePlayer(p);
|
m_players.removePlayer(p);
|
||||||
if (m_players.isEmpty()) {
|
if (m_players.isEmpty()) {
|
||||||
requestTransition(Stage.Idle);
|
requestTransition(Stage.Idle);
|
||||||
|
} else {
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.append(p.getName()).bold(true)
|
||||||
|
.append("Has left the game").color(ChatColor.AQUA)
|
||||||
|
.create();
|
||||||
|
broadcastMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,18 +545,13 @@ public class GameRunner {
|
|||||||
|
|
||||||
void broadcastTitle(String title, String subtitle) {
|
void broadcastTitle(String title, String subtitle) {
|
||||||
for(Player p : m_players.getPlayers()) {
|
for(Player p : m_players.getPlayers()) {
|
||||||
p.sendTitle(title, subtitle, 10, 70, 20);
|
p.sendTitle(title, subtitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void broadcastMessage(String string) {
|
void broadcastMessage(BaseComponent[] component) {
|
||||||
World world = m_arena.getWorld();
|
for(Player p : m_world.getPlayers()) {
|
||||||
for(Player p : world.getPlayers()) {
|
p.spigot().sendMessage(component);
|
||||||
p.sendMessage(string);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerSpawnedMob(Entity entity) {
|
|
||||||
m_mobs.addEntity(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,58 @@
|
|||||||
package gg.malloc.defense.engine;
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
import gg.malloc.defense.model.Spawner;
|
import gg.malloc.defense.model.Spawner;
|
||||||
import gg.malloc.defense.model.Spawnpoint;
|
import gg.malloc.defense.model.Waypoint;
|
||||||
import gg.malloc.defense.model.Arena;
|
import gg.malloc.defense.model.Arena;
|
||||||
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.LivingEntity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Mob;
|
import org.bukkit.entity.Mob;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
import org.bukkit.attribute.AttributeModifier;
|
||||||
|
|
||||||
public class GameSpawner implements Spawner {
|
public class GameSpawner implements Spawner {
|
||||||
Arena m_arena;
|
Arena m_arena;
|
||||||
MobManager m_manager;
|
MobManager m_manager;
|
||||||
PlayerManager m_players;
|
|
||||||
LivingEntity m_bombTarget;
|
|
||||||
int m_spawnIdx = 0;
|
int m_spawnIdx = 0;
|
||||||
|
World m_world;
|
||||||
|
|
||||||
public GameSpawner(Arena arena, MobManager manager, PlayerManager players, LivingEntity bombTarget) {
|
public GameSpawner(World world, Arena arena, MobManager manager) {
|
||||||
|
m_world = world;
|
||||||
m_arena = arena;
|
m_arena = arena;
|
||||||
m_manager = manager;
|
m_manager = manager;
|
||||||
m_players = players;
|
|
||||||
m_bombTarget = bombTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entity spawnMob(EntityType type) {
|
public LivingEntity spawnBombCarrier(EntityType type) {
|
||||||
Spawnpoint[] spawnpoints = m_arena.spawnpoints();
|
if (m_manager.bombCount() == 0) {
|
||||||
|
return m_manager.addBombCarrier(spawnMob(type, MobManager.Goal.BombCarrier));
|
||||||
|
} else {
|
||||||
|
return spawnMob(type, MobManager.Goal.BombCarrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LivingEntity spawnMob(EntityType type) {
|
||||||
|
return spawnMob(type, MobManager.Goal.PlayerHarassment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mob spawnMob(EntityType type, MobManager.Goal goal) {
|
||||||
|
Waypoint[] spawnpoints = m_arena.spawnpoints();
|
||||||
m_spawnIdx %= spawnpoints.length;
|
m_spawnIdx %= spawnpoints.length;
|
||||||
//m_log.fine("Spawning " + type + " at " + spawnpoints[m_spawnIdx]);
|
//m_log.fine("Spawning " + type + " at " + spawnpoints[m_spawnIdx]);
|
||||||
Entity newMob = m_arena.getWorld().spawnEntity(spawnpoints[m_spawnIdx].getLocation(), type);
|
Waypoint thisSpawner = spawnpoints[m_spawnIdx];
|
||||||
LivingEntity livingMob = (LivingEntity)newMob;
|
Location loc = new Location(m_world, thisSpawner.getX(), thisSpawner.getY(), thisSpawner.getZ());
|
||||||
livingMob.setRemoveWhenFarAway(false);
|
Mob newMob = (Mob)m_world.spawnEntity(loc, type);
|
||||||
m_manager.addEntity(newMob);
|
newMob.setRemoveWhenFarAway(false);
|
||||||
|
m_manager.addEntity(newMob, goal);
|
||||||
m_spawnIdx += 1;
|
m_spawnIdx += 1;
|
||||||
((Mob)newMob).setTarget(m_bombTarget);
|
|
||||||
return newMob;
|
return newMob;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
89
src/main/java/gg/malloc/defense/engine/GameState.java
Normal file
89
src/main/java/gg/malloc/defense/engine/GameState.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
|
import gg.malloc.defense.model.Progress;
|
||||||
|
import gg.malloc.defense.model.State;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class GameState implements State {
|
||||||
|
GameRunner m_runner;
|
||||||
|
WaveManager m_waves;
|
||||||
|
Game m_game;
|
||||||
|
MobManager m_mobs;
|
||||||
|
BombFuse m_fuse;
|
||||||
|
PlayerManager m_players;
|
||||||
|
|
||||||
|
int m_coinsDropped;
|
||||||
|
int m_coinsPickedUp;
|
||||||
|
|
||||||
|
public GameState(GameRunner runner, WaveManager waves, Game game, MobManager mobs, BombFuse fuse, PlayerManager players) {
|
||||||
|
m_runner = runner;
|
||||||
|
m_waves = waves;
|
||||||
|
m_game = game;
|
||||||
|
m_mobs = mobs;
|
||||||
|
m_fuse = fuse;
|
||||||
|
m_players = players;
|
||||||
|
m_coinsDropped = 0;
|
||||||
|
m_coinsPickedUp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemainingCoins() {
|
||||||
|
return coinsDropped() - coinsPickedUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int coinsDropped() {
|
||||||
|
return m_coinsDropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int coinsPickedUp() {
|
||||||
|
return m_coinsPickedUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int INITIAL_BALANCE = 1500;
|
||||||
|
|
||||||
|
public int getPlayerBalance(Player p) {
|
||||||
|
return coinsPickedUp() - m_players.getPlayerExpenses(p) + INITIAL_BALANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDroppedCoins(int v) {
|
||||||
|
m_coinsDropped += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPickedUpCoins(int v) {
|
||||||
|
m_coinsPickedUp += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetBalances() {
|
||||||
|
m_coinsDropped = 0;
|
||||||
|
m_coinsPickedUp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Progress playerRespawnProgress(Player p) {
|
||||||
|
return m_players.getRespawnProgress(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Progress coinProgress() {
|
||||||
|
return new StaticProgress(m_coinsPickedUp, m_coinsDropped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRunner.Stage getStage() {
|
||||||
|
return m_runner.getStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Progress fuseProgress() {
|
||||||
|
return m_fuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Progress waveProgress() {
|
||||||
|
return m_waves.asProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Progress mobProgress() {
|
||||||
|
return m_mobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Progress playerReadyProgress() {
|
||||||
|
return m_players.readyProgress();
|
||||||
|
}
|
||||||
|
}
|
@ -1,49 +1,85 @@
|
|||||||
package gg.malloc.defense.engine;
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.ArmorStand;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.Mob;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
|
||||||
import gg.malloc.defense.model.Game;
|
import gg.malloc.defense.model.Game;
|
||||||
|
import gg.malloc.defense.model.Progress;
|
||||||
|
|
||||||
|
import gg.malloc.defense.ui.BombCarrier;
|
||||||
|
import gg.malloc.defense.ui.Items;
|
||||||
|
|
||||||
|
public class MobManager implements Progress {
|
||||||
|
LivingEntity m_bombTarget;
|
||||||
|
HashSet<Mob> m_livingMobs = new HashSet<>();
|
||||||
|
HashSet<Mob> m_bombCarriers = new HashSet<>();
|
||||||
|
HashSet<LivingEntity> m_droppedBombs = new HashSet<>();
|
||||||
|
HashMap<Mob, Goal> m_mobGoals = new HashMap<>();
|
||||||
|
|
||||||
|
public enum Goal {
|
||||||
|
BombCarrier,
|
||||||
|
PlayerHarassment
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int value() {
|
||||||
|
return m_killedMobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int maximum() {
|
||||||
|
return m_createdMobs;
|
||||||
|
}
|
||||||
|
|
||||||
public class MobManager {
|
|
||||||
HashSet<Entity> m_livingMobs = new HashSet<>();
|
|
||||||
int m_createdMobs = 0;
|
int m_createdMobs = 0;
|
||||||
int m_killedMobs = 0;
|
int m_killedMobs = 0;
|
||||||
|
|
||||||
Game m_game;
|
|
||||||
|
|
||||||
MobManager(Game game) {
|
|
||||||
m_game = game;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleEntityDamage(EntityDamageEvent evt) {
|
|
||||||
Entity entity = evt.getEntity();
|
|
||||||
//m_log.info("Damage " + entity);
|
|
||||||
//m_log.info("Living Mobs " + m_livingMobs);
|
|
||||||
if (m_livingMobs.contains(entity)) {
|
|
||||||
m_game.onMobDamaged(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(Entity entity) {
|
public boolean contains(Entity entity) {
|
||||||
return m_livingMobs.contains(entity);
|
return m_livingMobs.contains(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBombCarrier(Entity entity) {
|
||||||
|
return m_bombCarriers.contains(entity);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean killMob(Entity entity) {
|
public boolean killMob(Entity entity) {
|
||||||
if (m_livingMobs.contains(entity)) {
|
if (m_livingMobs.contains(entity)) {
|
||||||
m_killedMobs += 1;
|
m_killedMobs += 1;
|
||||||
|
if (m_bombCarriers.remove(entity)) {
|
||||||
|
dropBomb(entity.getLocation());
|
||||||
|
}
|
||||||
return m_livingMobs.remove(entity);
|
return m_livingMobs.remove(entity);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
for(Entity e : m_livingMobs) {
|
for(Mob e : m_livingMobs) {
|
||||||
e.remove();
|
e.remove();
|
||||||
}
|
}
|
||||||
|
for(LivingEntity e : m_droppedBombs) {
|
||||||
|
e.remove();
|
||||||
|
}
|
||||||
|
if (m_bombTarget != null) {
|
||||||
|
m_bombTarget.remove();
|
||||||
|
}
|
||||||
|
m_bombTarget = null;
|
||||||
|
m_droppedBombs.clear();
|
||||||
m_livingMobs.clear();
|
m_livingMobs.clear();
|
||||||
|
m_bombCarriers.clear();
|
||||||
m_createdMobs = 0;
|
m_createdMobs = 0;
|
||||||
m_killedMobs = 0;
|
m_killedMobs = 0;
|
||||||
}
|
}
|
||||||
@ -64,13 +100,160 @@ public class MobManager {
|
|||||||
return (double)m_killedMobs / (double)m_createdMobs;
|
return (double)m_killedMobs / (double)m_createdMobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEntity(Entity entity) {
|
Player getNearestPlayer(Location location) {
|
||||||
//m_log.fine("Registered new mob " + entity);
|
Player nearestPlayer = null;
|
||||||
|
for(Player p : location.getWorld().getPlayers()) {
|
||||||
|
if (p.getGameMode() == GameMode.SPECTATOR) {
|
||||||
|
continue;
|
||||||
|
} else if (nearestPlayer == null) {
|
||||||
|
nearestPlayer = p;
|
||||||
|
} else if (location.distance(p.getLocation()) < location.distance(nearestPlayer.getLocation())) {
|
||||||
|
nearestPlayer = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nearestPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
LivingEntity getNearestBombObjective(Location location) {
|
||||||
|
LivingEntity nearest = null;
|
||||||
|
if (m_droppedBombs.size() > 0) {
|
||||||
|
for(LivingEntity ent : m_droppedBombs) {
|
||||||
|
if (nearest == null) {
|
||||||
|
nearest = ent;
|
||||||
|
} else if (location.distance(ent.getLocation()) < location.distance(nearest.getLocation())) {
|
||||||
|
nearest = ent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_bombCarriers.size() > 0) {
|
||||||
|
for(LivingEntity ent : m_bombCarriers) {
|
||||||
|
if (nearest == null) {
|
||||||
|
nearest = ent;
|
||||||
|
} else if (location.distance(ent.getLocation()) < location.distance(nearest.getLocation())) {
|
||||||
|
nearest = ent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nearest == null) {
|
||||||
|
nearest = m_bombTarget;
|
||||||
|
}
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player getBiggestPlayerThreat(Location location) {
|
||||||
|
Entity mostImportantObjective = getNearestBombObjective(location);
|
||||||
|
return getNearestPlayer(mostImportantObjective.getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
LivingEntity getMobTarget(Mob mob) {
|
||||||
|
switch(m_mobGoals.get(mob)) {
|
||||||
|
case PlayerHarassment:
|
||||||
|
if (mob.getTarget() instanceof Player) {
|
||||||
|
return mob.getTarget();
|
||||||
|
} else {
|
||||||
|
Player nearestThreat = getBiggestPlayerThreat(mob.getLocation());
|
||||||
|
return nearestThreat;
|
||||||
|
}
|
||||||
|
case BombCarrier:
|
||||||
|
if (m_bombCarriers.contains(mob)) {
|
||||||
|
return m_bombTarget;
|
||||||
|
} else if (m_droppedBombs.size() > 0) {
|
||||||
|
return m_droppedBombs.iterator().next();
|
||||||
|
} else {
|
||||||
|
return getBiggestPlayerThreat(mob.getLocation());
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return m_bombTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntity(Mob entity, Goal goal) {
|
||||||
m_livingMobs.add(entity);
|
m_livingMobs.add(entity);
|
||||||
|
m_mobGoals.put(entity, goal);
|
||||||
|
entity.setTarget(getMobTarget(entity));
|
||||||
m_createdMobs += 1;
|
m_createdMobs += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int bombCount() {
|
||||||
|
return m_bombCarriers.size() + m_droppedBombs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mob addBombCarrier(Mob entity) {
|
||||||
|
m_bombCarriers.add(entity);
|
||||||
|
entity.setTarget(m_bombTarget);
|
||||||
|
return (Mob)(new BombCarrier(entity).inHand());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBombCarrier(Mob entity) {
|
||||||
|
m_bombCarriers.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean empty() {
|
public boolean empty() {
|
||||||
return m_livingMobs.size() == 0;
|
return m_livingMobs.size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LivingEntity dropBomb(Location location) {
|
||||||
|
ArmorStand droppedBomb = (ArmorStand)(new BombCarrier((LivingEntity)location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND)).onHead());
|
||||||
|
droppedBomb.setCustomName("The Bomb");
|
||||||
|
droppedBomb.setVisible(false);
|
||||||
|
droppedBomb.setCustomNameVisible(true);
|
||||||
|
droppedBomb.setGlowing(true);
|
||||||
|
m_droppedBombs.add(droppedBomb);
|
||||||
|
for(Mob ent : m_livingMobs) {
|
||||||
|
if (!m_bombCarriers.contains(ent)) {
|
||||||
|
ent.setTarget(getMobTarget(ent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return droppedBomb;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean m_bombWasHit = false;
|
||||||
|
|
||||||
|
public boolean bombWasHit() {
|
||||||
|
return m_bombWasHit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleEntityDamage(EntityDamageEvent evt) {
|
||||||
|
Entity entity = evt.getEntity();
|
||||||
|
m_bombWasHit = false;
|
||||||
|
|
||||||
|
if (entity == m_bombTarget && evt instanceof EntityDamageByEntityEvent) {
|
||||||
|
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
||||||
|
evt.setCancelled(true);
|
||||||
|
if (m_bombCarriers.contains(entityEvt.getDamager())) {
|
||||||
|
m_bombWasHit = true;
|
||||||
|
}
|
||||||
|
} if (m_droppedBombs.contains(entity)) {
|
||||||
|
evt.setCancelled(true);
|
||||||
|
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
||||||
|
if (m_livingMobs.contains(entityEvt.getDamager())) {
|
||||||
|
evt.getEntity().remove();
|
||||||
|
m_droppedBombs.remove(evt.getEntity());
|
||||||
|
addBombCarrier((Mob)entityEvt.getDamager());
|
||||||
|
for(Mob ent : m_livingMobs) {
|
||||||
|
ent.setTarget(getMobTarget(ent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_livingMobs.contains(evt.getEntity())) {
|
||||||
|
//m_game.onMobDamaged(evt.getEntity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LivingEntity spawnTarget(Location location) {
|
||||||
|
assert(m_bombTarget == null);
|
||||||
|
m_bombTarget = (LivingEntity)location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND);
|
||||||
|
ArmorStand bombStand = (ArmorStand)m_bombTarget;
|
||||||
|
bombStand.setCustomName("Bomb Target");
|
||||||
|
bombStand.setVisible(false);
|
||||||
|
bombStand.setCustomNameVisible(true);
|
||||||
|
bombStand.setGlowing(true);
|
||||||
|
EntityEquipment equipment = bombStand.getEquipment();
|
||||||
|
equipment.setHelmet(Items.makeBombTarget());
|
||||||
|
return m_bombTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleEntityRetarget(EntityTargetEvent evt) {
|
||||||
|
if (m_livingMobs.contains(evt.getEntity())) {
|
||||||
|
evt.setTarget(getMobTarget((Mob)evt.getEntity()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,10 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.GameMode;
|
import org.bukkit.GameMode;
|
||||||
import org.bukkit.attribute.Attribute;
|
import org.bukkit.attribute.Attribute;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
|
import org.bukkit.scoreboard.ScoreboardManager;
|
||||||
|
|
||||||
|
import gg.malloc.defense.ui.Sidebar;
|
||||||
|
import gg.malloc.defense.model.Progress;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -12,6 +16,8 @@ import java.util.Collection;
|
|||||||
public class PlayerManager {
|
public class PlayerManager {
|
||||||
HashMap<Player, State> m_playerStates = new HashMap<>();
|
HashMap<Player, State> m_playerStates = new HashMap<>();
|
||||||
HashMap<Player, Boolean> m_playerReadyStates = new HashMap<>();
|
HashMap<Player, Boolean> m_playerReadyStates = new HashMap<>();
|
||||||
|
HashMap<Player, Integer> m_playerExpenses = new HashMap<>();
|
||||||
|
HashMap<Player, Integer> m_respawnCounters = new HashMap<>();
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
Idle,
|
Idle,
|
||||||
@ -27,6 +33,10 @@ public class PlayerManager {
|
|||||||
return allSuccess;
|
return allSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean contains(Player player) {
|
||||||
|
return m_playerStates.containsKey(player);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean requestTransition(Player player, State toState) {
|
public boolean requestTransition(Player player, State toState) {
|
||||||
State currentState = m_playerStates.get(player);
|
State currentState = m_playerStates.get(player);
|
||||||
if (currentState == toState) {
|
if (currentState == toState) {
|
||||||
@ -53,25 +63,29 @@ public class PlayerManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void healPlayer(Player player) {
|
||||||
|
player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
|
||||||
|
player.setFoodLevel(40);
|
||||||
|
}
|
||||||
|
|
||||||
// Respawn player
|
// Respawn player
|
||||||
public boolean enterPlaying(Player player) {
|
public boolean enterPlaying(Player player) {
|
||||||
//m_log.fine("Respawning player " + player);
|
|
||||||
player.setGameMode(Bukkit.getDefaultGameMode());
|
player.setGameMode(Bukkit.getDefaultGameMode());
|
||||||
player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
|
healPlayer(player);
|
||||||
player.setFoodLevel(10);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean enterDead(Player player) {
|
public boolean enterDead(Player player) {
|
||||||
//m_log.info("Player has died in game" + player);
|
|
||||||
player.setGameMode(GameMode.SPECTATOR);
|
player.setGameMode(GameMode.SPECTATOR);
|
||||||
|
m_respawnCounters.put(player, 15);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPlayer(Player player) {
|
public void addPlayer(Player player) {
|
||||||
//m_log.info("Adding player " + player);
|
|
||||||
m_playerStates.put(player, State.Idle);
|
m_playerStates.put(player, State.Idle);
|
||||||
m_playerReadyStates.put(player, false);
|
m_playerReadyStates.put(player, false);
|
||||||
|
m_playerExpenses.put(player, 0);
|
||||||
|
m_respawnCounters.put(player, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReady(Player player) {
|
public boolean isReady(Player player) {
|
||||||
@ -82,14 +96,14 @@ public class PlayerManager {
|
|||||||
m_playerReadyStates.put(player, ready);
|
m_playerReadyStates.put(player, ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double readyProgress() {
|
public Progress readyProgress() {
|
||||||
int readyNum = 0;
|
int readyNum = 0;
|
||||||
for(boolean b : m_playerReadyStates.values()) {
|
for(boolean b : m_playerReadyStates.values()) {
|
||||||
if (b) {
|
if (b) {
|
||||||
readyNum += 1;
|
readyNum += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (double)readyNum / (double)m_playerReadyStates.size();
|
return new StaticProgress(readyNum, m_playerReadyStates.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEveryoneReady() {
|
public boolean isEveryoneReady() {
|
||||||
@ -99,12 +113,53 @@ public class PlayerManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Progress getRespawnProgress(Player player) {
|
||||||
|
return new StaticProgress(m_respawnCounters.get(player), 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRespawnCounter(Player player) {
|
||||||
|
if (m_respawnCounters.containsKey(player)) {
|
||||||
|
return m_respawnCounters.get(player);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickRespawnCounters() {
|
||||||
|
for(Player p : m_respawnCounters.keySet()) {
|
||||||
|
if (m_respawnCounters.get(p) > 0) {
|
||||||
|
m_respawnCounters.put(p, m_respawnCounters.get(p) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean readyToRespawn(Player player) {
|
||||||
|
return m_respawnCounters.get(player) == 0 && m_playerStates.get(player) == State.Dead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayerExpenses(Player player, int amount) {
|
||||||
|
if (m_playerExpenses.containsKey(player)) {
|
||||||
|
m_playerExpenses.put(player, m_playerExpenses.get(player) + amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPlayerExpenses(Player player) {
|
||||||
|
if (m_playerExpenses.containsKey(player)) {
|
||||||
|
return m_playerExpenses.get(player);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removePlayer(Player player) {
|
public boolean removePlayer(Player player) {
|
||||||
//m_log.info("Removing player " + player);
|
healPlayer(player);
|
||||||
requestTransition(player, State.Idle);
|
requestTransition(player, State.Idle);
|
||||||
m_playerStates.remove(player);
|
m_playerStates.remove(player);
|
||||||
|
m_playerReadyStates.remove(player);
|
||||||
|
m_playerExpenses.remove(player);
|
||||||
|
m_respawnCounters.remove(player);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
src/main/java/gg/malloc/defense/engine/StaticProgress.java
Normal file
21
src/main/java/gg/malloc/defense/engine/StaticProgress.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Progress;
|
||||||
|
|
||||||
|
public class StaticProgress implements Progress {
|
||||||
|
int m_value;
|
||||||
|
int m_maximum;
|
||||||
|
|
||||||
|
public StaticProgress(int value, int maximum) {
|
||||||
|
m_value = value;
|
||||||
|
m_maximum = maximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value() {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int maximum() {
|
||||||
|
return m_maximum;
|
||||||
|
}
|
||||||
|
}
|
40
src/main/java/gg/malloc/defense/engine/TickTask.java
Normal file
40
src/main/java/gg/malloc/defense/engine/TickTask.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import gg.malloc.defense.Plugin;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
public class TickTask {
|
||||||
|
Runnable m_runnable;
|
||||||
|
int m_interval;
|
||||||
|
BukkitTask m_task = null;
|
||||||
|
Plugin m_plugin;
|
||||||
|
boolean m_cancelled = false;
|
||||||
|
|
||||||
|
public TickTask(Plugin plugin, Runnable runnable, int interval) {
|
||||||
|
m_plugin = plugin;
|
||||||
|
m_runnable = runnable;
|
||||||
|
m_interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
m_cancelled = false;
|
||||||
|
if (m_task == null) {
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tick() {
|
||||||
|
m_runnable.run();
|
||||||
|
if (!m_cancelled) {
|
||||||
|
m_task = m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {tick();}, m_interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
m_cancelled = true;
|
||||||
|
if (m_task != null) {
|
||||||
|
m_task.cancel();
|
||||||
|
m_task = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/main/java/gg/malloc/defense/engine/WaveManager.java
Normal file
56
src/main/java/gg/malloc/defense/engine/WaveManager.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Wave;
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
|
import gg.malloc.defense.model.Progress;
|
||||||
|
|
||||||
|
public class WaveManager {
|
||||||
|
int m_currentWaveNum = 0;
|
||||||
|
int m_currentBatch = 0;
|
||||||
|
Wave m_currentWave = null;
|
||||||
|
Game m_game;
|
||||||
|
|
||||||
|
public Progress asProgress() {
|
||||||
|
return new StaticProgress(m_currentWaveNum, m_game.getWaveCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public WaveManager(Game game) {
|
||||||
|
m_game = game;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
m_currentWaveNum = 0;
|
||||||
|
m_currentBatch = 0;
|
||||||
|
m_currentWave = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Wave currentWave() {
|
||||||
|
return m_currentWave;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int currentWaveNum() {
|
||||||
|
return m_currentWaveNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int currentBatchNum() {
|
||||||
|
return m_currentBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastWave() {
|
||||||
|
return m_currentWaveNum >= m_game.getWaveCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastBatch() {
|
||||||
|
return m_currentBatch >= m_currentWave.batchCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void nextBatch() {
|
||||||
|
m_currentBatch += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void next() {
|
||||||
|
m_currentWaveNum += 1;
|
||||||
|
m_currentBatch = 0;
|
||||||
|
m_currentWave = m_game.getWave(m_currentWaveNum);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,9 @@ import gg.malloc.defense.model.Spawner;
|
|||||||
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Mob;
|
||||||
|
import org.bukkit.attribute.AttributeModifier;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ public class ScaledWaves implements Game {
|
|||||||
|
|
||||||
MobWave(HashMap<EntityType, Double> weights, int totalCount, int batches, boolean hasRavager) {
|
MobWave(HashMap<EntityType, Double> weights, int totalCount, int batches, boolean hasRavager) {
|
||||||
m_batches = batches;
|
m_batches = batches;
|
||||||
m_mobsPerBatch = Math.max(1, totalCount / batches);
|
m_mobsPerBatch = Math.max(1, totalCount / batches) * 3;
|
||||||
m_spawnWeights = weights;
|
m_spawnWeights = weights;
|
||||||
m_hasRavager = hasRavager;
|
m_hasRavager = hasRavager;
|
||||||
}
|
}
|
||||||
@ -55,6 +58,8 @@ public class ScaledWaves implements Game {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawnBatch(Spawner spawner, int batch) {
|
public void spawnBatch(Spawner spawner, int batch) {
|
||||||
|
double healthScale = -0.7;
|
||||||
|
AttributeModifier modifier = new AttributeModifier("Scaled Health", healthScale, AttributeModifier.Operation.MULTIPLY_SCALAR_1);
|
||||||
assert(m_mobsPerBatch > 0);
|
assert(m_mobsPerBatch > 0);
|
||||||
for(int i = 0; i < m_mobsPerBatch; i++) {
|
for(int i = 0; i < m_mobsPerBatch; i++) {
|
||||||
EntityType selectedType = null;
|
EntityType selectedType = null;
|
||||||
@ -67,7 +72,13 @@ public class ScaledWaves implements Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(selectedType != null);
|
assert(selectedType != null);
|
||||||
Entity newMob = spawner.spawnMob(selectedType);
|
Mob newMob;
|
||||||
|
if (selectedType == EntityType.ZOMBIE) {
|
||||||
|
newMob = (Mob)spawner.spawnBombCarrier(selectedType);
|
||||||
|
newMob.getAttribute(Attribute.GENERIC_MAX_HEALTH).addModifier(modifier);
|
||||||
|
} else {
|
||||||
|
newMob = (Mob)spawner.spawnMob(selectedType);
|
||||||
|
}
|
||||||
newMob.setCustomName("Mob " + i + "/" + m_mobsPerBatch);
|
newMob.setCustomName("Mob " + i + "/" + m_mobsPerBatch);
|
||||||
}
|
}
|
||||||
if (m_hasRavager) {
|
if (m_hasRavager) {
|
||||||
|
@ -3,8 +3,6 @@ package gg.malloc.defense.model;
|
|||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
||||||
public interface Arena {
|
public interface Arena {
|
||||||
World getWorld();
|
Waypoint[] spawnpoints();
|
||||||
String name();
|
Waypoint bombTarget();
|
||||||
Spawnpoint[] spawnpoints();
|
|
||||||
Spawnpoint bombTarget();
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package gg.malloc.defense.model;
|
package gg.malloc.defense.model;
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
|
|
||||||
public interface Game {
|
public interface Game {
|
||||||
int getWaveCount();
|
int getWaveCount();
|
||||||
Wave getWave(int waveNumber);
|
Wave getWave(int waveNumber);
|
||||||
default void onMobDamaged(Entity entity) {}
|
|
||||||
}
|
}
|
||||||
|
26
src/main/java/gg/malloc/defense/model/Instance.java
Normal file
26
src/main/java/gg/malloc/defense/model/Instance.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package gg.malloc.defense.model;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public interface Instance {
|
||||||
|
public enum Stage {
|
||||||
|
Idle,
|
||||||
|
Warmup,
|
||||||
|
Countdown,
|
||||||
|
Playing,
|
||||||
|
GameOver
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID getId();
|
||||||
|
Stage getStage();
|
||||||
|
boolean requestTransition(Stage stage);
|
||||||
|
State getState();
|
||||||
|
void addPlayer(Player p);
|
||||||
|
void removePlayer(Player p);
|
||||||
|
Collection<Player> getPlayers();
|
||||||
|
void togglePlayerReady(Player p);
|
||||||
|
void spendMoney(Player p, int amount);
|
||||||
|
}
|
13
src/main/java/gg/malloc/defense/model/Progress.java
Normal file
13
src/main/java/gg/malloc/defense/model/Progress.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package gg.malloc.defense.model;
|
||||||
|
|
||||||
|
public interface Progress {
|
||||||
|
|
||||||
|
int value();
|
||||||
|
int maximum();
|
||||||
|
default int remaining() {
|
||||||
|
return maximum() - value();
|
||||||
|
}
|
||||||
|
default double toDouble() {
|
||||||
|
return Math.min(maximum(), Math.max(0.0, value())) / maximum();
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package gg.malloc.defense.model;
|
package gg.malloc.defense.model;
|
||||||
|
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
|
||||||
public interface Spawner {
|
public interface Spawner {
|
||||||
Entity spawnMob(EntityType type);
|
LivingEntity spawnMob(EntityType type);
|
||||||
|
LivingEntity spawnBombCarrier(EntityType type);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package gg.malloc.defense.model;
|
|
||||||
|
|
||||||
import org.bukkit.Location;
|
|
||||||
|
|
||||||
public interface Spawnpoint {
|
|
||||||
Location getLocation();
|
|
||||||
String getName();
|
|
||||||
String getID();
|
|
||||||
}
|
|
16
src/main/java/gg/malloc/defense/model/State.java
Normal file
16
src/main/java/gg/malloc/defense/model/State.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package gg.malloc.defense.model;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public interface State {
|
||||||
|
Instance.Stage getStage();
|
||||||
|
|
||||||
|
Progress fuseProgress();
|
||||||
|
Progress waveProgress();
|
||||||
|
Progress mobProgress();
|
||||||
|
Progress playerReadyProgress();
|
||||||
|
Progress coinProgress();
|
||||||
|
|
||||||
|
Progress playerRespawnProgress(Player p);
|
||||||
|
int getPlayerBalance(Player p);
|
||||||
|
}
|
18
src/main/java/gg/malloc/defense/model/Waypoint.java
Normal file
18
src/main/java/gg/malloc/defense/model/Waypoint.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package gg.malloc.defense.model;
|
||||||
|
|
||||||
|
public class Waypoint {
|
||||||
|
double m_x;
|
||||||
|
double m_y;
|
||||||
|
double m_z;
|
||||||
|
String m_name;
|
||||||
|
|
||||||
|
public Waypoint(double x, double y, double z) {
|
||||||
|
m_x = x;
|
||||||
|
m_y = y;
|
||||||
|
m_z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getX() { return m_x; }
|
||||||
|
public double getY() { return m_y; }
|
||||||
|
public double getZ() { return m_z; }
|
||||||
|
}
|
27
src/main/java/gg/malloc/defense/ui/BombCarrier.java
Normal file
27
src/main/java/gg/malloc/defense/ui/BombCarrier.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package gg.malloc.defense.ui;
|
||||||
|
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
public class BombCarrier {
|
||||||
|
LivingEntity m_entity;
|
||||||
|
|
||||||
|
public BombCarrier(LivingEntity entity) {
|
||||||
|
m_entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LivingEntity inHand() {
|
||||||
|
EntityEquipment equipment = m_entity.getEquipment();
|
||||||
|
equipment.setItemInOffHand(Items.makeBombHelmet());
|
||||||
|
return m_entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LivingEntity onHead() {
|
||||||
|
EntityEquipment equipment = m_entity.getEquipment();
|
||||||
|
equipment.setHelmet(Items.makeBombHelmet());
|
||||||
|
return m_entity;
|
||||||
|
}
|
||||||
|
}
|
126
src/main/java/gg/malloc/defense/ui/BossBars.java
Normal file
126
src/main/java/gg/malloc/defense/ui/BossBars.java
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package gg.malloc.defense.ui;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
|
import gg.malloc.defense.model.State;
|
||||||
|
import gg.malloc.defense.model.Progress;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.bukkit.boss.BossBar;
|
||||||
|
import org.bukkit.boss.BarStyle;
|
||||||
|
import org.bukkit.boss.BarColor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class BossBars {
|
||||||
|
BossBar m_gameBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID);
|
||||||
|
BossBar m_waveBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID);
|
||||||
|
BossBar m_bombBar = Bukkit.createBossBar("Bomb Fuse", BarColor.RED, BarStyle.SOLID);
|
||||||
|
HashMap<Player, BossBar> m_respawnBars = new HashMap<>();
|
||||||
|
|
||||||
|
State m_gameState;
|
||||||
|
|
||||||
|
double m_countdownProgress;
|
||||||
|
|
||||||
|
public void setCountdownProgress(double v) {
|
||||||
|
m_countdownProgress = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossBars(State gameState) {
|
||||||
|
m_gameState = gameState;
|
||||||
|
m_gameBar.setVisible(true);
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayer(Player p) {
|
||||||
|
m_gameBar.addPlayer(p);
|
||||||
|
m_waveBar.addPlayer(p);
|
||||||
|
m_bombBar.addPlayer(p);
|
||||||
|
BossBar respawnBar = Bukkit.createBossBar("Respawn", BarColor.BLUE, BarStyle.SOLID);
|
||||||
|
respawnBar.addPlayer(p);
|
||||||
|
respawnBar.setVisible(false);
|
||||||
|
m_respawnBars.put(p, respawnBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePlayer(Player p) {
|
||||||
|
m_gameBar.removePlayer(p);
|
||||||
|
m_waveBar.removePlayer(p);
|
||||||
|
m_bombBar.removePlayer(p);
|
||||||
|
if (m_respawnBars.containsKey(p)) {
|
||||||
|
m_respawnBars.get(p).removePlayer(p);
|
||||||
|
m_respawnBars.remove(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
switch(m_gameState.getStage()) {
|
||||||
|
case Idle:
|
||||||
|
m_gameBar.setProgress(1.0);
|
||||||
|
m_gameBar.setTitle("Waiting for players...");
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
case Warmup:
|
||||||
|
m_gameBar.setProgress(m_gameState.waveProgress().toDouble());
|
||||||
|
m_gameBar.setTitle("Wave " + m_gameState.waveProgress().value() + " / " + m_gameState.waveProgress().maximum());
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.BLUE);
|
||||||
|
m_waveBar.setTitle("Warmup - Waiting for players to get ready...");
|
||||||
|
m_waveBar.setProgress(m_gameState.playerReadyProgress().toDouble());
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
case Countdown:
|
||||||
|
m_gameBar.setProgress(m_gameState.waveProgress().toDouble());
|
||||||
|
m_gameBar.setTitle("Wave " + m_gameState.waveProgress().value() + " / " + m_gameState.waveProgress().maximum());
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.YELLOW);
|
||||||
|
m_waveBar.setTitle("Wave starting!");
|
||||||
|
m_waveBar.setProgress(m_countdownProgress);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
case Playing:
|
||||||
|
m_gameBar.setProgress(m_gameState.waveProgress().toDouble());
|
||||||
|
m_gameBar.setTitle("Wave " + m_gameState.waveProgress().value() + " / " + m_gameState.waveProgress().maximum());
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
if (m_gameState.mobProgress().maximum() > 0) {
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.GREEN);
|
||||||
|
m_waveBar.setTitle("Mobs remaining: " + (m_gameState.mobProgress().maximum() - m_gameState.mobProgress().value()));
|
||||||
|
m_waveBar.setProgress(m_gameState.mobProgress().toDouble());
|
||||||
|
} else {
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
}
|
||||||
|
if (m_gameState.fuseProgress().value() > 0) {
|
||||||
|
m_bombBar.setVisible(true);
|
||||||
|
m_bombBar.setProgress(1.0 - m_gameState.fuseProgress().toDouble());
|
||||||
|
} else {
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GameOver:
|
||||||
|
m_gameBar.setColor(BarColor.RED);
|
||||||
|
m_gameBar.setProgress(1.0);
|
||||||
|
m_gameBar.setTitle("Game Over!");
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.BLUE);
|
||||||
|
m_waveBar.setTitle("Returning to lobby...");
|
||||||
|
m_waveBar.setProgress(m_countdownProgress);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (Player p : m_respawnBars.keySet()) {
|
||||||
|
Progress respawnProgress = m_gameState.playerRespawnProgress(p);
|
||||||
|
if (respawnProgress.value() > 0) {
|
||||||
|
m_respawnBars.get(p).setVisible(true);
|
||||||
|
m_respawnBars.get(p).setProgress(respawnProgress.toDouble());
|
||||||
|
m_respawnBars.get(p).setTitle("Respawning in " + respawnProgress.value());
|
||||||
|
} else {
|
||||||
|
m_respawnBars.get(p).setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/main/java/gg/malloc/defense/ui/Items.java
Normal file
38
src/main/java/gg/malloc/defense/ui/Items.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package gg.malloc.defense.ui;
|
||||||
|
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import de.tr7zw.nbtapi.NBTItem;
|
||||||
|
import de.tr7zw.nbtapi.NBTCompound;
|
||||||
|
|
||||||
|
public class Items {
|
||||||
|
public static ItemStack makeCoins() {
|
||||||
|
ItemStack coinItem = new ItemStack(Material.IRON_NUGGET);
|
||||||
|
ItemMeta meta = coinItem.getItemMeta();
|
||||||
|
meta.setCustomModelData(93197);
|
||||||
|
coinItem.setItemMeta(meta);
|
||||||
|
|
||||||
|
NBTItem nbt = new NBTItem(coinItem);
|
||||||
|
nbt.addCompound("malloc").setInteger("coinValue", 1);
|
||||||
|
|
||||||
|
return nbt.getItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack makeBombHelmet() {
|
||||||
|
ItemStack bombItem = new ItemStack(Material.CARVED_PUMPKIN);
|
||||||
|
ItemMeta meta = bombItem.getItemMeta();
|
||||||
|
meta.setCustomModelData(33197);
|
||||||
|
bombItem.setItemMeta(meta);
|
||||||
|
return bombItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack makeBombTarget() {
|
||||||
|
ItemStack bombItem = new ItemStack(Material.CARVED_PUMPKIN);
|
||||||
|
ItemMeta meta = bombItem.getItemMeta();
|
||||||
|
meta.setCustomModelData(35197);
|
||||||
|
bombItem.setItemMeta(meta);
|
||||||
|
return bombItem;
|
||||||
|
}
|
||||||
|
}
|
94
src/main/java/gg/malloc/defense/ui/Sidebar.java
Normal file
94
src/main/java/gg/malloc/defense/ui/Sidebar.java
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package gg.malloc.defense.ui;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.State;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.scoreboard.Scoreboard;
|
||||||
|
import org.bukkit.scoreboard.DisplaySlot;
|
||||||
|
import org.bukkit.scoreboard.Score;
|
||||||
|
import org.bukkit.scoreboard.Team;
|
||||||
|
import org.bukkit.scoreboard.Objective;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class Sidebar {
|
||||||
|
Scoreboard m_scoreboard;
|
||||||
|
Objective m_objective;
|
||||||
|
ArrayList<String> m_rowKeys;
|
||||||
|
HashMap<Integer, String> m_rows;
|
||||||
|
ArrayList<Team> m_teams;
|
||||||
|
Player m_player;
|
||||||
|
int m_size;
|
||||||
|
|
||||||
|
State m_state;
|
||||||
|
|
||||||
|
public Sidebar(State state, Scoreboard scoreboard, Player player) {
|
||||||
|
m_state = state;
|
||||||
|
m_player = player;
|
||||||
|
m_scoreboard = scoreboard;
|
||||||
|
m_objective = m_scoreboard.registerNewObjective("text", "dummy", ChatColor.LIGHT_PURPLE + "" + ChatColor.BOLD + "Malloc Defense");
|
||||||
|
m_objective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
||||||
|
m_rowKeys = new ArrayList<>();
|
||||||
|
m_rows = new HashMap<>();
|
||||||
|
m_teams = new ArrayList<>();
|
||||||
|
m_rowKeys.add(ChatColor.BLACK + "" + ChatColor.WHITE);
|
||||||
|
m_rowKeys.add(ChatColor.GOLD+ "" + ChatColor.WHITE);
|
||||||
|
m_rowKeys.add(ChatColor.WHITE+ "" + ChatColor.WHITE);
|
||||||
|
m_rowKeys.add(ChatColor.BLACK+ "" + ChatColor.BLACK);
|
||||||
|
for(String key : m_rowKeys) {
|
||||||
|
Team team = m_scoreboard.registerNewTeam(key);
|
||||||
|
team.addEntry(key);
|
||||||
|
m_teams.add(team);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
switch(m_state.getStage()) {
|
||||||
|
case Idle:
|
||||||
|
m_rows.put(0, ChatColor.LIGHT_PURPLE + "Waiting for players...");
|
||||||
|
m_size = 1;
|
||||||
|
break;
|
||||||
|
case Warmup:
|
||||||
|
m_rows.put(0, ChatColor.LIGHT_PURPLE + "Wave " + m_state.waveProgress().value() + " / " + m_state.waveProgress().maximum());
|
||||||
|
m_rows.put(1, ChatColor.AQUA + "Get ready!");
|
||||||
|
m_rows.put(2, ChatColor.AQUA + "Balance: " + m_state.getPlayerBalance(m_player));
|
||||||
|
m_rows.put(3, ChatColor.AQUA + "Coins remaining: " + m_state.coinProgress().remaining());
|
||||||
|
m_size = 4;
|
||||||
|
break;
|
||||||
|
case Countdown:
|
||||||
|
m_rows.put(0, ChatColor.LIGHT_PURPLE + "Wave " + m_state.waveProgress().value() + " / " + m_state.waveProgress().maximum());
|
||||||
|
m_rows.put(1, ChatColor.GOLD + "Wave incoming!");
|
||||||
|
m_rows.put(2, ChatColor.AQUA + "Balance: " + m_state.getPlayerBalance(m_player));
|
||||||
|
m_rows.put(3, ChatColor.AQUA + "Coins remaining: " + m_state.coinProgress().remaining());
|
||||||
|
m_size = 4;
|
||||||
|
break;
|
||||||
|
case Playing:
|
||||||
|
m_rows.put(0, ChatColor.LIGHT_PURPLE + "Wave " + m_state.waveProgress().value() + " / " + m_state.waveProgress().maximum());
|
||||||
|
m_rows.put(1, ChatColor.GREEN + "Mobs remaining: " + m_state.mobProgress().remaining());
|
||||||
|
m_rows.put(2, ChatColor.AQUA + "Balance: " + m_state.getPlayerBalance(m_player));
|
||||||
|
m_rows.put(3, ChatColor.AQUA + "Coins remaining: " + m_state.coinProgress().remaining());
|
||||||
|
m_size = 4;
|
||||||
|
break;
|
||||||
|
case GameOver:
|
||||||
|
m_rows.put(0, ChatColor.RED + "Game over!");
|
||||||
|
m_size = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < Math.min(m_size, m_rowKeys.size()); i++) {
|
||||||
|
Score rowScore = m_objective.getScore(m_rowKeys.get(i));
|
||||||
|
rowScore.setScore(m_rowKeys.size() - i);
|
||||||
|
if (m_rows.containsKey(i)) {
|
||||||
|
m_teams.get(i).setPrefix(m_rows.get(i));
|
||||||
|
} else {
|
||||||
|
m_teams.get(i).setPrefix("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scoreboard getScoreboard() {
|
||||||
|
return m_scoreboard;
|
||||||
|
}
|
||||||
|
}
|
35
src/main/java/gg/malloc/defense/ui/Sidebars.java
Normal file
35
src/main/java/gg/malloc/defense/ui/Sidebars.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package gg.malloc.defense.ui;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.scoreboard.ScoreboardManager;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.State;
|
||||||
|
|
||||||
|
public class Sidebars {
|
||||||
|
State m_state;
|
||||||
|
ScoreboardManager m_scoreboards;
|
||||||
|
HashMap<Player, Sidebar> m_sidebars = new HashMap<>();
|
||||||
|
|
||||||
|
public Sidebars(State state, ScoreboardManager scoreboards) {
|
||||||
|
m_state = state;
|
||||||
|
m_scoreboards = scoreboards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayer(Player player) {
|
||||||
|
m_sidebars.put(player, new Sidebar(m_state, m_scoreboards.getNewScoreboard(), player));
|
||||||
|
player.setScoreboard(m_sidebars.get(player).getScoreboard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
m_sidebars.remove(player);
|
||||||
|
player.setScoreboard(m_scoreboards.getMainScoreboard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
for(Player p : m_sidebars.keySet()) {
|
||||||
|
m_sidebars.get(p).update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/main/resources/config.yml
Normal file
18
src/main/resources/config.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
lobby:
|
||||||
|
world: world
|
||||||
|
maps:
|
||||||
|
quarry:
|
||||||
|
target:
|
||||||
|
x: -20.0
|
||||||
|
y: 80.0
|
||||||
|
z: 31.0
|
||||||
|
spawnpoints:
|
||||||
|
- x: -15.0
|
||||||
|
y: 80.0
|
||||||
|
z: -46.0
|
||||||
|
- x: -1.0
|
||||||
|
y: 80.0
|
||||||
|
z: -45.0
|
||||||
|
- x: 12.0
|
||||||
|
y: 81.0
|
||||||
|
z: -42.0
|
@ -2,13 +2,21 @@ name: Malloc-Defense
|
|||||||
version: 1.0
|
version: 1.0
|
||||||
api-version: 1.18
|
api-version: 1.18
|
||||||
main: gg.malloc.defense.Plugin
|
main: gg.malloc.defense.Plugin
|
||||||
depend: []
|
depend: [Vault]
|
||||||
commands:
|
commands:
|
||||||
|
ready:
|
||||||
|
description: Mark yourself as ready for the next wave
|
||||||
|
list:
|
||||||
|
description: List games
|
||||||
|
join:
|
||||||
|
description: Join a game
|
||||||
|
leave:
|
||||||
|
description: Leave a game
|
||||||
|
reload:
|
||||||
|
description: Reload the configuration
|
||||||
setstage:
|
setstage:
|
||||||
description: Sets a game stage
|
description: Sets a game stage
|
||||||
addplayer:
|
addplayer:
|
||||||
description: Adds a player to a game
|
description: Adds a player to a game
|
||||||
debuginfo:
|
debuginfo:
|
||||||
description: Unknowable powers
|
description: Unknowable powers
|
||||||
ready:
|
|
||||||
description: Mark yourself as ready for the next wave
|
|
||||||
|
Loading…
Reference in New Issue
Block a user