# (Solved) Loot-Chest

Discussion in 'Programming' started by sunyx, May 16, 2018 at 6:59 PM.

1. ### sunyx

Hello! Can anyone tell me how to create the animation (From the Crate at 00:55) from the video?
Thank You

2. ### sunyx

I coded the Holos already. I only need the circular animation

3. ### Mareckoo01

The math that you are probably looking for is explained here

4. ### sunyx

I alreadly tried it for over 8 hours so it would be nice if anyone can write the code for me because 8 hours are enough xd.

5. ### Mareckoo01

What did you try/post your current code...

6. ### sunyx

Code (Text):
double y = center.getY();

Location loc = new Location(center.getWorld(), x, y, z);
Vector difference = center.toVector().clone().subtract(loc.toVector()); // this sets the returned location's
// direction toward the center of the
// circle
loc.setDirection(difference);

return loc;
}

public void open(Player p, Treasure treasure) {

final Location lo = p.getLocation().clone();

List<TreasureHologram> holos = new ArrayList<>();

int points = 9; // amount of points to be generated
for (int i = 0; i < 370; i += 370 / points) {

double angle = (i * Math.PI / 180);
double x = 4 * Math.cos(angle);
double z = 4 * Math.sin(angle);
Location loc = lo.clone().add(x, 0, z);
TreasureHologram th = new TreasureHologram(
this.treasureItems.get(new Random().nextInt(treasureItems.size())));

th.spawn(loc);

}

TreasureHologram winHologram = new TreasureHologram();
winHologram.spawnText(lo.clone().add(4 * Math.cos(82 * Math.PI / 180), 0.3, 4 * Math.sin(82 * Math.PI / 180)));

holos.get(0).getItem().remove();
holos.get(0).getRarityText().setCustomNameVisible(false);
holos.get(0).getArmorstand().setCustomNameVisible(false);

new BukkitRunnable() {

int angle = 10;

@Override
public void run() {

for (int i = 0; i < holos.size(); i++) holos.get(i).teleport(holos.get((i + 1 != holos.size() ? i + 1 : 0)).getLocation(), angle);
p.playSound(p.getLocation(), Sound.CLICK, 1F, 1F);
angle--;

if (angle == 0) {

cancel();
TreasureItem ti = holos.get(2).getTreasureItem();
holos.forEach(a -> {
a.remove();
});
winHologram.remove();

winItem(p, ti, treasure);

}

}

}

TreasureHologram is just a holo

#6
Last edited: May 17, 2018 at 5:53 PM
7. ### sunyx

It is not smooth and i want to add the items one by one not all Armorstands instant

8. ### Mareckoo01

You shouldn't make new Random instance in that loop(creating it is heavy), make it outside or use ThreadLocalRandom
And for the "smooth animation" you need to teleport armor stands every tick, if you don't want them to appear instantly then don't spawn them instantly
Also you should probably change the way you are doing things, for example make a loop(bukkit timer) like this:
Code (Text):
-check if the loop should stop instantly(for example if player disconnected/died, etc..)

-check if you should spawn new armorstand

-teleport spawned armorstands to their location on circle around player/location

-check if the loop should/is slowing down and decrease "-increase circle offset"(and pick a winner when it slows down and stop the loop)

-increase circle offset

-check if the loop is running longer than X seconds and set slowing down to true if it is
and you would have player interact event handler somewhere to tell the loop to start slowing down

I don't want to spoon feed you code but if you want i can post the code i wrote so you can use it as a reference(copy-pasting won't help you if you want to write more code in future)

#8
Last edited: May 17, 2018 at 8:04 PM
9. ### sunyx

Well i tried it again today but i messed up Can you please give your code so i can understand the circular rotations better? I know Copy paste is bad. But im not copying anything. Ill look through your code and try to understand. Thank you

10. ### Mareckoo01

It works roughly the same way as i wrote in my post.
To understand the math take a look at the link i posted before(check his other tutorials too).
The only thing that this is missing is what i would call "pernament circle offset", you will need to calculate it from chest location (so that it looks like items appear from chest)
And i would recommed you to use your player wrapper instead of List and Map to store information

Code (Text):
private List<UUID> playersDoingTheThing = new ArrayList<>();
private Map<UUID, Boolean> cancelTheThing = new HashMap<>(); // true = start slowing down and give reward, false = stop instantly and don't give reward
public BukkitTask doTheThing(Player pl, int numberOfArmorstands, double speed, double radius, Location winLocation){
if(playersDoingTheThing.contains(pl.getUniqueId())) return null; // already running for this player
return new BukkitRunnable() {
List<ArmorStand> armorstands = new ArrayList<>();
Player p = pl;
int numOfAS = numberOfArmorstands,
ran = 0,
endAtRun = -1;
double PI_2 = Math.PI*2,
spd = speed,
currentOffset = 0,
asOffset = PI_2/numOfAS;
Location winLoc = winLocation;

@Override
public void run() {
if(!p.isOnline() || p.isDead() || (cancelTheThing.containsKey(p.getUniqueId()) && !cancelTheThing.get(p.getUniqueId()))){ // check if task should stop (instantly)
cancel();
armorstands.forEach(a -> a.remove());
armorstands.clear();
playersDoingTheThing.remove(p.getUniqueId());
cancelTheThing.remove(p.getUniqueId());
return;
}
Location pLoc = p.getLocation().clone();
if(armorstands.size() < numOfAS){ // check if we should spawn new armor stands
ArmorStand as = (ArmorStand) pLoc.getWorld().spawnEntity(pLoc.clone().add(0, -10, 0), EntityType.ARMOR_STAND);
as.setBasePlate(false);
as.setArms(true);
as.setGravity(false);
as.setInvulnerable(true);
as.setCustomName("ArmorStand #" + (armorstands.size()+1));
as.setCustomNameVisible(true);
as.setVisible(false);
return; // need to wait 1 tick after spawning new armor stand otherwise it will desync client side
}
}

int asNum = 0;
for(double d = 0; d<PI_2; d+=PI_2/numOfAS){ // rotate armorstands
if(asNum >= armorstands.size()) break;
double a = d+cO,
cos = Math.cos(a),
sin = Math.sin(a);
ArmorStand as = armorstands.get(asNum++);
l.setDirection(pLoc.toVector().subtract(l.toVector()).normalize());
as.teleport(l);
}
double m = 1d;
if(armorstands.size()==numOfAS && cancelTheThing.getOrDefault(p.getUniqueId(), false)){
if(endAtRun == -1) endAtRun = ran + 20*3 + ThreadLocalRandom.current().nextInt(20*5); // end time = 3 to 8 seconds from now
if(ran >= endAtRun){ // if we reached end time
ArmorStand closest = null;
double closestDistance = Double.MAX_VALUE;
for(ArmorStand as : armorstands){
double distance = as.getLocation().distance(winLoc); // this is a bit heavy, consider making different method of picking winning armorstand
if(distance < closestDistance){
closestDistance = distance;
closest = as;
}
}
if(closest != null){
ItemStack is = closest.getItemInHand();
if(is.hasItemMeta() && is.getItemMeta().hasDisplayName()){
p.sendMessage("You got " + is.getItemMeta().getDisplayName());
} else {
p.sendMessage("You got " + is.getType().name());
}

}
cancel();
armorstands.forEach(as -> as.remove());
armorstands.clear();
playersDoingTheThing.remove(p.getUniqueId());
cancelTheThing.remove(p.getUniqueId());
return; // finished
}
int remaining = endAtRun-ran;
m = .1+remaining/20d/10d; // 0.1 to 0.9 modifier
}

currentOffset+=spd*m;
ran++;
if(ran > 20*20 && !cancelTheThing.containsKey(p.getUniqueId())){ // limit to 20 seconds
cancelTheThing.put(p.getUniqueId(), true);
}
}
}

public Map<UUID, Boolean> getCancelTheThing(){
return cancelTheThing;
}

public List<UUID> getPlayersDoingTheThing(){
return playersDoingTheThing;
}

@EventHandler
public void onClick(PlayerInteractEvent e){
if(e.getAction() == Action.PHYSICAL) return;
Player p = e.getPlayer();
if(getPlayersDoingTheThing().contains(p.getUniqueId()) && !getCancelTheThing().containsKey(p.getUniqueId())){
getCancelTheThing().put(p.getUniqueId(), true);
}
}
Code (Text):
public class TestCommand implements CommandExecutor {
private SCT instance;

public Test(SCT instance){
this.instance = instance;
}

@Override
public boolean onCommand(CommandSender arg0, Command arg1, String arg2, String[] arg3) {
if(!(arg0 instanceof Player)) return false;
Player p = (Player) arg0;
p.sendMessage("Started!");
return true;
}

}