
Need to work on how the EntitySpellProjectile saves its data. Does not work properly. Perhaps it was not registered?
576 lines
17 KiB
Java
576 lines
17 KiB
Java
package WayofTime.alchemicalWizardry.common.spell.complex;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.entity.IProjectile;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.nbt.NBTTagList;
|
|
import net.minecraft.util.AxisAlignedBB;
|
|
import net.minecraft.util.DamageSource;
|
|
import net.minecraft.util.EnumMovingObjectType;
|
|
import net.minecraft.util.MathHelper;
|
|
import net.minecraft.util.MovingObjectPosition;
|
|
import net.minecraft.util.Vec3;
|
|
import net.minecraft.world.World;
|
|
import WayofTime.alchemicalWizardry.common.spell.complex.effect.SpellEffect;
|
|
import WayofTime.alchemicalWizardry.common.spell.complex.effect.impactEffects.IProjectileImpactEffect;
|
|
import WayofTime.alchemicalWizardry.common.spell.complex.effect.impactEffects.IProjectileUpdateEffect;
|
|
import cpw.mods.fml.relauncher.Side;
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
|
|
public class EntitySpellProjectile extends Entity implements IProjectile
|
|
{
|
|
private int xTile = -1;
|
|
private int yTile = -1;
|
|
private int zTile = -1;
|
|
private int inTile = 0;
|
|
private int inData = 0;
|
|
private boolean inGround = false;
|
|
/** The owner of this arrow. */
|
|
public EntityPlayer shootingEntity;
|
|
private int ticksInAir = 0;
|
|
private int ricochetCounter = 0;
|
|
private boolean scheduledForDeath = false;
|
|
|
|
//Custom variables
|
|
private int maxRicochet = 0;
|
|
private float damage = 1;
|
|
public List<IProjectileImpactEffect> impactList = new ArrayList();
|
|
private boolean penetration = false;
|
|
public List<IProjectileUpdateEffect> updateEffectList = new ArrayList();
|
|
public List<String> effectList = new LinkedList();
|
|
public List<SpellEffect> spellEffectList = new LinkedList();
|
|
|
|
public EntitySpellProjectile(World par1World)
|
|
{
|
|
super(par1World);
|
|
this.setSize(0.5F, 0.5F);
|
|
}
|
|
|
|
public EntitySpellProjectile(World par1World, double par2, double par4, double par6)
|
|
{
|
|
super(par1World);
|
|
this.setSize(0.5F, 0.5F);
|
|
this.setPosition(par2, par4, par6);
|
|
yOffset = 0.0F;
|
|
}
|
|
|
|
public EntitySpellProjectile(World par1World, EntityPlayer par2EntityPlayer)
|
|
{
|
|
super(par1World);
|
|
shootingEntity = par2EntityPlayer;
|
|
float par3 = 0.8F;
|
|
this.setSize(0.1F, 0.1F);
|
|
this.setLocationAndAngles(par2EntityPlayer.posX, par2EntityPlayer.posY + par2EntityPlayer.getEyeHeight(), par2EntityPlayer.posZ, par2EntityPlayer.rotationYaw, par2EntityPlayer.rotationPitch);
|
|
posX -= MathHelper.cos(rotationYaw / 180.0F * (float)Math.PI) * 0.16F;
|
|
posY -= 0.2D;
|
|
posZ -= MathHelper.sin(rotationYaw / 180.0F * (float)Math.PI) * 0.16F;
|
|
this.setPosition(posX, posY, posZ);
|
|
yOffset = 0.0F;
|
|
motionX = -MathHelper.sin(rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(rotationPitch / 180.0F * (float)Math.PI);
|
|
motionZ = MathHelper.cos(rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(rotationPitch / 180.0F * (float)Math.PI);
|
|
motionY = -MathHelper.sin(rotationPitch / 180.0F * (float)Math.PI);
|
|
this.setThrowableHeading(motionX, motionY, motionZ, par3 * 1.5F, 1.0F);
|
|
|
|
}
|
|
|
|
@Override
|
|
protected void entityInit() {
|
|
dataWatcher.addObject(16, Byte.valueOf((byte)0));
|
|
}
|
|
|
|
/**
|
|
* Similar to setArrowHeading, it's point the throwable entity to a x, y, z
|
|
* direction.
|
|
*/
|
|
@Override
|
|
public void setThrowableHeading(double var1, double var3, double var5, float var7, float var8) {
|
|
float var9 = MathHelper.sqrt_double(var1 * var1 + var3 * var3 + var5 * var5);
|
|
var1 /= var9;
|
|
var3 /= var9;
|
|
var5 /= var9;
|
|
var1 += rand.nextGaussian() * 0.007499999832361937D * var8;
|
|
var3 += rand.nextGaussian() * 0.007499999832361937D * var8;
|
|
var5 += rand.nextGaussian() * 0.007499999832361937D * var8;
|
|
var1 *= var7;
|
|
var3 *= var7;
|
|
var5 *= var7;
|
|
motionX = var1;
|
|
motionY = var3;
|
|
motionZ = var5;
|
|
float var10 = MathHelper.sqrt_double(var1 * var1 + var5 * var5);
|
|
prevRotationYaw = rotationYaw = (float)(Math.atan2(var1, var5) * 180.0D / Math.PI);
|
|
prevRotationPitch = rotationPitch = (float)(Math.atan2(var3, var10) * 180.0D / Math.PI);
|
|
}
|
|
|
|
@Override
|
|
@SideOnly(Side.CLIENT)
|
|
/**
|
|
* Sets the position and rotation. Only difference from the other one is no bounding on the rotation. Args: posX,
|
|
* posY, posZ, yaw, pitch
|
|
*/
|
|
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) {
|
|
this.setPosition(par1, par3, par5);
|
|
this.setRotation(par7, par8);
|
|
}
|
|
|
|
@Override
|
|
@SideOnly(Side.CLIENT)
|
|
/**
|
|
* Sets the velocity to the args. Args: x, y, z
|
|
*/
|
|
public void setVelocity(double par1, double par3, double par5) {
|
|
motionX = par1;
|
|
motionY = par3;
|
|
motionZ = par5;
|
|
if (prevRotationPitch == 0.0F && prevRotationYaw == 0.0F) {
|
|
float var7 = MathHelper.sqrt_double(par1 * par1 + par5 * par5);
|
|
prevRotationYaw = rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI);
|
|
prevRotationPitch = rotationPitch = (float)(Math.atan2(par3, var7) * 180.0D / Math.PI);
|
|
prevRotationPitch = rotationPitch;
|
|
prevRotationYaw = rotationYaw;
|
|
this.setLocationAndAngles(posX, posY, posZ, rotationYaw, rotationPitch);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called to update the entity's position/logic.
|
|
*/
|
|
@Override
|
|
public void onUpdate()
|
|
{
|
|
super.onUpdate();
|
|
this.performUpdateEffects();
|
|
if (ticksInAir > 600) {
|
|
this.setDead();
|
|
}
|
|
if (shootingEntity == null) {
|
|
List players = worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox(posX - 1, posY - 1, posZ - 1, posX + 1, posY + 1, posZ + 1));
|
|
Iterator i = players.iterator();
|
|
double closestDistance = Double.MAX_VALUE;
|
|
EntityPlayer closestPlayer = null;
|
|
while (i.hasNext()) {
|
|
EntityPlayer e = (EntityPlayer)i.next();
|
|
double distance = e.getDistanceToEntity(this);
|
|
if (distance < closestDistance) {
|
|
closestPlayer = e;
|
|
}
|
|
}
|
|
if (closestPlayer != null) {
|
|
shootingEntity = closestPlayer;
|
|
}
|
|
}
|
|
if (prevRotationPitch == 0.0F && prevRotationYaw == 0.0F) {
|
|
float var1 = MathHelper.sqrt_double(motionX * motionX + motionZ * motionZ);
|
|
prevRotationYaw = rotationYaw = (float)(Math.atan2(motionX, motionZ) * 180.0D / Math.PI);
|
|
prevRotationPitch = rotationPitch = (float)(Math.atan2(motionY, var1) * 180.0D / Math.PI);
|
|
}
|
|
int var16 = worldObj.getBlockId(xTile, yTile, zTile);
|
|
if (var16 > 0) {
|
|
Block.blocksList[var16].setBlockBoundsBasedOnState(worldObj, xTile, yTile, zTile);
|
|
AxisAlignedBB var2 = Block.blocksList[var16].getCollisionBoundingBoxFromPool(worldObj, xTile, yTile, zTile);
|
|
if (var2 != null && var2.isVecInside(worldObj.getWorldVec3Pool().getVecFromPool(posX, posY, posZ))) {
|
|
inGround = true;
|
|
}
|
|
}
|
|
if (inGround) {
|
|
int var18 = worldObj.getBlockId(xTile, yTile, zTile);
|
|
int var19 = worldObj.getBlockMetadata(xTile, yTile, zTile);
|
|
if (var18 == inTile && var19 == inData) {
|
|
// this.groundImpact();
|
|
// this.setDead();
|
|
}
|
|
} else {
|
|
++ticksInAir;
|
|
if (ticksInAir > 1 && ticksInAir < 3) {
|
|
worldObj.spawnParticle("flame", posX + smallGauss(0.1D), posY + smallGauss(0.1D), posZ + smallGauss(0.1D), 0D, 0D, 0D);
|
|
for (int particles = 0; particles < 3; particles++) {
|
|
this.doFiringParticles();
|
|
}
|
|
} else {
|
|
this.doFlightParticles();
|
|
}
|
|
Vec3 var17 = worldObj.getWorldVec3Pool().getVecFromPool(posX, posY, posZ);
|
|
Vec3 var3 = worldObj.getWorldVec3Pool().getVecFromPool(posX + motionX, posY + motionY, posZ + motionZ);
|
|
MovingObjectPosition var4 = worldObj.rayTraceBlocks_do_do(var17, var3, false, true);
|
|
var17 = worldObj.getWorldVec3Pool().getVecFromPool(posX, posY, posZ);
|
|
var3 = worldObj.getWorldVec3Pool().getVecFromPool(posX + motionX, posY + motionY, posZ + motionZ);
|
|
if (var4 != null) {
|
|
var3 = worldObj.getWorldVec3Pool().getVecFromPool(var4.hitVec.xCoord, var4.hitVec.yCoord, var4.hitVec.zCoord);
|
|
}
|
|
Entity var5 = null;
|
|
List var6 = worldObj.getEntitiesWithinAABBExcludingEntity(this, boundingBox.addCoord(motionX, motionY, motionZ).expand(1.0D, 1.0D, 1.0D));
|
|
double var7 = 0.0D;
|
|
Iterator var9 = var6.iterator();
|
|
float var11;
|
|
while (var9.hasNext()) {
|
|
Entity var10 = (Entity)var9.next();
|
|
if (var10.canBeCollidedWith() && (var10 != shootingEntity || ticksInAir >= 5)) {
|
|
var11 = 0.3F;
|
|
AxisAlignedBB var12 = var10.boundingBox.expand(var11, var11, var11);
|
|
MovingObjectPosition var13 = var12.calculateIntercept(var17, var3);
|
|
if (var13 != null) {
|
|
double var14 = var17.distanceTo(var13.hitVec);
|
|
if (var14 < var7 || var7 == 0.0D) {
|
|
var5 = var10;
|
|
var7 = var14;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (var5 != null) {
|
|
var4 = new MovingObjectPosition(var5);
|
|
}
|
|
if (var4 != null) {
|
|
this.onImpact(var4);
|
|
if (scheduledForDeath) {
|
|
this.setDead();
|
|
}
|
|
}
|
|
posX += motionX;
|
|
posY += motionY;
|
|
posZ += motionZ;
|
|
MathHelper.sqrt_double(motionX * motionX + motionZ * motionZ);
|
|
this.setPosition(posX, posY, posZ);
|
|
this.doBlockCollisions();
|
|
}
|
|
}
|
|
|
|
private void doFlightParticles() {
|
|
if (ticksInAir % 3 == 0) {
|
|
double gauss = gaussian(1.0F);
|
|
worldObj.spawnParticle("mobSpell", posX, posY, posZ, gauss, gauss, 0.0F);
|
|
}
|
|
}
|
|
|
|
private void doFiringParticles() {
|
|
worldObj.spawnParticle("mobSpellAmbient", posX + smallGauss(0.1D), posY + smallGauss(0.1D), posZ + smallGauss(0.1D), 0.5D, 0.5D, 0.5D);
|
|
worldObj.spawnParticle("flame", posX, posY, posZ, gaussian(motionX), gaussian(motionY), gaussian(motionZ));
|
|
}
|
|
|
|
/**
|
|
* (abstract) Protected helper method to write subclass entity data to NBT.
|
|
*/
|
|
@Override
|
|
public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
|
|
{
|
|
par1NBTTagCompound.setShort("xTile", (short)xTile);
|
|
par1NBTTagCompound.setShort("yTile", (short)yTile);
|
|
par1NBTTagCompound.setShort("zTile", (short)zTile);
|
|
par1NBTTagCompound.setByte("inTile", (byte)inTile);
|
|
par1NBTTagCompound.setByte("inData", (byte)inData);
|
|
par1NBTTagCompound.setByte("inGround", (byte)(inGround ? 1 : 0));
|
|
|
|
NBTTagList effectList = new NBTTagList();
|
|
|
|
for(SpellEffect eff : spellEffectList)
|
|
{
|
|
effectList.appendTag(eff.getTag());
|
|
}
|
|
|
|
// for (String str : this.effectList)
|
|
// {
|
|
// if (str != null)
|
|
// {
|
|
// NBTTagCompound tag = new NBTTagCompound();
|
|
//
|
|
// tag.setString("Class", str);
|
|
// effectList.appendTag(tag);
|
|
// }
|
|
// }
|
|
|
|
par1NBTTagCompound.setTag("Effects", effectList);
|
|
}
|
|
|
|
/**
|
|
* (abstract) Protected helper method to read subclass entity data from NBT.
|
|
*/
|
|
@Override
|
|
public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
|
|
{
|
|
xTile = par1NBTTagCompound.getShort("xTile");
|
|
yTile = par1NBTTagCompound.getShort("yTile");
|
|
zTile = par1NBTTagCompound.getShort("zTile");
|
|
inTile = par1NBTTagCompound.getByte("inTile") & 255;
|
|
inData = par1NBTTagCompound.getByte("inData") & 255;
|
|
inGround = par1NBTTagCompound.getByte("inGround") == 1;
|
|
|
|
NBTTagList tagList = par1NBTTagCompound.getTagList("Effects");
|
|
|
|
List<SpellEffect> spellEffectList = new LinkedList();
|
|
for (int i = 0; i < tagList.tagCount(); i++)
|
|
{
|
|
NBTTagCompound tag = (NBTTagCompound) tagList.tagAt(i);
|
|
|
|
SpellEffect eff = SpellEffect.getEffectFromTag(tag);
|
|
if(eff!=null)
|
|
{
|
|
spellEffectList.add(eff);
|
|
}
|
|
}
|
|
this.spellEffectList = spellEffectList;
|
|
|
|
|
|
// this.effectList = new LinkedList();
|
|
// for (int i = 0; i < tagList.tagCount(); i++)
|
|
// {
|
|
// NBTTagCompound tag = (NBTTagCompound) tagList.tagAt(i);
|
|
//
|
|
// this.effectList.add(tag.getString("Class"));
|
|
// }
|
|
|
|
//SpellParadigmProjectile parad = SpellParadigmProjectile.getParadigmForStringArray(effectList);
|
|
SpellParadigmProjectile parad = SpellParadigmProjectile.getParadigmForEffectArray(spellEffectList);
|
|
parad.applyAllSpellEffects();
|
|
parad.prepareProjectile(this);
|
|
}
|
|
|
|
/**
|
|
* returns if this entity triggers Block.onEntityWalking on the blocks they
|
|
* walk on. used for spiders and wolves to prevent them from trampling crops
|
|
*/
|
|
@Override
|
|
protected boolean canTriggerWalking() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
@SideOnly(Side.CLIENT)
|
|
public float getShadowSize() {
|
|
return 0.0F;
|
|
}
|
|
|
|
/**
|
|
* Sets the amount of knockback the arrow applies when it hits a mob.
|
|
*/
|
|
public void setKnockbackStrength(int par1) {
|
|
}
|
|
|
|
/**
|
|
* If returns false, the item will not inflict any damage against entities.
|
|
*/
|
|
@Override
|
|
public boolean canAttackWithItem() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Whether the arrow has a stream of critical hit particles flying behind
|
|
* it.
|
|
*/
|
|
public void setIsCritical(boolean par1) {
|
|
byte var2 = dataWatcher.getWatchableObjectByte(16);
|
|
if (par1) {
|
|
dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 1)));
|
|
} else {
|
|
dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -2)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the arrow has a stream of critical hit particles flying behind
|
|
* it.
|
|
*/
|
|
public boolean getIsCritical() {
|
|
byte var1 = dataWatcher.getWatchableObjectByte(16);
|
|
return (var1 & 1) != 0;
|
|
}
|
|
|
|
private void onImpact(MovingObjectPosition mop)
|
|
{
|
|
if (mop.typeOfHit == EnumMovingObjectType.ENTITY && mop.entityHit != null)
|
|
{
|
|
if (mop.entityHit == shootingEntity) return;
|
|
this.onImpact(mop.entityHit);
|
|
this.performEntityImpactEffects(mop.entityHit);
|
|
}
|
|
else if (mop.typeOfHit == EnumMovingObjectType.TILE)
|
|
{
|
|
if(!this.penetration)
|
|
{
|
|
this.groundImpact(mop.sideHit);
|
|
this.performTileImpactEffects(mop);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void onImpact(Entity mop) //TODO
|
|
{
|
|
if (mop == shootingEntity && ticksInAir > 3)
|
|
{
|
|
shootingEntity.attackEntityFrom(DamageSource.causePlayerDamage(shootingEntity), 1);
|
|
this.setDead();
|
|
}
|
|
else
|
|
{
|
|
doDamage(this.damage, mop);
|
|
}
|
|
spawnHitParticles("exorcism", 8);
|
|
this.setDead();
|
|
}
|
|
|
|
|
|
private void spawnHitParticles(String string, int i) {
|
|
for (int particles = 0; particles < i; particles++) {
|
|
worldObj.spawnParticle("mobSpellAmbient", posX + smallGauss(0.1D), posY + smallGauss(0.1D), posZ + smallGauss(0.1D), posGauss(1.0F), posGauss(1.0F), 0.0F);
|
|
}
|
|
}
|
|
|
|
private void doDamage(float f, Entity mop)
|
|
{
|
|
mop.attackEntityFrom(this.getDamageSource(), f);
|
|
}
|
|
|
|
private DamageSource getDamageSource()
|
|
{
|
|
return DamageSource.causePlayerDamage(shootingEntity);
|
|
}
|
|
|
|
private void groundImpact(int sideHit) {
|
|
this.ricochet(sideHit);
|
|
}
|
|
|
|
private double smallGauss(double d) {
|
|
return (worldObj.rand.nextFloat() - 0.5D) * d;
|
|
}
|
|
|
|
private double posGauss(double d) {
|
|
return rand.nextFloat() * 0.5D * d;
|
|
}
|
|
|
|
private double gaussian(double d) {
|
|
return d + d * ((rand.nextFloat() - 0.5D) / 4);
|
|
}
|
|
|
|
private void ricochet(int sideHit) {
|
|
switch (sideHit) {
|
|
case 0:
|
|
case 1:
|
|
// topHit, bottomHit, reflect Y
|
|
motionY = motionY * -1;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
// westHit, eastHit, reflect Z
|
|
motionZ = motionZ * -1;
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
// southHit, northHit, reflect X
|
|
motionX = motionX * -1;
|
|
break;
|
|
}
|
|
ricochetCounter++;
|
|
if (ricochetCounter > this.getRicochetMax()) {
|
|
scheduledForDeath = true;
|
|
for (int particles = 0; particles < 4; particles++) {
|
|
switch (sideHit) {
|
|
case 0:
|
|
worldObj.spawnParticle("smoke", posX, posY, posZ, gaussian(0.1D), -gaussian(0.1D), gaussian(0.1D));
|
|
break;
|
|
case 1:
|
|
worldObj.spawnParticle("smoke", posX, posY, posZ, gaussian(0.1D), gaussian(0.1D), gaussian(0.1D));
|
|
break;
|
|
case 2:
|
|
worldObj.spawnParticle("smoke", posX, posY, posZ, gaussian(0.1D), gaussian(0.1D), -gaussian(0.1D));
|
|
break;
|
|
case 3:
|
|
worldObj.spawnParticle("smoke", posX, posY, posZ, gaussian(0.1D), gaussian(0.1D), gaussian(0.1D));
|
|
break;
|
|
case 4:
|
|
worldObj.spawnParticle("smoke", posX, posY, posZ, -gaussian(0.1D), gaussian(0.1D), gaussian(0.1D));
|
|
break;
|
|
case 5:
|
|
worldObj.spawnParticle("smoke", posX, posY, posZ, gaussian(0.1D), gaussian(0.1D), gaussian(0.1D));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Custom stuff
|
|
public int getRicochetMax()
|
|
{
|
|
return this.maxRicochet;
|
|
}
|
|
|
|
public void setRicochetMax(int ricochet)
|
|
{
|
|
this.maxRicochet = ricochet;
|
|
}
|
|
|
|
public void setImpactList(List<IProjectileImpactEffect> list)
|
|
{
|
|
this.impactList = list;
|
|
}
|
|
|
|
public void setUpdateEffectList(List<IProjectileUpdateEffect> list)
|
|
{
|
|
this.updateEffectList = list;
|
|
}
|
|
|
|
private void performEntityImpactEffects(Entity mop)
|
|
{
|
|
if(impactList!=null)
|
|
{
|
|
for(IProjectileImpactEffect impactEffect : impactList)
|
|
{
|
|
impactEffect.onEntityImpact(mop);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void performTileImpactEffects(MovingObjectPosition mop)
|
|
{
|
|
if(impactList!=null)
|
|
{
|
|
for(IProjectileImpactEffect impactEffect : impactList)
|
|
{
|
|
impactEffect.onTileImpact(worldObj, mop);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void performUpdateEffects()
|
|
{
|
|
if(updateEffectList!=null)
|
|
{
|
|
for(IProjectileUpdateEffect updateEffect : updateEffectList)
|
|
{
|
|
updateEffect.onUpdateEffect(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setPenetration(boolean penetration)
|
|
{
|
|
this.penetration = penetration;
|
|
}
|
|
|
|
public float getDamage()
|
|
{
|
|
return this.damage;
|
|
}
|
|
|
|
public void setDamage(float damage)
|
|
{
|
|
this.damage = damage;
|
|
}
|
|
|
|
public void setEffectList(List<String> stringList)
|
|
{
|
|
this.effectList = stringList;
|
|
}
|
|
|
|
public void setSpellEffectList(List<SpellEffect> list)
|
|
{
|
|
this.spellEffectList = list;
|
|
}
|
|
}
|