001 package net.minecraft.src;
002
003 import cpw.mods.fml.common.Side;
004 import cpw.mods.fml.common.asm.SideOnly;
005
006 import java.util.ArrayList;
007 import java.util.List;
008 import net.minecraftforge.common.IMinecartCollisionHandler;
009 import net.minecraftforge.common.MinecartRegistry;
010 import net.minecraftforge.common.MinecraftForge;
011 import net.minecraftforge.event.entity.minecart.MinecartCollisionEvent;
012 import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
013 import net.minecraftforge.event.entity.minecart.MinecartUpdateEvent;
014
015 public class EntityMinecart extends Entity implements IInventory
016 {
017 /** Array of item stacks stored in minecart (for storage minecarts). */
018 protected ItemStack[] cargoItems;
019 protected int fuel;
020 protected boolean field_70499_f;
021
022 /** The type of minecart, 2 for powered, 1 for storage. */
023 public int minecartType;
024 public double pushX;
025 public double pushZ;
026 protected static final int[][][] field_70500_g = new int[][][] {{{0, 0, -1}, {0, 0, 1}}, {{ -1, 0, 0}, {1, 0, 0}}, {{ -1, -1, 0}, {1, 0, 0}}, {{ -1, 0, 0}, {1, -1, 0}}, {{0, 0, -1}, {0, -1, 1}}, {{0, -1, -1}, {0, 0, 1}}, {{0, 0, 1}, {1, 0, 0}}, {{0, 0, 1}, { -1, 0, 0}}, {{0, 0, -1}, { -1, 0, 0}}, {{0, 0, -1}, {1, 0, 0}}};
027
028 /** appears to be the progress of the turn */
029 protected int turnProgress;
030 protected double minecartX;
031 protected double minecartY;
032 protected double minecartZ;
033 protected double minecartYaw;
034 protected double minecartPitch;
035 @SideOnly(Side.CLIENT)
036 protected double velocityX;
037 @SideOnly(Side.CLIENT)
038 protected double velocityY;
039 @SideOnly(Side.CLIENT)
040 protected double velocityZ;
041
042 /* Forge: Minecart Compatibility Layer Integration. */
043 public static float defaultMaxSpeedRail = 0.4f;
044 public static float defaultMaxSpeedGround = 0.4f;
045 public static float defaultMaxSpeedAirLateral = 0.4f;
046 public static float defaultMaxSpeedAirVertical = -1f;
047 public static double defaultDragRidden = 0.996999979019165D;
048 public static double defaultDragEmpty = 0.9599999785423279D;
049 public static double defaultDragAir = 0.94999998807907104D;
050 protected boolean canUseRail = true;
051 protected boolean canBePushed = true;
052 private static IMinecartCollisionHandler collisionHandler = null;
053
054 /* Instance versions of the above physics properties */
055 protected float maxSpeedRail;
056 protected float maxSpeedGround;
057 protected float maxSpeedAirLateral;
058 protected float maxSpeedAirVertical;
059 protected double dragAir;
060
061 public EntityMinecart(World par1World)
062 {
063 super(par1World);
064 this.cargoItems = new ItemStack[36];
065 this.fuel = 0;
066 this.field_70499_f = false;
067 this.preventEntitySpawning = true;
068 this.setSize(0.98F, 0.7F);
069 this.yOffset = this.height / 2.0F;
070
071 maxSpeedRail = defaultMaxSpeedRail;
072 maxSpeedGround = defaultMaxSpeedGround;
073 maxSpeedAirLateral = defaultMaxSpeedAirLateral;
074 maxSpeedAirVertical = defaultMaxSpeedAirVertical;
075 dragAir = defaultDragAir;
076 }
077
078 public EntityMinecart(World world, int type)
079 {
080 this(world);
081 minecartType = type;
082 }
083
084 /**
085 * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
086 * prevent them from trampling crops
087 */
088 protected boolean canTriggerWalking()
089 {
090 return false;
091 }
092
093 protected void entityInit()
094 {
095 this.dataWatcher.addObject(16, new Byte((byte)0));
096 this.dataWatcher.addObject(17, new Integer(0));
097 this.dataWatcher.addObject(18, new Integer(1));
098 this.dataWatcher.addObject(19, new Integer(0));
099 }
100
101 /**
102 * Returns a boundingBox used to collide the entity with other entities and blocks. This enables the entity to be
103 * pushable on contact, like boats or minecarts.
104 */
105 public AxisAlignedBB getCollisionBox(Entity par1Entity)
106 {
107 if (getCollisionHandler() != null)
108 {
109 return getCollisionHandler().getCollisionBox(this, par1Entity);
110 }
111 return par1Entity.boundingBox;
112 }
113
114 /**
115 * returns the bounding box for this entity
116 */
117 public AxisAlignedBB getBoundingBox()
118 {
119 if (getCollisionHandler() != null)
120 {
121 return getCollisionHandler().getBoundingBox(this);
122 }
123 return null;
124 }
125
126 /**
127 * Returns true if this entity should push and be pushed by other entities when colliding.
128 */
129 public boolean canBePushed()
130 {
131 return canBePushed;
132 }
133
134 public EntityMinecart(World par1World, double par2, double par4, double par6, int par8)
135 {
136 this(par1World);
137 this.setPosition(par2, par4 + (double)this.yOffset, par6);
138 this.motionX = 0.0D;
139 this.motionY = 0.0D;
140 this.motionZ = 0.0D;
141 this.prevPosX = par2;
142 this.prevPosY = par4;
143 this.prevPosZ = par6;
144 this.minecartType = par8;
145 }
146
147 /**
148 * Returns the Y offset from the entity's position for any entity riding this one.
149 */
150 public double getMountedYOffset()
151 {
152 return (double)this.height * 0.0D - 0.30000001192092896D;
153 }
154
155 /**
156 * Called when the entity is attacked.
157 */
158 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
159 {
160 if (!this.worldObj.isRemote && !this.isDead)
161 {
162 this.func_70494_i(-this.func_70493_k());
163 this.func_70497_h(10);
164 this.setBeenAttacked();
165 this.setDamage(this.getDamage() + par2 * 10);
166
167 if (par1DamageSource.getEntity() instanceof EntityPlayer && ((EntityPlayer)par1DamageSource.getEntity()).capabilities.isCreativeMode)
168 {
169 this.setDamage(100);
170 }
171
172 if (this.getDamage() > 40)
173 {
174 if (this.riddenByEntity != null)
175 {
176 this.riddenByEntity.mountEntity(this);
177 }
178
179 this.setDead();
180 dropCartAsItem();
181 }
182
183 return true;
184 }
185 else
186 {
187 return true;
188 }
189 }
190
191 @SideOnly(Side.CLIENT)
192
193 /**
194 * Setups the entity to do the hurt animation. Only used by packets in multiplayer.
195 */
196 public void performHurtAnimation()
197 {
198 this.func_70494_i(-this.func_70493_k());
199 this.func_70497_h(10);
200 this.setDamage(this.getDamage() + this.getDamage() * 10);
201 }
202
203 /**
204 * Returns true if other Entities should be prevented from moving through this Entity.
205 */
206 public boolean canBeCollidedWith()
207 {
208 return !this.isDead;
209 }
210
211 /**
212 * Will get destroyed next tick.
213 */
214 public void setDead()
215 {
216 for (int var1 = 0; var1 < this.getSizeInventory(); ++var1)
217 {
218 ItemStack var2 = this.getStackInSlot(var1);
219
220 if (var2 != null)
221 {
222 float var3 = this.rand.nextFloat() * 0.8F + 0.1F;
223 float var4 = this.rand.nextFloat() * 0.8F + 0.1F;
224 float var5 = this.rand.nextFloat() * 0.8F + 0.1F;
225
226 while (var2.stackSize > 0)
227 {
228 int var6 = this.rand.nextInt(21) + 10;
229
230 if (var6 > var2.stackSize)
231 {
232 var6 = var2.stackSize;
233 }
234
235 var2.stackSize -= var6;
236 EntityItem var7 = new EntityItem(this.worldObj, this.posX + (double)var3, this.posY + (double)var4, this.posZ + (double)var5, new ItemStack(var2.itemID, var6, var2.getItemDamage()));
237
238 if (var2.hasTagCompound())
239 {
240 var7.item.setTagCompound((NBTTagCompound)var2.getTagCompound().copy());
241 }
242
243 float var8 = 0.05F;
244 var7.motionX = (double)((float)this.rand.nextGaussian() * var8);
245 var7.motionY = (double)((float)this.rand.nextGaussian() * var8 + 0.2F);
246 var7.motionZ = (double)((float)this.rand.nextGaussian() * var8);
247 this.worldObj.spawnEntityInWorld(var7);
248 }
249 }
250 }
251
252 super.setDead();
253 }
254
255 /**
256 * Called to update the entity's position/logic.
257 */
258 public void onUpdate()
259 {
260 if (this.func_70496_j() > 0)
261 {
262 this.func_70497_h(this.func_70496_j() - 1);
263 }
264
265 if (this.getDamage() > 0)
266 {
267 this.setDamage(this.getDamage() - 1);
268 }
269
270 if (this.posY < -64.0D)
271 {
272 this.kill();
273 }
274
275 if (this.isMinecartPowered() && this.rand.nextInt(4) == 0 && minecartType == 2 && getClass() == EntityMinecart.class)
276 {
277 this.worldObj.spawnParticle("largesmoke", this.posX, this.posY + 0.8D, this.posZ, 0.0D, 0.0D, 0.0D);
278 }
279
280 if (this.worldObj.isRemote)
281 {
282 if (this.turnProgress > 0)
283 {
284 double var45 = this.posX + (this.minecartX - this.posX) / (double)this.turnProgress;
285 double var46 = this.posY + (this.minecartY - this.posY) / (double)this.turnProgress;
286 double var5 = this.posZ + (this.minecartZ - this.posZ) / (double)this.turnProgress;
287 double var7 = MathHelper.wrapAngleTo180_double(this.minecartYaw - (double)this.rotationYaw);
288 this.rotationYaw = (float)((double)this.rotationYaw + var7 / (double)this.turnProgress);
289 this.rotationPitch = (float)((double)this.rotationPitch + (this.minecartPitch - (double)this.rotationPitch) / (double)this.turnProgress);
290 --this.turnProgress;
291 this.setPosition(var45, var46, var5);
292 this.setRotation(this.rotationYaw, this.rotationPitch);
293 }
294 else
295 {
296 this.setPosition(this.posX, this.posY, this.posZ);
297 this.setRotation(this.rotationYaw, this.rotationPitch);
298 }
299 }
300 else
301 {
302 this.prevPosX = this.posX;
303 this.prevPosY = this.posY;
304 this.prevPosZ = this.posZ;
305 this.motionY -= 0.03999999910593033D;
306 int var1 = MathHelper.floor_double(this.posX);
307 int var2 = MathHelper.floor_double(this.posY);
308 int var3 = MathHelper.floor_double(this.posZ);
309
310 if (BlockRail.isRailBlockAt(this.worldObj, var1, var2 - 1, var3))
311 {
312 --var2;
313 }
314
315 double var4 = 0.4D;
316 double var6 = 0.0078125D;
317 int var8 = this.worldObj.getBlockId(var1, var2, var3);
318
319 if (canUseRail() && BlockRail.isRailBlock(var8))
320 {
321 Vec3 var9 = this.func_70489_a(this.posX, this.posY, this.posZ);
322 int var10 = ((BlockRail)Block.blocksList[var8]).getBasicRailMetadata(worldObj, this, var1, var2, var3);
323 this.posY = (double)var2;
324 boolean var11 = false;
325 boolean var12 = false;
326
327 if (var8 == Block.railPowered.blockID)
328 {
329 var11 = (worldObj.getBlockMetadata(var1, var2, var3) & 8) != 0;
330 var12 = !var11;
331 }
332
333 if (((BlockRail)Block.blocksList[var8]).isPowered())
334 {
335 var10 &= 7;
336 }
337
338 if (var10 >= 2 && var10 <= 5)
339 {
340 this.posY = (double)(var2 + 1);
341 }
342
343 adjustSlopeVelocities(var10);
344
345 int[][] var13 = field_70500_g[var10];
346 double var14 = (double)(var13[1][0] - var13[0][0]);
347 double var16 = (double)(var13[1][2] - var13[0][2]);
348 double var18 = Math.sqrt(var14 * var14 + var16 * var16);
349 double var20 = this.motionX * var14 + this.motionZ * var16;
350
351 if (var20 < 0.0D)
352 {
353 var14 = -var14;
354 var16 = -var16;
355 }
356
357 double var22 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
358 this.motionX = var22 * var14 / var18;
359 this.motionZ = var22 * var16 / var18;
360 double var24;
361 double var26;
362
363 if (this.riddenByEntity != null)
364 {
365 var24 = this.riddenByEntity.motionX * this.riddenByEntity.motionX + this.riddenByEntity.motionZ * this.riddenByEntity.motionZ;
366 var26 = this.motionX * this.motionX + this.motionZ * this.motionZ;
367
368 if (var24 > 1.0E-4D && var26 < 0.01D)
369 {
370 this.motionX += this.riddenByEntity.motionX * 0.1D;
371 this.motionZ += this.riddenByEntity.motionZ * 0.1D;
372 var12 = false;
373 }
374 }
375
376 if (var12 && shouldDoRailFunctions())
377 {
378 var24 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
379
380 if (var24 < 0.03D)
381 {
382 this.motionX *= 0.0D;
383 this.motionY *= 0.0D;
384 this.motionZ *= 0.0D;
385 }
386 else
387 {
388 this.motionX *= 0.5D;
389 this.motionY *= 0.0D;
390 this.motionZ *= 0.5D;
391 }
392 }
393
394 var24 = 0.0D;
395 var26 = (double)var1 + 0.5D + (double)var13[0][0] * 0.5D;
396 double var28 = (double)var3 + 0.5D + (double)var13[0][2] * 0.5D;
397 double var30 = (double)var1 + 0.5D + (double)var13[1][0] * 0.5D;
398 double var32 = (double)var3 + 0.5D + (double)var13[1][2] * 0.5D;
399 var14 = var30 - var26;
400 var16 = var32 - var28;
401 double var34;
402 double var36;
403
404 if (var14 == 0.0D)
405 {
406 this.posX = (double)var1 + 0.5D;
407 var24 = this.posZ - (double)var3;
408 }
409 else if (var16 == 0.0D)
410 {
411 this.posZ = (double)var3 + 0.5D;
412 var24 = this.posX - (double)var1;
413 }
414 else
415 {
416 var34 = this.posX - var26;
417 var36 = this.posZ - var28;
418 var24 = (var34 * var14 + var36 * var16) * 2.0D;
419 }
420
421 this.posX = var26 + var14 * var24;
422 this.posZ = var28 + var16 * var24;
423 this.setPosition(this.posX, this.posY + (double)this.yOffset, this.posZ);
424
425 moveMinecartOnRail(var1, var2, var3);
426
427 if (var13[0][1] != 0 && MathHelper.floor_double(this.posX) - var1 == var13[0][0] && MathHelper.floor_double(this.posZ) - var3 == var13[0][2])
428 {
429 this.setPosition(this.posX, this.posY + (double)var13[0][1], this.posZ);
430 }
431 else if (var13[1][1] != 0 && MathHelper.floor_double(this.posX) - var1 == var13[1][0] && MathHelper.floor_double(this.posZ) - var3 == var13[1][2])
432 {
433 this.setPosition(this.posX, this.posY + (double)var13[1][1], this.posZ);
434 }
435
436 applyDragAndPushForces();
437
438 Vec3 var52 = this.func_70489_a(this.posX, this.posY, this.posZ);
439
440 if (var52 != null && var9 != null)
441 {
442 double var39 = (var9.yCoord - var52.yCoord) * 0.05D;
443 var22 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
444
445 if (var22 > 0.0D)
446 {
447 this.motionX = this.motionX / var22 * (var22 + var39);
448 this.motionZ = this.motionZ / var22 * (var22 + var39);
449 }
450
451 this.setPosition(this.posX, var52.yCoord, this.posZ);
452 }
453
454 int var51 = MathHelper.floor_double(this.posX);
455 int var53 = MathHelper.floor_double(this.posZ);
456
457 if (var51 != var1 || var53 != var3)
458 {
459 var22 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
460 this.motionX = var22 * (double)(var51 - var1);
461 this.motionZ = var22 * (double)(var53 - var3);
462 }
463
464 double var41;
465
466 updatePushForces();
467
468 if(shouldDoRailFunctions())
469 {
470 ((BlockRail)Block.blocksList[var8]).onMinecartPass(worldObj, this, var1, var2, var3);
471 }
472
473 if (var11 && shouldDoRailFunctions())
474 {
475 var41 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
476
477 if (var41 > 0.01D)
478 {
479 double var43 = 0.06D;
480 this.motionX += this.motionX / var41 * var43;
481 this.motionZ += this.motionZ / var41 * var43;
482 }
483 else if (var10 == 1)
484 {
485 if (this.worldObj.isBlockNormalCube(var1 - 1, var2, var3))
486 {
487 this.motionX = 0.02D;
488 }
489 else if (this.worldObj.isBlockNormalCube(var1 + 1, var2, var3))
490 {
491 this.motionX = -0.02D;
492 }
493 }
494 else if (var10 == 0)
495 {
496 if (this.worldObj.isBlockNormalCube(var1, var2, var3 - 1))
497 {
498 this.motionZ = 0.02D;
499 }
500 else if (this.worldObj.isBlockNormalCube(var1, var2, var3 + 1))
501 {
502 this.motionZ = -0.02D;
503 }
504 }
505 }
506
507 this.doBlockCollisions();
508 }
509 else
510 {
511 moveMinecartOffRail(var1, var2, var3);
512 }
513
514 this.rotationPitch = 0.0F;
515 double var47 = this.prevPosX - this.posX;
516 double var48 = this.prevPosZ - this.posZ;
517
518 if (var47 * var47 + var48 * var48 > 0.001D)
519 {
520 this.rotationYaw = (float)(Math.atan2(var48, var47) * 180.0D / Math.PI);
521
522 if (this.field_70499_f)
523 {
524 this.rotationYaw += 180.0F;
525 }
526 }
527
528 double var49 = (double)MathHelper.wrapAngleTo180_float(this.rotationYaw - this.prevRotationYaw);
529
530 if (var49 < -170.0D || var49 >= 170.0D)
531 {
532 this.rotationYaw += 180.0F;
533 this.field_70499_f = !this.field_70499_f;
534 }
535
536 this.setRotation(this.rotationYaw, this.rotationPitch);
537
538 AxisAlignedBB box = null;
539 if (getCollisionHandler() != null)
540 {
541 box = getCollisionHandler().getMinecartCollisionBox(this);
542 }
543 else
544 {
545 box = boundingBox.expand(0.2D, 0.0D, 0.2D);
546 }
547
548 List var15 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, box);
549
550 if (var15 != null && !var15.isEmpty())
551 {
552 for (int var50 = 0; var50 < var15.size(); ++var50)
553 {
554 Entity var17 = (Entity)var15.get(var50);
555
556 if (var17 != this.riddenByEntity && var17.canBePushed() && var17 instanceof EntityMinecart)
557 {
558 var17.applyEntityCollision(this);
559 }
560 }
561 }
562
563 if (this.riddenByEntity != null && this.riddenByEntity.isDead)
564 {
565 if (this.riddenByEntity.ridingEntity == this)
566 {
567 this.riddenByEntity.ridingEntity = null;
568 }
569
570 this.riddenByEntity = null;
571 }
572
573 updateFuel();
574 MinecraftForge.EVENT_BUS.post(new MinecartUpdateEvent(this, var1, var2, var3));
575 }
576 }
577
578 @SideOnly(Side.CLIENT)
579 public Vec3 func_70495_a(double par1, double par3, double par5, double par7)
580 {
581 int var9 = MathHelper.floor_double(par1);
582 int var10 = MathHelper.floor_double(par3);
583 int var11 = MathHelper.floor_double(par5);
584
585 if (BlockRail.isRailBlockAt(this.worldObj, var9, var10 - 1, var11))
586 {
587 --var10;
588 }
589
590 int var12 = this.worldObj.getBlockId(var9, var10, var11);
591
592 if (!BlockRail.isRailBlock(var12))
593 {
594 return null;
595 }
596 else
597 {
598 int var13 = ((BlockRail)Block.blocksList[var12]).getBasicRailMetadata(worldObj, this, var9, var10, var11);
599
600 par3 = (double)var10;
601
602 if (var13 >= 2 && var13 <= 5)
603 {
604 par3 = (double)(var10 + 1);
605 }
606
607 int[][] var14 = field_70500_g[var13];
608 double var15 = (double)(var14[1][0] - var14[0][0]);
609 double var17 = (double)(var14[1][2] - var14[0][2]);
610 double var19 = Math.sqrt(var15 * var15 + var17 * var17);
611 var15 /= var19;
612 var17 /= var19;
613 par1 += var15 * par7;
614 par5 += var17 * par7;
615
616 if (var14[0][1] != 0 && MathHelper.floor_double(par1) - var9 == var14[0][0] && MathHelper.floor_double(par5) - var11 == var14[0][2])
617 {
618 par3 += (double)var14[0][1];
619 }
620 else if (var14[1][1] != 0 && MathHelper.floor_double(par1) - var9 == var14[1][0] && MathHelper.floor_double(par5) - var11 == var14[1][2])
621 {
622 par3 += (double)var14[1][1];
623 }
624
625 return this.func_70489_a(par1, par3, par5);
626 }
627 }
628
629 public Vec3 func_70489_a(double par1, double par3, double par5)
630 {
631 int var7 = MathHelper.floor_double(par1);
632 int var8 = MathHelper.floor_double(par3);
633 int var9 = MathHelper.floor_double(par5);
634
635 if (BlockRail.isRailBlockAt(this.worldObj, var7, var8 - 1, var9))
636 {
637 --var8;
638 }
639
640 int var10 = this.worldObj.getBlockId(var7, var8, var9);
641
642 if (BlockRail.isRailBlock(var10))
643 {
644 int var11 = ((BlockRail)Block.blocksList[var10]).getBasicRailMetadata(worldObj, this, var7, var8, var9);
645 par3 = (double)var8;
646
647 if (var11 >= 2 && var11 <= 5)
648 {
649 par3 = (double)(var8 + 1);
650 }
651
652 int[][] var12 = field_70500_g[var11];
653 double var13 = 0.0D;
654 double var15 = (double)var7 + 0.5D + (double)var12[0][0] * 0.5D;
655 double var17 = (double)var8 + 0.5D + (double)var12[0][1] * 0.5D;
656 double var19 = (double)var9 + 0.5D + (double)var12[0][2] * 0.5D;
657 double var21 = (double)var7 + 0.5D + (double)var12[1][0] * 0.5D;
658 double var23 = (double)var8 + 0.5D + (double)var12[1][1] * 0.5D;
659 double var25 = (double)var9 + 0.5D + (double)var12[1][2] * 0.5D;
660 double var27 = var21 - var15;
661 double var29 = (var23 - var17) * 2.0D;
662 double var31 = var25 - var19;
663
664 if (var27 == 0.0D)
665 {
666 par1 = (double)var7 + 0.5D;
667 var13 = par5 - (double)var9;
668 }
669 else if (var31 == 0.0D)
670 {
671 par5 = (double)var9 + 0.5D;
672 var13 = par1 - (double)var7;
673 }
674 else
675 {
676 double var33 = par1 - var15;
677 double var35 = par5 - var19;
678 var13 = (var33 * var27 + var35 * var31) * 2.0D;
679 }
680
681 par1 = var15 + var27 * var13;
682 par3 = var17 + var29 * var13;
683 par5 = var19 + var31 * var13;
684
685 if (var29 < 0.0D)
686 {
687 ++par3;
688 }
689
690 if (var29 > 0.0D)
691 {
692 par3 += 0.5D;
693 }
694
695 return Vec3.getVec3Pool().getVecFromPool(par1, par3, par5);
696 }
697 else
698 {
699 return null;
700 }
701 }
702
703 /**
704 * (abstract) Protected helper method to write subclass entity data to NBT.
705 */
706 protected void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
707 {
708 par1NBTTagCompound.setInteger("Type", this.minecartType);
709
710 if (isPoweredCart())
711 {
712 par1NBTTagCompound.setDouble("PushX", this.pushX);
713 par1NBTTagCompound.setDouble("PushZ", this.pushZ);
714 par1NBTTagCompound.setInteger("Fuel", this.fuel);
715 }
716
717 if (getSizeInventory() > 0)
718 {
719 NBTTagList var2 = new NBTTagList();
720
721 for (int var3 = 0; var3 < this.cargoItems.length; ++var3)
722 {
723 if (this.cargoItems[var3] != null)
724 {
725 NBTTagCompound var4 = new NBTTagCompound();
726 var4.setByte("Slot", (byte)var3);
727 this.cargoItems[var3].writeToNBT(var4);
728 var2.appendTag(var4);
729 }
730 }
731
732 par1NBTTagCompound.setTag("Items", var2);
733 }
734 }
735
736 /**
737 * (abstract) Protected helper method to read subclass entity data from NBT.
738 */
739 protected void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
740 {
741 this.minecartType = par1NBTTagCompound.getInteger("Type");
742
743 if (isPoweredCart())
744 {
745 this.pushX = par1NBTTagCompound.getDouble("PushX");
746 this.pushZ = par1NBTTagCompound.getDouble("PushZ");
747 try
748 {
749 this.fuel = par1NBTTagCompound.getInteger("Fuel");
750 }
751 catch (ClassCastException e)
752 {
753 this.fuel = par1NBTTagCompound.getShort("Fuel");
754 }
755 }
756
757 if (getSizeInventory() > 0)
758 {
759 NBTTagList var2 = par1NBTTagCompound.getTagList("Items");
760 this.cargoItems = new ItemStack[this.getSizeInventory()];
761
762 for (int var3 = 0; var3 < var2.tagCount(); ++var3)
763 {
764 NBTTagCompound var4 = (NBTTagCompound)var2.tagAt(var3);
765 int var5 = var4.getByte("Slot") & 255;
766
767 if (var5 >= 0 && var5 < this.cargoItems.length)
768 {
769 this.cargoItems[var5] = ItemStack.loadItemStackFromNBT(var4);
770 }
771 }
772 }
773 }
774
775 @SideOnly(Side.CLIENT)
776 public float getShadowSize()
777 {
778 return 0.0F;
779 }
780
781 /**
782 * Applies a velocity to each of the entities pushing them away from each other. Args: entity
783 */
784 public void applyEntityCollision(Entity par1Entity)
785 {
786 MinecraftForge.EVENT_BUS.post(new MinecartCollisionEvent(this, par1Entity));
787 if (getCollisionHandler() != null)
788 {
789 getCollisionHandler().onEntityCollision(this, par1Entity);
790 return;
791 }
792 if (!this.worldObj.isRemote)
793 {
794 if (par1Entity != this.riddenByEntity)
795 {
796 if (par1Entity instanceof EntityLiving && !(par1Entity instanceof EntityPlayer) && !(par1Entity instanceof EntityIronGolem) && canBeRidden() && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.01D && this.riddenByEntity == null && par1Entity.ridingEntity == null)
797 {
798 par1Entity.mountEntity(this);
799 }
800
801 double var2 = par1Entity.posX - this.posX;
802 double var4 = par1Entity.posZ - this.posZ;
803 double var6 = var2 * var2 + var4 * var4;
804
805 if (var6 >= 9.999999747378752E-5D)
806 {
807 var6 = (double)MathHelper.sqrt_double(var6);
808 var2 /= var6;
809 var4 /= var6;
810 double var8 = 1.0D / var6;
811
812 if (var8 > 1.0D)
813 {
814 var8 = 1.0D;
815 }
816
817 var2 *= var8;
818 var4 *= var8;
819 var2 *= 0.10000000149011612D;
820 var4 *= 0.10000000149011612D;
821 var2 *= (double)(1.0F - this.entityCollisionReduction);
822 var4 *= (double)(1.0F - this.entityCollisionReduction);
823 var2 *= 0.5D;
824 var4 *= 0.5D;
825
826 if (par1Entity instanceof EntityMinecart)
827 {
828 double var10 = par1Entity.posX - this.posX;
829 double var12 = par1Entity.posZ - this.posZ;
830 Vec3 var14 = Vec3.getVec3Pool().getVecFromPool(var10, 0.0D, var12).normalize();
831 Vec3 var15 = Vec3.getVec3Pool().getVecFromPool((double)MathHelper.cos(this.rotationYaw * (float)Math.PI / 180.0F), 0.0D, (double)MathHelper.sin(this.rotationYaw * (float)Math.PI / 180.0F)).normalize();
832 double var16 = Math.abs(var14.dotProduct(var15));
833
834 if (var16 < 0.800000011920929D)
835 {
836 return;
837 }
838
839 double var18 = par1Entity.motionX + this.motionX;
840 double var20 = par1Entity.motionZ + this.motionZ;
841
842 if (((EntityMinecart)par1Entity).isPoweredCart() && !isPoweredCart())
843 {
844 this.motionX *= 0.20000000298023224D;
845 this.motionZ *= 0.20000000298023224D;
846 this.addVelocity(par1Entity.motionX - var2, 0.0D, par1Entity.motionZ - var4);
847 par1Entity.motionX *= 0.949999988079071D;
848 par1Entity.motionZ *= 0.949999988079071D;
849 }
850 else if (!((EntityMinecart)par1Entity).isPoweredCart() && isPoweredCart())
851 {
852 par1Entity.motionX *= 0.20000000298023224D;
853 par1Entity.motionZ *= 0.20000000298023224D;
854 par1Entity.addVelocity(this.motionX + var2, 0.0D, this.motionZ + var4);
855 this.motionX *= 0.949999988079071D;
856 this.motionZ *= 0.949999988079071D;
857 }
858 else
859 {
860 var18 /= 2.0D;
861 var20 /= 2.0D;
862 this.motionX *= 0.20000000298023224D;
863 this.motionZ *= 0.20000000298023224D;
864 this.addVelocity(var18 - var2, 0.0D, var20 - var4);
865 par1Entity.motionX *= 0.20000000298023224D;
866 par1Entity.motionZ *= 0.20000000298023224D;
867 par1Entity.addVelocity(var18 + var2, 0.0D, var20 + var4);
868 }
869 }
870 else
871 {
872 this.addVelocity(-var2, 0.0D, -var4);
873 par1Entity.addVelocity(var2 / 4.0D, 0.0D, var4 / 4.0D);
874 }
875 }
876 }
877 }
878 }
879
880 /**
881 * Returns the number of slots in the inventory.
882 */
883 public int getSizeInventory()
884 {
885 return (minecartType == 1 && getClass() == EntityMinecart.class ? 27 : 0);
886 }
887
888 /**
889 * Returns the stack in slot i
890 */
891 public ItemStack getStackInSlot(int par1)
892 {
893 return this.cargoItems[par1];
894 }
895
896 /**
897 * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
898 * new stack.
899 */
900 public ItemStack decrStackSize(int par1, int par2)
901 {
902 if (this.cargoItems[par1] != null)
903 {
904 ItemStack var3;
905
906 if (this.cargoItems[par1].stackSize <= par2)
907 {
908 var3 = this.cargoItems[par1];
909 this.cargoItems[par1] = null;
910 return var3;
911 }
912 else
913 {
914 var3 = this.cargoItems[par1].splitStack(par2);
915
916 if (this.cargoItems[par1].stackSize == 0)
917 {
918 this.cargoItems[par1] = null;
919 }
920
921 return var3;
922 }
923 }
924 else
925 {
926 return null;
927 }
928 }
929
930 /**
931 * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
932 * like when you close a workbench GUI.
933 */
934 public ItemStack getStackInSlotOnClosing(int par1)
935 {
936 if (this.cargoItems[par1] != null)
937 {
938 ItemStack var2 = this.cargoItems[par1];
939 this.cargoItems[par1] = null;
940 return var2;
941 }
942 else
943 {
944 return null;
945 }
946 }
947
948 /**
949 * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
950 */
951 public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
952 {
953 this.cargoItems[par1] = par2ItemStack;
954
955 if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
956 {
957 par2ItemStack.stackSize = this.getInventoryStackLimit();
958 }
959 }
960
961 /**
962 * Returns the name of the inventory.
963 */
964 public String getInvName()
965 {
966 return "container.minecart";
967 }
968
969 /**
970 * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
971 * this more of a set than a get?*
972 */
973 public int getInventoryStackLimit()
974 {
975 return 64;
976 }
977
978 /**
979 * Called when an the contents of an Inventory change, usually
980 */
981 public void onInventoryChanged() {}
982
983 /**
984 * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
985 */
986 public boolean interact(EntityPlayer par1EntityPlayer)
987 {
988 if (MinecraftForge.EVENT_BUS.post(new MinecartInteractEvent(this, par1EntityPlayer)))
989 {
990 return true;
991 }
992
993 if (canBeRidden())
994 {
995 if (this.riddenByEntity != null && this.riddenByEntity instanceof EntityPlayer && this.riddenByEntity != par1EntityPlayer)
996 {
997 return true;
998 }
999
1000 if (!this.worldObj.isRemote)
1001 {
1002 par1EntityPlayer.mountEntity(this);
1003 }
1004 }
1005 else if (getSizeInventory() > 0)
1006 {
1007 if (!this.worldObj.isRemote)
1008 {
1009 par1EntityPlayer.displayGUIChest(this);
1010 }
1011 }
1012 else if (this.minecartType == 2 && getClass() == EntityMinecart.class)
1013 {
1014 ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
1015
1016 if (var2 != null && var2.itemID == Item.coal.shiftedIndex)
1017 {
1018 if (--var2.stackSize == 0)
1019 {
1020 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
1021 }
1022
1023 this.fuel += 3600;
1024 }
1025
1026 this.pushX = this.posX - par1EntityPlayer.posX;
1027 this.pushZ = this.posZ - par1EntityPlayer.posZ;
1028 }
1029
1030 return true;
1031 }
1032
1033 @SideOnly(Side.CLIENT)
1034
1035 /**
1036 * Sets the position and rotation. Only difference from the other one is no bounding on the rotation. Args: posX,
1037 * posY, posZ, yaw, pitch
1038 */
1039 public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9)
1040 {
1041 this.minecartX = par1;
1042 this.minecartY = par3;
1043 this.minecartZ = par5;
1044 this.minecartYaw = (double)par7;
1045 this.minecartPitch = (double)par8;
1046 this.turnProgress = par9 + 2;
1047 this.motionX = this.velocityX;
1048 this.motionY = this.velocityY;
1049 this.motionZ = this.velocityZ;
1050 }
1051
1052 /**
1053 * Do not make give this method the name canInteractWith because it clashes with Container
1054 */
1055 public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
1056 {
1057 return this.isDead ? false : par1EntityPlayer.getDistanceSqToEntity(this) <= 64.0D;
1058 }
1059
1060 @SideOnly(Side.CLIENT)
1061
1062 /**
1063 * Sets the velocity to the args. Args: x, y, z
1064 */
1065 public void setVelocity(double par1, double par3, double par5)
1066 {
1067 this.velocityX = this.motionX = par1;
1068 this.velocityY = this.motionY = par3;
1069 this.velocityZ = this.motionZ = par5;
1070 }
1071
1072 /**
1073 * Is this minecart powered (Fuel > 0)
1074 */
1075 protected boolean isMinecartPowered()
1076 {
1077 return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0;
1078 }
1079
1080 /**
1081 * Set if this minecart is powered (Fuel > 0)
1082 */
1083 protected void setMinecartPowered(boolean par1)
1084 {
1085 if (par1)
1086 {
1087 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(this.dataWatcher.getWatchableObjectByte(16) | 1)));
1088 }
1089 else
1090 {
1091 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(this.dataWatcher.getWatchableObjectByte(16) & -2)));
1092 }
1093 }
1094
1095 public void openChest() {}
1096
1097 public void closeChest() {}
1098
1099 /**
1100 * Sets the current amount of damage the minecart has taken. Decreases over time. The cart breaks when this is over
1101 * 40.
1102 */
1103 public void setDamage(int par1)
1104 {
1105 this.dataWatcher.updateObject(19, Integer.valueOf(par1));
1106 }
1107
1108 /**
1109 * Gets the current amount of damage the minecart has taken. Decreases over time. The cart breaks when this is over
1110 * 40.
1111 */
1112 public int getDamage()
1113 {
1114 return this.dataWatcher.getWatchableObjectInt(19);
1115 }
1116
1117 public void func_70497_h(int par1)
1118 {
1119 this.dataWatcher.updateObject(17, Integer.valueOf(par1));
1120 }
1121
1122 public int func_70496_j()
1123 {
1124 return this.dataWatcher.getWatchableObjectInt(17);
1125 }
1126
1127 public void func_70494_i(int par1)
1128 {
1129 this.dataWatcher.updateObject(18, Integer.valueOf(par1));
1130 }
1131
1132 public int func_70493_k()
1133 {
1134 return this.dataWatcher.getWatchableObjectInt(18);
1135 }
1136
1137 /**
1138 * Drops the cart as a item. The exact item dropped is defined by getItemDropped().
1139 */
1140 public void dropCartAsItem()
1141 {
1142 for(ItemStack item : getItemsDropped())
1143 {
1144 entityDropItem(item, 0);
1145 }
1146 }
1147
1148 /**
1149 * Override this to define which items your cart drops when broken.
1150 * This does not include items contained in the inventory,
1151 * that is handled elsewhere.
1152 * @return A list of items dropped.
1153 */
1154 public List<ItemStack> getItemsDropped()
1155 {
1156 List<ItemStack> items = new ArrayList<ItemStack>();
1157 items.add(new ItemStack(Item.minecartEmpty));
1158
1159 switch(minecartType)
1160 {
1161 case 1:
1162 items.add(new ItemStack(Block.chest));
1163 break;
1164 case 2:
1165 items.add(new ItemStack(Block.stoneOvenIdle));
1166 break;
1167 }
1168 return items;
1169 }
1170
1171 /**
1172 * This function returns an ItemStack that represents this cart.
1173 * This should be an ItemStack that can be used by the player to place the cart.
1174 * This is the item that was registered with the cart via the registerMinecart function,
1175 * but is not necessary the item the cart drops when destroyed.
1176 * @return An ItemStack that can be used to place the cart.
1177 */
1178 public ItemStack getCartItem()
1179 {
1180 return MinecartRegistry.getItemForCart(this);
1181 }
1182
1183 /**
1184 * Returns true if this cart is self propelled.
1185 * @return True if powered.
1186 */
1187 public boolean isPoweredCart()
1188 {
1189 return minecartType == 2 && getClass() == EntityMinecart.class;
1190 }
1191
1192 /**
1193 * Returns true if this cart is a storage cart
1194 * Some carts may have inventories but not be storage carts
1195 * and some carts without inventories may be storage carts.
1196 * @return True if this cart should be classified as a storage cart.
1197 */
1198 public boolean isStorageCart()
1199 {
1200 return minecartType == 1 && getClass() == EntityMinecart.class;
1201 }
1202
1203 /**
1204 * Returns true if this cart can be ridden by an Entity.
1205 * @return True if this cart can be ridden.
1206 */
1207 public boolean canBeRidden()
1208 {
1209 if(minecartType == 0 && getClass() == EntityMinecart.class)
1210 {
1211 return true;
1212 }
1213 return false;
1214 }
1215
1216 /**
1217 * Returns true if this cart can currently use rails.
1218 * This function is mainly used to gracefully detach a minecart from a rail.
1219 * @return True if the minecart can use rails.
1220 */
1221 public boolean canUseRail()
1222 {
1223 return canUseRail;
1224 }
1225
1226 /**
1227 * Set whether the minecart can use rails.
1228 * This function is mainly used to gracefully detach a minecart from a rail.
1229 * @param use Whether the minecart can currently use rails.
1230 */
1231 public void setCanUseRail(boolean use)
1232 {
1233 canUseRail = use;
1234 }
1235
1236 /**
1237 * Return false if this cart should not call IRail.onMinecartPass() and should ignore Powered Rails.
1238 * @return True if this cart should call IRail.onMinecartPass().
1239 */
1240 public boolean shouldDoRailFunctions()
1241 {
1242 return true;
1243 }
1244
1245 /**
1246 * Simply returns the minecartType variable.
1247 * @return minecartType
1248 */
1249 public int getMinecartType()
1250 {
1251 return minecartType;
1252 }
1253
1254 /**
1255 * Gets the current global Minecart Collision handler if none
1256 * is registered, returns null
1257 * @return The collision handler or null
1258 */
1259 public static IMinecartCollisionHandler getCollisionHandler()
1260 {
1261 return collisionHandler;
1262 }
1263
1264 /**
1265 * Sets the global Minecart Collision handler, overwrites any
1266 * that is currently set.
1267 * @param handler The new handler
1268 */
1269 public static void setCollisionHandler(IMinecartCollisionHandler handler)
1270 {
1271 collisionHandler = handler;
1272 }
1273
1274 /**
1275 * Carts should return their drag factor here
1276 * @return The drag rate.
1277 */
1278 protected double getDrag()
1279 {
1280 return riddenByEntity != null ? defaultDragRidden : defaultDragEmpty;
1281 }
1282
1283 /**
1284 * Moved to allow overrides.
1285 * This code applies drag and updates push forces.
1286 */
1287 protected void applyDragAndPushForces()
1288 {
1289 if(isPoweredCart())
1290 {
1291 double d27 = MathHelper.sqrt_double(pushX * pushX + pushZ * pushZ);
1292 if(d27 > 0.01D)
1293 {
1294 pushX /= d27;
1295 pushZ /= d27;
1296 double d29 = 0.04;
1297 motionX *= 0.8D;
1298 motionY *= 0.0D;
1299 motionZ *= 0.8D;
1300 motionX += pushX * d29;
1301 motionZ += pushZ * d29;
1302 }
1303 else
1304 {
1305 motionX *= 0.9D;
1306 motionY *= 0.0D;
1307 motionZ *= 0.9D;
1308 }
1309 }
1310 motionX *= getDrag();
1311 motionY *= 0.0D;
1312 motionZ *= getDrag();
1313 }
1314
1315 /**
1316 * Moved to allow overrides.
1317 * This code updates push forces.
1318 */
1319 protected void updatePushForces()
1320 {
1321 if(isPoweredCart())
1322 {
1323 double push = MathHelper.sqrt_double(pushX * pushX + pushZ * pushZ);
1324 if(push > 0.01D && motionX * motionX + motionZ * motionZ > 0.001D)
1325 {
1326 pushX /= push;
1327 pushZ /= push;
1328 if(pushX * motionX + pushZ * motionZ < 0.0D)
1329 {
1330 pushX = 0.0D;
1331 pushZ = 0.0D;
1332 }
1333 else
1334 {
1335 pushX = motionX;
1336 pushZ = motionZ;
1337 }
1338 }
1339 }
1340 }
1341
1342 /**
1343 * Moved to allow overrides.
1344 * This code handles minecart movement and speed capping when on a rail.
1345 */
1346 protected void moveMinecartOnRail(int i, int j, int k)
1347 {
1348 int id = worldObj.getBlockId(i, j, k);
1349 if (!BlockRail.isRailBlock(id))
1350 {
1351 return;
1352 }
1353 float railMaxSpeed = ((BlockRail)Block.blocksList[id]).getRailMaxSpeed(worldObj, this, i, j, k);
1354
1355 double maxSpeed = Math.min(railMaxSpeed, getMaxSpeedRail());
1356 double mX = motionX;
1357 double mZ = motionZ;
1358 if(riddenByEntity != null)
1359 {
1360 mX *= 0.75D;
1361 mZ *= 0.75D;
1362 }
1363 if(mX < -maxSpeed) mX = -maxSpeed;
1364 if(mX > maxSpeed) mX = maxSpeed;
1365 if(mZ < -maxSpeed) mZ = -maxSpeed;
1366 if(mZ > maxSpeed) mZ = maxSpeed;
1367 moveEntity(mX, 0.0D, mZ);
1368 }
1369
1370 /**
1371 * Moved to allow overrides.
1372 * This code handles minecart movement and speed capping when not on a rail.
1373 */
1374 protected void moveMinecartOffRail(int i, int j, int k)
1375 {
1376 double d2 = getMaxSpeedGround();
1377 if(!onGround)
1378 {
1379 d2 = getMaxSpeedAirLateral();
1380 }
1381 if(motionX < -d2) motionX = -d2;
1382 if(motionX > d2) motionX = d2;
1383 if(motionZ < -d2) motionZ = -d2;
1384 if(motionZ > d2) motionZ = d2;
1385 double moveY = motionY;
1386 if(getMaxSpeedAirVertical() > 0 && motionY > getMaxSpeedAirVertical())
1387 {
1388 moveY = getMaxSpeedAirVertical();
1389 if(Math.abs(motionX) < 0.3f && Math.abs(motionZ) < 0.3f)
1390 {
1391 moveY = 0.15f;
1392 motionY = moveY;
1393 }
1394 }
1395 if(onGround)
1396 {
1397 motionX *= 0.5D;
1398 motionY *= 0.5D;
1399 motionZ *= 0.5D;
1400 }
1401 moveEntity(motionX, moveY, motionZ);
1402 if(!onGround)
1403 {
1404 motionX *= getDragAir();
1405 motionY *= getDragAir();
1406 motionZ *= getDragAir();
1407 }
1408 }
1409
1410 /**
1411 * Moved to allow overrides.
1412 * This code applies fuel consumption.
1413 */
1414 protected void updateFuel()
1415 {
1416 if (fuel > 0) fuel--;
1417 if (fuel <= 0) pushX = pushZ = 0.0D;
1418 setMinecartPowered(fuel > 0);
1419 }
1420
1421 /**
1422 * Moved to allow overrides, This code handle slopes affecting velocity.
1423 * @param metadata The blocks position metadata
1424 */
1425 protected void adjustSlopeVelocities(int metadata)
1426 {
1427 double acceleration = 0.0078125D;
1428 if (metadata == 2)
1429 {
1430 motionX -= acceleration;
1431 }
1432 else if (metadata == 3)
1433 {
1434 motionX += acceleration;
1435 }
1436 else if (metadata == 4)
1437 {
1438 motionZ += acceleration;
1439 }
1440 else if (metadata == 5)
1441 {
1442 motionZ -= acceleration;
1443 }
1444 }
1445
1446 /**
1447 * Getters/setters for physics variables
1448 */
1449
1450 /**
1451 * Returns the carts max speed.
1452 * Carts going faster than 1.1 cause issues with chunk loading.
1453 * Carts cant traverse slopes or corners at greater than 0.5 - 0.6.
1454 * This value is compared with the rails max speed to determine
1455 * the carts current max speed. A normal rails max speed is 0.4.
1456 * @return Carts max speed.
1457 */
1458 public float getMaxSpeedRail()
1459 {
1460 return maxSpeedRail;
1461 }
1462
1463 public void setMaxSpeedRail(float value)
1464 {
1465 maxSpeedRail = value;
1466 }
1467
1468 public float getMaxSpeedGround()
1469 {
1470 return maxSpeedGround;
1471 }
1472
1473 public void setMaxSpeedGround(float value)
1474 {
1475 maxSpeedGround = value;
1476 }
1477
1478 public float getMaxSpeedAirLateral()
1479 {
1480 return maxSpeedAirLateral;
1481 }
1482
1483 public void setMaxSpeedAirLateral(float value)
1484 {
1485 maxSpeedAirLateral = value;
1486 }
1487
1488 public float getMaxSpeedAirVertical()
1489 {
1490 return maxSpeedAirVertical;
1491 }
1492
1493 public void setMaxSpeedAirVertical(float value)
1494 {
1495 maxSpeedAirVertical = value;
1496 }
1497
1498 public double getDragAir()
1499 {
1500 return dragAir;
1501 }
1502
1503 public void setDragAir(double value)
1504 {
1505 dragAir = value;
1506 }
1507 }