/**
* Copyright (c) Lambda Innovation, 2013-2016
* This file is part of the AcademyCraft mod.
* https://github.com/LambdaInnovation/AcademyCraft
* Licensed under GPLv3, see project root for more information.
*/
package cn.academy.vanilla.teleporter.skill;
import cn.academy.ability.api.Skill;
import cn.academy.ability.api.context.ClientRuntime;
import cn.academy.ability.api.context.ClientRuntime.ActivateHandlers;
import cn.academy.ability.api.context.ClientRuntime.IActivateHandler;
import cn.academy.ability.api.context.Context;
import cn.academy.ability.api.context.ContextManager;
import cn.academy.ability.api.context.KeyDelegate;
import cn.academy.ability.api.event.FlushControlEvent;
import cn.academy.core.Resources;
import cn.academy.core.client.sound.ACSounds;
import cn.academy.vanilla.teleporter.entity.EntityTPMarking;
import cn.academy.vanilla.teleporter.util.GravityCancellor;
import cn.academy.vanilla.teleporter.util.TPSkillHelper;
import cn.lambdalib.s11n.network.NetworkMessage.Listener;
import cn.lambdalib.util.deprecated.LIFMLGameEventDispatcher;
import cn.lambdalib.util.generic.MathUtils;
import cn.lambdalib.util.generic.VecUtils;
import cn.lambdalib.util.helper.Motion3D;
import cn.lambdalib.util.mc.EntitySelectors;
import cn.lambdalib.util.mc.Raytrace;
import com.google.common.base.Preconditions;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.MovingObjectPosition.MovingObjectType;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Vec3;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.input.Keyboard;
import java.util.Optional;
import static cn.lambdalib.util.generic.MathUtils.lerpf;
/**
* @author WeAthFolD
*/
public class Flashing extends Skill {
public static final Flashing instance = new Flashing();
private static final String
MSG_PERFORM = "perform",
KEY_GROUP = "TP_Flashing";
private Flashing() {
super("flashing", 5);
}
@SideOnly(Side.CLIENT)
@Override
public void activate(ClientRuntime rt, int keyID) {
rt.addKey(keyID, new KeyDelegate() {
@Override
public void onKeyDown() {
Optional<MainContext> opt = ContextManager.instance.find(MainContext.class);
if (!opt.isPresent()) {
ContextManager.instance.activate(new MainContext(getPlayer()));
} else {
opt.get().terminate();
}
MinecraftForge.EVENT_BUS.post(new FlushControlEvent());
}
@Override
public ResourceLocation getIcon() {
return instance.getHintIcon();
}
@Override
public int createID() {
return 0;
}
public Skill getSkill() {
return instance;
}
});
}
private static final Vec3[] dirs = new Vec3[] {
null,
VecUtils.vec(0, 0, -1),
VecUtils.vec(0, 0, 1),
VecUtils.vec(1, 0, 0),
VecUtils.vec(-1, 0, 0)
};
public static class MainContext extends Context {
int performingKey = -1;
@SideOnly(Side.CLIENT)
EntityTPMarking marking;
@SideOnly(Side.CLIENT)
GravityCancellor cancellor;
@SideOnly(Side.CLIENT)
IActivateHandler activateHandler;
final float exp, consumption;
final float overload_start, consumption_start;
final int cooldown_time;
float overloadKeep;
final int max_time;
int ticks = 0;
public MainContext(EntityPlayer player) {
super(player, instance);
exp = ctx.getSkillExp();
consumption = lerpf(13, 6, exp);
overload_start = lerpf(250, 180, exp);
consumption_start = lerpf(80, 60, exp);
max_time = (int) lerpf(60, 150, exp);
cooldown_time = (int) lerpf(900, 400, exp);
}
@Listener(channel=Context.MSG_MADEALIVE, side=Side.SERVER)
void serverMadeAlive() {
if(!ctx.consume(overload_start, consumption_start)) terminate(); else {
overloadKeep = ctx.cpData.getOverload();
}
}
@SideOnly(Side.CLIENT)
@Listener(channel=Context.MSG_MADEALIVE, side=Side.CLIENT)
void localMakeAlive() {
if (isLocal()) {
activateHandler = ActivateHandlers.terminatesContext(this);
clientRuntime().addActivateHandler(activateHandler);
final String[] strs = new String[] { null, "a", "d", "w", "s"};
final int[] keys = new int[] { -1, Keyboard.KEY_A, Keyboard.KEY_D, Keyboard.KEY_W, Keyboard.KEY_S };
for (int i = 0; i < 4; ++i) {
final int localid = i + 1;
clientRuntime().addKey(KEY_GROUP, keys[localid], new KeyDelegate() {
@Override
public void onKeyDown() {
localStart(localid);
}
@Override
public void onKeyUp() {
localEnd(localid);
}
@Override
public void onKeyAbort() {
localAbort(localid);
}
@Override
public ResourceLocation getIcon() {
return Resources.getTexture("abilities/teleporter/flashing/" + strs[localid]);
}
@Override
public int createID() {
return localid;
}
public Skill getSkill() {
return instance;
}
});
}
}
}
@SideOnly(Side.CLIENT)
void localStart(int keyid) {
performingKey = keyid;
startEffects();
}
@SideOnly(Side.CLIENT)
void localEnd(int keyid) {
if (keyid != performingKey) {
return;
}
endEffects();
sendToServer(MSG_PERFORM, performingKey);
performingKey = -1;
}
@SideOnly(Side.CLIENT)
void localAbort(int localid) {
if (performingKey == localid) {
performingKey = -1;
endEffects();
}
}
@SideOnly(Side.CLIENT)
@Listener(channel=MSG_TICK, side=Side.CLIENT)
void localTick() {
if (isLocal()) {
if (performingKey != -1 && !consume(true)) {
performingKey = -1;
endEffects();
} else {
if (marking != null) {
Vec3 dest = getDest(performingKey);
marking.setPosition(dest.xCoord, dest.yCoord, dest.zCoord);
}
}
if (cancellor != null && cancellor.isDead())
cancellor = null;
}
}
@Listener(channel=MSG_TICK, side=Side.SERVER)
void serverTick() {
if(ctx.cpData.getOverload() < overloadKeep) ctx.cpData.setOverload(overloadKeep);
if(ticks > max_time) terminate();
ticks++;
}
@Listener(channel=MSG_PERFORM, side=Side.SERVER)
void serverPerform(int keyid) {
if (ctx.consume(0, consumption)) {
Vec3 dest = getDest(keyid);
if(player.isRiding())
player.ridingEntity=null;
player.setPositionAndUpdate(dest.xCoord, dest.yCoord, dest.zCoord);
player.fallDistance = 0.0f;
ctx.addSkillExp(.002f);
instance.triggerAchievement(player);
TPSkillHelper.incrTPCount(player);
sendToClient(MSG_PERFORM);
}
}
@Listener(channel=MSG_PERFORM, side=Side.CLIENT)
void clientPerform() {
ACSounds.playClient(player, "tp.tp_flashing", 1.0f);
if (isLocal()) {
if (cancellor != null) {
cancellor.setDead();
cancellor = null;
}
cancellor = new GravityCancellor(player, 40);
LIFMLGameEventDispatcher.INSTANCE.registerClientTick(cancellor);
}
}
@Listener(channel=MSG_TERMINATED, side=Side.CLIENT)
void localTerminate() {
if (isLocal()) {
clientRuntime().removeActiveHandler(activateHandler);
clientRuntime().clearKeys(KEY_GROUP);
endEffects();
}
}
@Listener(channel=MSG_TERMINATED, side=Side.SERVER)
void serverTerminated() {
ctx.setCooldown(cooldown_time);
}
@SideOnly(Side.CLIENT)
private void startEffects() {
endEffects();
marking = new EntityTPMarking(player);
Vec3 dest = getDest(performingKey);
marking.setPosition(dest.xCoord, dest.yCoord, dest.zCoord);
world().spawnEntityInWorld(marking);
}
@SideOnly(Side.CLIENT)
private void endEffects() {
if (marking != null) {
marking.setDead();
marking = null;
}
}
private boolean consume(boolean simulate) {
return simulate ? ctx.canConsumeCP(consumption) : ctx.consume(0, consumption);
}
private Vec3 getDest(int keyid) {
Preconditions.checkState(keyid != -1);
double dist = lerpf(12, 18, exp);
Vec3 dir = VecUtils.copy(dirs[keyid]);
dir.rotateAroundZ(player.rotationPitch * MathUtils.PI_F / 180);
dir.rotateAroundY((-90 - player.rotationYaw) * MathUtils.PI_F / 180);
Motion3D mo = new Motion3D(player, true);
mo.setMotion(dir.xCoord, dir.yCoord, dir.zCoord);
MovingObjectPosition mop = Raytrace.perform(player.worldObj, mo.getPosVec(), mo.move(dist).getPosVec(),
EntitySelectors.living().and(EntitySelectors.exclude(player)));
double x, y, z;
if (mop != null) {
x = mop.hitVec.xCoord;
y = mop.hitVec.yCoord;
z = mop.hitVec.zCoord;
if (mop.typeOfHit == MovingObjectType.BLOCK) {
switch (mop.sideHit) {
case 0:
y -= 1.0;
break;
case 1:
y += 1.8;
break;
case 2:
z -= .6;
y = mop.blockY + 1.7;
break;
case 3:
z += .6;
y = mop.blockY + 1.7;
break;
case 4:
x -= .6;
y = mop.blockY + 1.7;
break;
case 5:
x += .6;
y = mop.blockY + 1.7;
break;
}
// check head
if (mop.sideHit > 1) {
int hx = (int) x, hy = (int) (y + 1), hz = (int) z;
if (!player.worldObj.isAirBlock(hx, hy, hz)) {
y -= 1.25;
}
}
} else {
y += mop.entityHit.getEyeHeight();
}
} else {
x = mo.px;
y = mo.py;
z = mo.pz;
}
return VecUtils.vec(x, y, z);
}
}
}