Advertisement
Morok

EntityPlayerSP

May 19th, 2013
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.39 KB | None | 0 0
  1. package net.minecraft.client.entity;
  2.  
  3. import cpw.mods.fml.relauncher.Side;
  4. import cpw.mods.fml.relauncher.SideOnly;
  5. import net.minecraft.client.Minecraft;
  6. import net.minecraft.client.gui.GuiCommandBlock;
  7. import net.minecraft.client.gui.GuiEnchantment;
  8. import net.minecraft.client.gui.GuiHopper;
  9. import net.minecraft.client.gui.GuiMerchant;
  10. import net.minecraft.client.gui.GuiRepair;
  11. import net.minecraft.client.gui.GuiScreen;
  12. import net.minecraft.client.gui.GuiScreenBook;
  13. import net.minecraft.client.gui.inventory.GuiBeacon;
  14. import net.minecraft.client.gui.inventory.GuiBrewingStand;
  15. import net.minecraft.client.gui.inventory.GuiChest;
  16. import net.minecraft.client.gui.inventory.GuiCrafting;
  17. import net.minecraft.client.gui.inventory.GuiDispenser;
  18. import net.minecraft.client.gui.inventory.GuiEditSign;
  19. import net.minecraft.client.gui.inventory.GuiFurnace;
  20. import net.minecraft.client.particle.EntityCrit2FX;
  21. import net.minecraft.client.particle.EntityPickupFX;
  22. import net.minecraft.entity.Entity;
  23. import net.minecraft.entity.IMerchant;
  24. import net.minecraft.entity.item.EntityMinecartHopper;
  25. import net.minecraft.entity.player.EntityPlayer;
  26. import net.minecraft.inventory.IInventory;
  27. import net.minecraft.item.Item;
  28. import net.minecraft.item.ItemStack;
  29. import net.minecraft.potion.Potion;
  30. import net.minecraft.stats.Achievement;
  31. import net.minecraft.stats.AchievementList;
  32. import net.minecraft.stats.StatBase;
  33. import net.minecraft.tileentity.TileEntity;
  34. import net.minecraft.tileentity.TileEntityBeacon;
  35. import net.minecraft.tileentity.TileEntityBrewingStand;
  36. import net.minecraft.tileentity.TileEntityCommandBlock;
  37. import net.minecraft.tileentity.TileEntityDispenser;
  38. import net.minecraft.tileentity.TileEntityFurnace;
  39. import net.minecraft.tileentity.TileEntityHopper;
  40. import net.minecraft.tileentity.TileEntitySign;
  41. import net.minecraft.util.ChunkCoordinates;
  42. import net.minecraft.util.DamageSource;
  43. import net.minecraft.util.MathHelper;
  44. import net.minecraft.util.MouseFilter;
  45. import net.minecraft.util.MovementInput;
  46. import net.minecraft.util.Session;
  47. import net.minecraft.util.StringUtils;
  48. import net.minecraft.world.World;
  49.  
  50. @SideOnly(Side.CLIENT)
  51. public class EntityPlayerSP extends EntityPlayer
  52. {
  53. public MovementInput movementInput;
  54. protected Minecraft mc;
  55.  
  56. /**
  57. * Used to tell if the player pressed forward twice. If this is at 0 and it's pressed (And they are allowed to
  58. * sprint, aka enough food on the ground etc) it sets this to 7. If it's pressed and it's greater than 0 enable
  59. * sprinting.
  60. */
  61. protected int sprintToggleTimer = 0;
  62.  
  63. /** Ticks left before sprinting is disabled. */
  64. public int sprintingTicksLeft = 0;
  65. public float renderArmYaw;
  66. public float renderArmPitch;
  67. public float prevRenderArmYaw;
  68. public float prevRenderArmPitch;
  69. private MouseFilter field_71162_ch = new MouseFilter();
  70. private MouseFilter field_71160_ci = new MouseFilter();
  71. private MouseFilter field_71161_cj = new MouseFilter();
  72.  
  73. /** The amount of time an entity has been in a Portal */
  74. public float timeInPortal;
  75.  
  76. /** The amount of time an entity has been in a Portal the previous tick */
  77. public float prevTimeInPortal;
  78.  
  79. public EntityPlayerSP(Minecraft par1Minecraft, World par2World, Session par3Session, int par4)
  80. {
  81. super(par2World);
  82. this.mc = par1Minecraft;
  83. this.dimension = par4;
  84.  
  85. if (par3Session != null && par3Session.username != null && par3Session.username.length() > 0)
  86. {
  87. this.skinUrl = "http://skins.minecraft.net/MinecraftSkins/" + StringUtils.stripControlCodes(par3Session.username) + ".png";
  88. }
  89.  
  90. this.username = par3Session.username;
  91. }
  92.  
  93. /**
  94. * Tries to moves the entity by the passed in displacement. Args: x, y, z
  95. */
  96. public void moveEntity(double par1, double par3, double par5)
  97. {
  98. super.moveEntity(par1, par3, par5);
  99. }
  100.  
  101. public void updateEntityActionState()
  102. {
  103. super.updateEntityActionState();
  104. this.moveStrafing = this.movementInput.moveStrafe;
  105. this.moveForward = this.movementInput.moveForward;
  106. this.isJumping = this.movementInput.jump;
  107. this.prevRenderArmYaw = this.renderArmYaw;
  108. this.prevRenderArmPitch = this.renderArmPitch;
  109. this.renderArmPitch = (float)((double)this.renderArmPitch + (double)(this.rotationPitch - this.renderArmPitch) * 0.5D);
  110. this.renderArmYaw = (float)((double)this.renderArmYaw + (double)(this.rotationYaw - this.renderArmYaw) * 0.5D);
  111. }
  112.  
  113. /**
  114. * Returns whether the entity is in a local (client) world
  115. */
  116. protected boolean isClientWorld()
  117. {
  118. return true;
  119. }
  120.  
  121. /**
  122. * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
  123. * use this to react to sunlight and start to burn.
  124. */
  125. public void onLivingUpdate()
  126. {
  127. if (this.sprintingTicksLeft > 0)
  128. {
  129. --this.sprintingTicksLeft;
  130.  
  131. if (this.sprintingTicksLeft == 0)
  132. {
  133. this.setSprinting(false);
  134. }
  135. }
  136.  
  137. if (this.sprintToggleTimer > 0)
  138. {
  139. --this.sprintToggleTimer;
  140. }
  141.  
  142. if (this.mc.playerController.enableEverythingIsScrewedUpMode())
  143. {
  144. this.posX = this.posZ = 0.5D;
  145. this.posX = 0.0D;
  146. this.posZ = 0.0D;
  147. this.rotationYaw = (float)this.ticksExisted / 12.0F;
  148. this.rotationPitch = 10.0F;
  149. this.posY = 68.5D;
  150. }
  151. else
  152. {
  153. if (!this.mc.statFileWriter.hasAchievementUnlocked(AchievementList.openInventory))
  154. {
  155. this.mc.guiAchievement.queueAchievementInformation(AchievementList.openInventory);
  156. }
  157.  
  158. this.prevTimeInPortal = this.timeInPortal;
  159.  
  160. if (this.inPortal)
  161. {
  162. if (this.mc.currentScreen != null)
  163. {
  164. this.mc.displayGuiScreen((GuiScreen)null);
  165. }
  166.  
  167. if (this.timeInPortal == 0.0F)
  168. {
  169. this.mc.sndManager.playSoundFX("portal.trigger", 1.0F, this.rand.nextFloat() * 0.4F + 0.8F);
  170. }
  171.  
  172. this.timeInPortal += 0.0125F;
  173.  
  174. if (this.timeInPortal >= 1.0F)
  175. {
  176. this.timeInPortal = 1.0F;
  177. }
  178.  
  179. this.inPortal = false;
  180. }
  181. else if (this.isPotionActive(Potion.confusion) && this.getActivePotionEffect(Potion.confusion).getDuration() > 60)
  182. {
  183. this.timeInPortal += 0.006666667F;
  184.  
  185. if (this.timeInPortal > 1.0F)
  186. {
  187. this.timeInPortal = 1.0F;
  188. }
  189. }
  190. else
  191. {
  192. if (this.timeInPortal > 0.0F)
  193. {
  194. this.timeInPortal -= 0.05F;
  195. }
  196.  
  197. if (this.timeInPortal < 0.0F)
  198. {
  199. this.timeInPortal = 0.0F;
  200. }
  201. }
  202.  
  203. if (this.timeUntilPortal > 0)
  204. {
  205. --this.timeUntilPortal;
  206. }
  207.  
  208. boolean flag = this.movementInput.jump;
  209. float f = 0.8F;
  210. boolean flag1 = this.movementInput.moveForward >= f;
  211. this.movementInput.updatePlayerMoveState();
  212.  
  213. if (this.isUsingItem())
  214. {
  215. this.movementInput.moveStrafe *= 0.2F;
  216. this.movementInput.moveForward *= 0.2F;
  217. this.sprintToggleTimer = 0;
  218. }
  219.  
  220. if (this.movementInput.sneak && this.ySize < 0.2F)
  221. {
  222. this.ySize = 0.2F;
  223. }
  224.  
  225. this.pushOutOfBlocks(this.posX - (double)this.width * 0.35D, this.boundingBox.minY + 0.5D, this.posZ + (double)this.width * 0.35D);
  226. this.pushOutOfBlocks(this.posX - (double)this.width * 0.35D, this.boundingBox.minY + 0.5D, this.posZ - (double)this.width * 0.35D);
  227. this.pushOutOfBlocks(this.posX + (double)this.width * 0.35D, this.boundingBox.minY + 0.5D, this.posZ - (double)this.width * 0.35D);
  228. this.pushOutOfBlocks(this.posX + (double)this.width * 0.35D, this.boundingBox.minY + 0.5D, this.posZ + (double)this.width * 0.35D);
  229. boolean flag2 = (float)this.getFoodStats().getFoodLevel() > 6.0F || this.capabilities.allowFlying;
  230.  
  231. if (this.onGround && !flag1 && this.movementInput.moveForward >= f && !this.isSprinting() && flag2 && !this.isUsingItem() && !this.isPotionActive(Potion.blindness))
  232. {
  233. if (this.sprintToggleTimer == 0)
  234. {
  235. this.sprintToggleTimer = 7;
  236. }
  237. else
  238. {
  239. this.setSprinting(true);
  240. this.sprintToggleTimer = 0;
  241. }
  242. }
  243.  
  244. if (this.isSneaking())
  245. {
  246. this.sprintToggleTimer = 0;
  247. }
  248.  
  249. if (this.isSprinting() && (this.movementInput.moveForward < f || this.isCollidedHorizontally || !flag2))
  250. {
  251. this.setSprinting(false);
  252. }
  253.  
  254. if (this.capabilities.allowFlying && !flag && this.movementInput.jump)
  255. {
  256. if (this.flyToggleTimer == 0)
  257. {
  258. this.flyToggleTimer = 7;
  259. }
  260. else
  261. {
  262. this.capabilities.isFlying = !this.capabilities.isFlying;
  263. this.sendPlayerAbilities();
  264. this.flyToggleTimer = 0;
  265. }
  266. }
  267.  
  268. if (this.capabilities.isFlying)
  269. {
  270. if (this.movementInput.sneak)
  271. {
  272. this.motionY -= 0.15D;
  273. }
  274.  
  275. if (this.movementInput.jump)
  276. {
  277. this.motionY += 0.15D;
  278. }
  279. }
  280.  
  281. super.onLivingUpdate();
  282.  
  283. if (this.onGround && this.capabilities.isFlying)
  284. {
  285. this.capabilities.isFlying = false;
  286. this.sendPlayerAbilities();
  287. }
  288. }
  289. }
  290.  
  291. /**
  292. * Gets the player's field of view multiplier. (ex. when flying)
  293. */
  294. public float getFOVMultiplier()
  295. {
  296. float f = 1.0F;
  297.  
  298. if (this.capabilities.isFlying)
  299. {
  300. f *= 1.1F;
  301. }
  302.  
  303. f *= (this.landMovementFactor * this.getSpeedModifier() / this.speedOnGround + 1.0F) / 2.0F;
  304.  
  305. if (this.isUsingItem() && this.getItemInUse().itemID == Item.bow.itemID)
  306. {
  307. int i = this.getItemInUseDuration();
  308. float f1 = (float)i / 20.0F;
  309.  
  310. if (f1 > 1.0F)
  311. {
  312. f1 = 1.0F;
  313. }
  314. else
  315. {
  316. f1 *= f1;
  317. }
  318.  
  319. f *= 1.0F - f1 * 0.15F;
  320. }
  321.  
  322. return f;
  323. }
  324.  
  325. public void updateCloak()
  326. {
  327. this.cloakUrl = "http://skins.minecraft.net/MinecraftCloaks/" + StringUtils.stripControlCodes(this.username) + ".png";
  328. }
  329.  
  330. /**
  331. * sets current screen to null (used on escape buttons of GUIs)
  332. */
  333. public void closeScreen()
  334. {
  335. super.closeScreen();
  336. this.mc.displayGuiScreen((GuiScreen)null);
  337. }
  338.  
  339. /**
  340. * Displays the GUI for editing a sign. Args: tileEntitySign
  341. */
  342. public void displayGUIEditSign(TileEntity par1TileEntity)
  343. {
  344. if (par1TileEntity instanceof TileEntitySign)
  345. {
  346. this.mc.displayGuiScreen(new GuiEditSign((TileEntitySign)par1TileEntity));
  347. }
  348. else if (par1TileEntity instanceof TileEntityCommandBlock)
  349. {
  350. this.mc.displayGuiScreen(new GuiCommandBlock((TileEntityCommandBlock)par1TileEntity));
  351. }
  352. }
  353.  
  354. /**
  355. * Displays the GUI for interacting with a book.
  356. */
  357. public void displayGUIBook(ItemStack par1ItemStack)
  358. {
  359. Item item = par1ItemStack.getItem();
  360.  
  361. if (item == Item.writtenBook)
  362. {
  363. this.mc.displayGuiScreen(new GuiScreenBook(this, par1ItemStack, false));
  364. }
  365. else if (item == Item.writableBook)
  366. {
  367. this.mc.displayGuiScreen(new GuiScreenBook(this, par1ItemStack, true));
  368. }
  369. }
  370.  
  371. /**
  372. * Displays the GUI for interacting with a chest inventory. Args: chestInventory
  373. */
  374. public void displayGUIChest(IInventory par1IInventory)
  375. {
  376. this.mc.displayGuiScreen(new GuiChest(this.inventory, par1IInventory));
  377. }
  378.  
  379. public void func_94064_a(TileEntityHopper par1TileEntityHopper)
  380. {
  381. this.mc.displayGuiScreen(new GuiHopper(this.inventory, par1TileEntityHopper));
  382. }
  383.  
  384. public void func_96125_a(EntityMinecartHopper par1EntityMinecartHopper)
  385. {
  386. this.mc.displayGuiScreen(new GuiHopper(this.inventory, par1EntityMinecartHopper));
  387. }
  388.  
  389. /**
  390. * Displays the crafting GUI for a workbench.
  391. */
  392. public void displayGUIWorkbench(int par1, int par2, int par3)
  393. {
  394. this.mc.displayGuiScreen(new GuiCrafting(this.inventory, this.worldObj, par1, par2, par3));
  395. }
  396.  
  397. public void displayGUIEnchantment(int par1, int par2, int par3, String par4Str)
  398. {
  399. this.mc.displayGuiScreen(new GuiEnchantment(this.inventory, this.worldObj, par1, par2, par3, par4Str));
  400. }
  401.  
  402. /**
  403. * Displays the GUI for interacting with an anvil.
  404. */
  405. public void displayGUIAnvil(int par1, int par2, int par3)
  406. {
  407. this.mc.displayGuiScreen(new GuiRepair(this.inventory, this.worldObj, par1, par2, par3));
  408. }
  409.  
  410. /**
  411. * Displays the furnace GUI for the passed in furnace entity. Args: tileEntityFurnace
  412. */
  413. public void displayGUIFurnace(TileEntityFurnace par1TileEntityFurnace)
  414. {
  415. this.mc.displayGuiScreen(new GuiFurnace(this.inventory, par1TileEntityFurnace));
  416. }
  417.  
  418. /**
  419. * Displays the GUI for interacting with a brewing stand.
  420. */
  421. public void displayGUIBrewingStand(TileEntityBrewingStand par1TileEntityBrewingStand)
  422. {
  423. this.mc.displayGuiScreen(new GuiBrewingStand(this.inventory, par1TileEntityBrewingStand));
  424. }
  425.  
  426. /**
  427. * Displays the GUI for interacting with a beacon.
  428. */
  429. public void displayGUIBeacon(TileEntityBeacon par1TileEntityBeacon)
  430. {
  431. this.mc.displayGuiScreen(new GuiBeacon(this.inventory, par1TileEntityBeacon));
  432. }
  433.  
  434. /**
  435. * Displays the dipsenser GUI for the passed in dispenser entity. Args: TileEntityDispenser
  436. */
  437. public void displayGUIDispenser(TileEntityDispenser par1TileEntityDispenser)
  438. {
  439. this.mc.displayGuiScreen(new GuiDispenser(this.inventory, par1TileEntityDispenser));
  440. }
  441.  
  442. public void displayGUIMerchant(IMerchant par1IMerchant, String par2Str)
  443. {
  444. this.mc.displayGuiScreen(new GuiMerchant(this.inventory, par1IMerchant, this.worldObj, par2Str));
  445. }
  446.  
  447. /**
  448. * Called when the player performs a critical hit on the Entity. Args: entity that was hit critically
  449. */
  450. public void onCriticalHit(Entity par1Entity)
  451. {
  452. this.mc.effectRenderer.addEffect(new EntityCrit2FX(this.mc.theWorld, par1Entity));
  453. }
  454.  
  455. public void onEnchantmentCritical(Entity par1Entity)
  456. {
  457. EntityCrit2FX entitycrit2fx = new EntityCrit2FX(this.mc.theWorld, par1Entity, "magicCrit");
  458. this.mc.effectRenderer.addEffect(entitycrit2fx);
  459. }
  460.  
  461. /**
  462. * Called whenever an item is picked up from walking over it. Args: pickedUpEntity, stackSize
  463. */
  464. public void onItemPickup(Entity par1Entity, int par2)
  465. {
  466. this.mc.effectRenderer.addEffect(new EntityPickupFX(this.mc.theWorld, par1Entity, this, -0.5F));
  467. }
  468.  
  469. /**
  470. * Returns if this entity is sneaking.
  471. */
  472. public boolean isSneaking()
  473. {
  474. return this.movementInput.sneak && !this.sleeping;
  475. }
  476.  
  477. /**
  478. * Updates health locally.
  479. */
  480. public void setHealth(int par1)
  481. {
  482. int j = this.getHealth() - par1;
  483.  
  484. if (j <= 0)
  485. {
  486. this.setEntityHealth(par1);
  487.  
  488. if (j < 0)
  489. {
  490. this.hurtResistantTime = this.maxHurtResistantTime / 2;
  491. }
  492. }
  493. else
  494. {
  495. this.lastDamage = j;
  496. this.setEntityHealth(this.getHealth());
  497. this.hurtResistantTime = this.maxHurtResistantTime;
  498. this.damageEntity(DamageSource.generic, j);
  499. this.hurtTime = this.maxHurtTime = 10;
  500. }
  501. }
  502.  
  503. /**
  504. * Add a chat message to the player
  505. */
  506. public void addChatMessage(String par1Str)
  507. {
  508. this.mc.ingameGUI.getChatGUI().addTranslatedMessage(par1Str, new Object[0]);
  509. }
  510.  
  511. /**
  512. * Adds a value to a statistic field.
  513. */
  514. public void addStat(StatBase par1StatBase, int par2)
  515. {
  516. if (par1StatBase != null)
  517. {
  518. if (par1StatBase.isAchievement())
  519. {
  520. Achievement achievement = (Achievement)par1StatBase;
  521.  
  522. if (achievement.parentAchievement == null || this.mc.statFileWriter.hasAchievementUnlocked(achievement.parentAchievement))
  523. {
  524. if (!this.mc.statFileWriter.hasAchievementUnlocked(achievement))
  525. {
  526. this.mc.guiAchievement.queueTakenAchievement(achievement);
  527. }
  528.  
  529. this.mc.statFileWriter.readStat(par1StatBase, par2);
  530. }
  531. }
  532. else
  533. {
  534. this.mc.statFileWriter.readStat(par1StatBase, par2);
  535. }
  536. }
  537. }
  538.  
  539. private boolean isBlockTranslucent(int par1, int par2, int par3)
  540. {
  541. return this.worldObj.isBlockNormalCube(par1, par2, par3);
  542. }
  543.  
  544. /**
  545. * Adds velocity to push the entity out of blocks at the specified x, y, z position Args: x, y, z
  546. */
  547. protected boolean pushOutOfBlocks(double par1, double par3, double par5)
  548. {
  549. int i = MathHelper.floor_double(par1);
  550. int j = MathHelper.floor_double(par3);
  551. int k = MathHelper.floor_double(par5);
  552. double d3 = par1 - (double)i;
  553. double d4 = par5 - (double)k;
  554.  
  555. if (this.isBlockTranslucent(i, j, k) || this.isBlockTranslucent(i, j + 1, k))
  556. {
  557. boolean flag = !this.isBlockTranslucent(i - 1, j, k) && !this.isBlockTranslucent(i - 1, j + 1, k);
  558. boolean flag1 = !this.isBlockTranslucent(i + 1, j, k) && !this.isBlockTranslucent(i + 1, j + 1, k);
  559. boolean flag2 = !this.isBlockTranslucent(i, j, k - 1) && !this.isBlockTranslucent(i, j + 1, k - 1);
  560. boolean flag3 = !this.isBlockTranslucent(i, j, k + 1) && !this.isBlockTranslucent(i, j + 1, k + 1);
  561. byte b0 = -1;
  562. double d5 = 9999.0D;
  563.  
  564. if (flag && d3 < d5)
  565. {
  566. d5 = d3;
  567. b0 = 0;
  568. }
  569.  
  570. if (flag1 && 1.0D - d3 < d5)
  571. {
  572. d5 = 1.0D - d3;
  573. b0 = 1;
  574. }
  575.  
  576. if (flag2 && d4 < d5)
  577. {
  578. d5 = d4;
  579. b0 = 4;
  580. }
  581.  
  582. if (flag3 && 1.0D - d4 < d5)
  583. {
  584. d5 = 1.0D - d4;
  585. b0 = 5;
  586. }
  587.  
  588. float f = 0.1F;
  589.  
  590. if (b0 == 0)
  591. {
  592. this.motionX = (double)(-f);
  593. }
  594.  
  595. if (b0 == 1)
  596. {
  597. this.motionX = (double)f;
  598. }
  599.  
  600. if (b0 == 4)
  601. {
  602. this.motionZ = (double)(-f);
  603. }
  604.  
  605. if (b0 == 5)
  606. {
  607. this.motionZ = (double)f;
  608. }
  609. }
  610.  
  611. return false;
  612. }
  613.  
  614. /**
  615. * Set sprinting switch for Entity.
  616. */
  617. public void setSprinting(boolean par1)
  618. {
  619. super.setSprinting(par1);
  620. this.sprintingTicksLeft = par1 ? 600 : 0;
  621. }
  622.  
  623. /**
  624. * Sets the current XP, total XP, and level number.
  625. */
  626. public void setXPStats(float par1, int par2, int par3)
  627. {
  628. this.experience = par1;
  629. this.experienceTotal = par2;
  630. this.experienceLevel = par3;
  631. }
  632.  
  633. public void sendChatToPlayer(String par1Str)
  634. {
  635. this.mc.ingameGUI.getChatGUI().printChatMessage(par1Str);
  636. }
  637.  
  638. /**
  639. * Returns true if the command sender is allowed to use the given command.
  640. */
  641. public boolean canCommandSenderUseCommand(int par1, String par2Str)
  642. {
  643. return par1 <= 0;
  644. }
  645.  
  646. /**
  647. * Return the position for this command sender.
  648. */
  649. public ChunkCoordinates getPlayerCoordinates()
  650. {
  651. return new ChunkCoordinates(MathHelper.floor_double(this.posX + 0.5D), MathHelper.floor_double(this.posY + 0.5D), MathHelper.floor_double(this.posZ + 0.5D));
  652. }
  653.  
  654. /**
  655. * Returns the item that this EntityLiving is holding, if any.
  656. */
  657. public ItemStack getHeldItem()
  658. {
  659. return this.inventory.getCurrentItem();
  660. }
  661.  
  662. public void playSound(String par1Str, float par2, float par3)
  663. {
  664. this.worldObj.playSound(this.posX, this.posY - (double)this.yOffset, this.posZ, par1Str, par2, par3, false);
  665. }
  666. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement