the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1// 4J TODO
2
3// All the instanceof s from Java have been converted to dynamic_cast in this file
4// Once all the classes are finished it may be that we do not need to use dynamic_cast
5// for every test and a simple virtual function should suffice. We probably only need
6// dynamic_cast to find one of the classes that an object derives from, and not to find
7// the derived class itself (which should own the virtual GetType function)
8
9#include "stdafx.h"
10#include "JavaMath.h"
11#include "net.minecraft.h"
12#include "net.minecraft.world.h"
13#include "net.minecraft.stats.h"
14#include "net.minecraft.world.level.h"
15#include "net.minecraft.world.level.chunk.h"
16#include "net.minecraft.world.phys.h"
17#include "net.minecraft.world.entity.h"
18#include "net.minecraft.world.entity.ai.attributes.h"
19#include "net.minecraft.world.entity.animal.h"
20#include "net.minecraft.world.entity.boss.h"
21#include "net.minecraft.world.entity.monster.h"
22#include "net.minecraft.world.entity.item.h"
23#include "net.minecraft.world.item.h"
24#include "net.minecraft.world.item.enchantment.h"
25#include "net.minecraft.world.level.dimension.h"
26#include "net.minecraft.world.level.material.h"
27#include "net.minecraft.world.level.tile.h"
28#include "net.minecraft.world.level.tile.entity.h"
29#include "net.minecraft.world.scores.h"
30#include "net.minecraft.world.scores.criteria.h"
31#include "net.minecraft.world.entity.projectile.h"
32#include "net.minecraft.world.inventory.h"
33#include "net.minecraft.world.damagesource.h"
34#include "net.minecraft.world.effect.h"
35#include "net.minecraft.world.food.h"
36#include "Inventory.h"
37#include "Player.h"
38#include "ParticleTypes.h"
39
40#include "..\Minecraft.Client\Textures.h"
41
42#include "..\Minecraft.Client\LocalPlayer.h"
43#include "..\Minecraft.Client\HumanoidModel.h"
44#include "SoundTypes.h"
45
46
47
48void Player::_init()
49{
50 registerAttributes();
51 setHealth(getMaxHealth());
52
53 inventory = shared_ptr<Inventory>( new Inventory( this ) );
54
55 userType = 0;
56 oBob = bob = 0.0f;
57
58 xCloakO = yCloakO = zCloakO = 0.0;
59 xCloak = yCloak = zCloak = 0.0;
60
61 m_isSleeping = false;
62
63 customTextureUrl = L"";
64 customTextureUrl2 = L"";
65 m_uiPlayerCurrentSkin=0;
66
67 bedPosition = NULL;
68
69 sleepCounter = 0;
70 deathFadeCounter=0;
71
72 bedOffsetX = bedOffsetY = bedOffsetZ = 0.0f;
73 stats = NULL;
74
75 respawnPosition = NULL;
76 respawnForced = false;
77 minecartAchievementPos = NULL;
78
79 fishing = nullptr;
80
81 distanceWalk = distanceSwim = distanceFall = distanceClimb = distanceMinecart = distanceBoat = distancePig = 0;
82
83 m_uiDebugOptions=0L;
84
85 jumpTriggerTime = 0;
86 takeXpDelay = 0;
87 experienceLevel = totalExperience = 0;
88 experienceProgress = 0.0f;
89
90 useItem = nullptr;
91 useItemDuration = 0;
92
93 defaultWalkSpeed = 0.1f;
94 defaultFlySpeed = 0.02f;
95
96 lastLevelUpTime = 0;
97
98 m_uiGamePrivileges = 0;
99
100 m_ppAdditionalModelParts=NULL;
101 m_bCheckedForModelParts=false;
102 m_bCheckedDLCForModelParts=false;
103
104#if defined(__PS3__) || defined(__ORBIS__)
105 m_ePlayerNameValidState=ePlayerNameValid_NotSet;
106#endif
107
108 enderChestInventory = shared_ptr<PlayerEnderChestContainer>(new PlayerEnderChestContainer());
109
110 m_bAwardedOnARail=false;
111}
112
113Player::Player(Level *level, const wstring &name) : LivingEntity( level )
114{
115 // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
116 // the derived version of the function is called
117 this->defineSynchedData();
118
119 this->name = name;
120
121 _init();
122 MemSect(11);
123 inventoryMenu = new InventoryMenu(inventory, !level->isClientSide, this);
124 MemSect(0);
125
126 containerMenu = inventoryMenu;
127
128 heightOffset = 1.62f;
129 Pos *spawnPos = level->getSharedSpawnPos();
130 moveTo(spawnPos->x + 0.5, spawnPos->y + 1, spawnPos->z + 0.5, 0, 0);
131 delete spawnPos;
132
133 rotOffs = 180;
134 flameTime = 20;
135
136 m_skinIndex = eDefaultSkins_Skin0;
137 m_playerIndex = 0;
138 m_dwSkinId = 0;
139 m_dwCapeId = 0;
140
141 // 4J Added
142 m_xuid = INVALID_XUID;
143 m_OnlineXuid = INVALID_XUID;
144 //m_bShownOnMaps = true;
145 setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false);
146 m_bIsGuest = false;
147
148#ifndef _XBOX_ONE
149 // 4J: Set UUID to name on none-XB1 consoles, may change in future but for now
150 // ownership of animals on these consoles is done by name
151 setUUID(name);
152#endif
153}
154
155Player::~Player()
156{
157 // TODO 4J
158 //printf("A player has been destroyed.\n");
159 delete inventoryMenu;
160
161 // 4J Stu - Fix for #10938 - CRASH - Game hardlocks when client has an open chest and Xbox Guide while host exits without saving.
162 // If the container menu is not the inventory menu, then the player has a menu open. These get deleted when the xui scene
163 // is destroyed, so we can not delete it here
164 //if( containerMenu != inventoryMenu ) delete containerMenu;
165}
166
167void Player::registerAttributes()
168{
169 LivingEntity::registerAttributes();
170
171 getAttributes()->registerAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(1);
172}
173
174void Player::defineSynchedData()
175{
176 LivingEntity::defineSynchedData();
177
178 entityData->define(DATA_PLAYER_FLAGS_ID, (byte) 0);
179 entityData->define(DATA_PLAYER_ABSORPTION_ID, (float) 0);
180 entityData->define(DATA_SCORE_ID, (int) 0);
181}
182
183shared_ptr<ItemInstance> Player::getUseItem()
184{
185 return useItem;
186}
187
188int Player::getUseItemDuration()
189{
190 return useItemDuration;
191}
192
193bool Player::isUsingItem()
194{
195 return useItem != NULL;
196}
197
198int Player::getTicksUsingItem()
199{
200 if (isUsingItem())
201 {
202 return useItem->getUseDuration() - useItemDuration;
203 }
204 return 0;
205}
206
207void Player::releaseUsingItem()
208{
209 if (useItem != NULL)
210 {
211 useItem->releaseUsing(level, dynamic_pointer_cast<Player>( shared_from_this() ), useItemDuration);
212
213 // 4J Stu - Fix for various bugs where an incorrect bow was displayed when it broke (#70859,#93972,#93974)
214 if (useItem->count == 0)
215 {
216 removeSelectedItem();
217 }
218 }
219 stopUsingItem();
220}
221
222void Player::stopUsingItem()
223{
224 useItem = nullptr;
225 useItemDuration = 0;
226 if (!level->isClientSide)
227 {
228 setUsingItemFlag(false);
229 }
230}
231
232bool Player::isBlocking()
233{
234 return isUsingItem() && Item::items[useItem->id]->getUseAnimation(useItem) == UseAnim_block;
235}
236
237// 4J Stu - Added for things that should only be ticked once per simulation frame
238void Player::updateFrameTick()
239{
240 if (useItem != NULL)
241 {
242 shared_ptr<ItemInstance> item = inventory->getSelected();
243 // 4J Stu - Fix for #45508 - TU5: Gameplay: Eating one piece of food will result in a second piece being eaten as well
244 // Original code was item != useItem. Changed this now to use the equals function, and add the NULL check as well for the other possible not equals (useItem is not NULL if we are here)
245 // This is because the useItem and item could be different objects due to an inventory update from the server, but still be the same item (with the same id,count and auxvalue)
246 if (item == NULL || !item->equals(useItem) )
247 {
248 stopUsingItem();
249 }
250 else
251 {
252 if (useItemDuration <= 25 && useItemDuration % 4 == 0)
253 {
254 spawnEatParticles(item, 5);
255 }
256 if (--useItemDuration == 0)
257 {
258 if (!level->isClientSide)
259 {
260 completeUsingItem();
261 }
262 }
263 }
264 }
265
266 if (takeXpDelay > 0) takeXpDelay--;
267
268 if (isSleeping())
269 {
270 sleepCounter++;
271 if (sleepCounter > SLEEP_DURATION)
272 {
273 sleepCounter = SLEEP_DURATION;
274 }
275
276 if (!level->isClientSide)
277 {
278 if (!checkBed())
279 {
280 stopSleepInBed(true, true, false);
281 }
282 else if (level->isDay())
283 {
284 stopSleepInBed(false, true, true);
285 }
286 }
287 }
288 else if (sleepCounter > 0)
289 {
290 sleepCounter++;
291 if (sleepCounter >= (SLEEP_DURATION + WAKE_UP_DURATION))
292 {
293 sleepCounter = 0;
294 }
295 }
296
297 if(!isAlive())
298 {
299 deathFadeCounter++;
300 if (deathFadeCounter > DEATHFADE_DURATION)
301 {
302 deathFadeCounter = DEATHFADE_DURATION;
303 }
304 }
305}
306
307void Player::tick()
308{
309 if(level->isClientSide)
310 {
311 // 4J Stu - Server player calls this differently so that it only happens once per simulation tick
312 updateFrameTick();
313 }
314
315 LivingEntity::tick();
316
317 if (!level->isClientSide)
318 {
319 if (containerMenu != NULL && !containerMenu->stillValid( dynamic_pointer_cast<Player>( shared_from_this() ) ))
320 {
321 closeContainer();
322 containerMenu = inventoryMenu;
323 }
324 }
325
326 if (isOnFire() && (abilities.invulnerable || hasInvulnerablePrivilege() ) )
327 {
328 clearFire();
329 }
330
331 xCloakO = xCloak;
332 yCloakO = yCloak;
333 zCloakO = zCloak;
334
335 double xca = x - xCloak;
336 double yca = y - yCloak;
337 double zca = z - zCloak;
338
339 double m = 10;
340 if (xca > m) xCloakO = xCloak = x;
341 if (zca > m) zCloakO = zCloak = z;
342 if (yca > m) yCloakO = yCloak = y;
343 if (xca < -m) xCloakO = xCloak = x;
344 if (zca < -m) zCloakO = zCloak = z;
345 if (yca < -m) yCloakO = yCloak = y;
346
347 xCloak += xca * 0.25;
348 zCloak += zca * 0.25;
349 yCloak += yca * 0.25;
350
351 if (riding == NULL)
352 {
353 if( minecartAchievementPos != NULL )
354 {
355 delete minecartAchievementPos;
356 minecartAchievementPos = NULL;
357 }
358 }
359
360 if (!level->isClientSide)
361 {
362 foodData.tick(dynamic_pointer_cast<Player>(shared_from_this()));
363 }
364
365 // 4J Stu Debugging
366 if (!level->isClientSide)
367 {
368 static int count = 0;
369 if( count++ == 100 )
370 {
371#if 0
372#ifdef _WINDOWS64
373 // Drop some items so we have them in inventory to play with
374 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Tile::recordPlayer) ) );
375 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::map) ) );
376 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_01) ) );
377 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_02) ) );
378 this->drop( shared_ptr<ItemInstance>(new ItemInstance( Item::pickAxe_diamond, 1 )) );
379#endif
380
381#ifdef __PS3__
382 // #ifdef _DEBUG
383 // // Drop some items so we have them in inventory to play with
384 // this->drop( shared_ptr<ItemInstance>( new ItemInstance(Tile::recordPlayer) ) );
385 // this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::map) ) );
386 // this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_01) ) );
387 // this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_02) ) );
388 // this->drop( shared_ptr<ItemInstance>(new ItemInstance( Item::pickAxe_diamond, 1 )) );
389 // #endif
390#endif
391
392#ifdef _DURANGO
393 // Drop some items so we have them in inventory to play with
394 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Tile::recordPlayer) ) );
395 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::map) ) );
396 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_01) ) );
397 this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_02) ) );
398 this->drop( shared_ptr<ItemInstance>(new ItemInstance( Item::pickAxe_diamond, 1 )) );
399#endif
400#endif
401 // 4J-PB - Throw items out at the start of the level
402 //this->drop( new ItemInstance( Item::pickAxe_diamond, 1 ) );
403 //this->drop( new ItemInstance( Tile::workBench, 1 ) );
404 //this->drop( new ItemInstance( Tile::treeTrunk, 8 ) );
405 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::milk, 3 ) ) );
406 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::sugar, 2 ) ) );
407 //this->drop( new ItemInstance( Tile::stoneBrick, 8 ) );
408 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::wheat, 3 ) ) );
409 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::egg, 1 ) ) );
410 //this->drop( new ItemInstance( Item::bow, 1 ) );
411 //this->drop( new ItemInstance( Item::arrow, 10 ) );
412 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::saddle, 10 ) ) );
413 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::fence, 64 ) ) );
414 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::fence, 64 ) ) );
415 //this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::fence, 64 ) ) );
416
417
418 //shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(Pig::_class->newInstance( level ));
419 //mob->moveTo(x+1, y, z+1, level->random->nextFloat() * 360, 0);
420 //level->addEntity(mob);
421
422 // 4J : WESTY : Spawn some wolves to befriend!
423 /*
424 shared_ptr<Mob> mob1 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
425 mob1->moveTo(x+1, y, z+1, level->random->nextFloat() * 360, 0);
426 level->addEntity(mob1);
427
428 shared_ptr<Mob> mob2 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
429 mob2->moveTo(x+2, y, z+1, level->random->nextFloat() * 360, 0);
430 level->addEntity(mob2);
431
432 shared_ptr<Mob> mob3 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
433 mob3->moveTo(x+1, y, z+2, level->random->nextFloat() * 360, 0);
434 level->addEntity(mob3);
435
436 shared_ptr<Mob> mob4 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
437 mob4->moveTo(x+3, y, z+1, level->random->nextFloat() * 360, 0);
438 level->addEntity(mob4);
439
440 shared_ptr<Mob> mob5 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
441 mob5->moveTo(x+1, y, z+3, level->random->nextFloat() * 360, 0);
442 level->addEntity(mob5);
443 */
444
445 // inventory.add(new ItemInstance(Item.potion, 1, PotionBrewing.THROWABLE_MASK | 0xc));
446 // addEffect(new MobEffectInstance(MobEffect.blindness.id, 60));
447 // increaseXp(10);
448
449 {
450 // ItemInstance itemInstance = new ItemInstance(Item.pickAxe_diamond);
451 // itemInstance.enchant(Enchantment.diggingBonus, 3);
452 // inventory.add(itemInstance);
453 }
454 }
455#if 0
456 // 4J Stu - This makes a tunnel with a powered track just over length to get the On A Rail achievement
457 // It needs a few items at the start to get you going (a level and some powered rails) and of course a
458 // minecart. For some reason some of the torches come off so it will also need some fixing along the way.
459 static bool madeTrack = false;
460 if( !madeTrack )
461 {
462 this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::minecart, 1 ) ) );
463 this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::goldenRail, 10 ) ) );
464 this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::lever, 10 ) ) );
465
466 int poweredCount = 0;
467 for(int i = 10; i < 2800; ++i)
468 {
469 level->setTileAndData(x+i,y-1,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
470 level->setTileAndData(x+i,y,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
471 level->setTileAndData(x+i,y+1,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
472 level->setTileAndData(x+i,y+2,z-2,Tile::glowstone_Id,0,Tile::UPDATE_CLIENTS);
473 level->setTileAndData(x+i,y+3,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
474
475 level->setTileAndData(x+i,y-1,z-1,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS);
476 if(i%20 == 0)
477 {
478 level->setTileAndData(x+i,y,z-1,Tile::redstoneTorch_on_Id,0,Tile::UPDATE_CLIENTS);
479 poweredCount = 4;
480 }
481 else
482 {
483 level->setTileAndData(x+i,y,z-1,0,0,Tile::UPDATE_CLIENTS);
484 }
485 level->setTileAndData(x+i,y+1,z-1,0,0,Tile::UPDATE_CLIENTS);
486 level->setTileAndData(x+i,y+2,z-1,0,0,Tile::UPDATE_CLIENTS);
487 level->setTileAndData(x+i,y+3,z-1,0,0,Tile::UPDATE_CLIENTS);
488
489 level->setTileAndData(x+i,y-1,z,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS);
490 if(poweredCount>0)
491 {
492 level->setTileAndData(x+i,y,z,Tile::goldenRail_Id,0,Tile::UPDATE_CLIENTS);
493 --poweredCount;
494 }
495 else
496 {
497 level->setTileAndData(x+i,y,z,Tile::rail_Id,0,Tile::UPDATE_CLIENTS);
498 }
499 level->setTileAndData(x+i,y+1,z,0,0,Tile::UPDATE_CLIENTS);
500 level->setTileAndData(x+i,y+2,z,0,0,Tile::UPDATE_CLIENTS);
501 level->setTileAndData(x+i,y+3,z,0,0,Tile::UPDATE_CLIENTS);
502
503 level->setTileAndData(x+i,y-1,z+1,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS);
504 if((i+5)%20 == 0)
505 {
506 level->setTileAndData(x+i,y,z+1,Tile::torch_Id,0,Tile::UPDATE_CLIENTS);
507 }
508 else
509 {
510 level->setTileAndData(x+i,y,z+1,0,0,Tile::UPDATE_CLIENTS);
511 }
512 level->setTileAndData(x+i,y+1,z+1,0,0,Tile::UPDATE_CLIENTS);
513 level->setTileAndData(x+i,y+2,z+1,0,0,Tile::UPDATE_CLIENTS);
514 level->setTileAndData(x+i,y+3,z+1,0,0,Tile::UPDATE_CLIENTS);
515
516 level->setTileAndData(x+i,y-1,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
517 level->setTileAndData(x+i,y,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
518 level->setTileAndData(x+i,y+1,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
519 level->setTileAndData(x+i,y+2,z+2,Tile::glowstone_Id,0,Tile::UPDATE_CLIENTS);
520 level->setTileAndData(x+i,y+3,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
521 }
522 madeTrack = true;
523 }
524#endif
525 }
526 //End 4J sTU
527}
528
529int Player::getPortalWaitTime()
530{
531 return abilities.invulnerable ? 0 : SharedConstants::TICKS_PER_SECOND * 4;
532}
533
534int Player::getDimensionChangingDelay()
535{
536 return SharedConstants::TICKS_PER_SECOND / 2;
537}
538
539void Player::playSound(int iSound, float volume, float pitch)
540{
541 // this sound method will play locally for the local player, and
542 // broadcast to remote players
543 level->playPlayerSound(dynamic_pointer_cast<Player>(shared_from_this()), iSound, volume, pitch);
544}
545
546void Player::spawnEatParticles(shared_ptr<ItemInstance> useItem, int count)
547{
548 if (useItem->getUseAnimation() == UseAnim_drink)
549 {
550 playSound(eSoundType_RANDOM_DRINK, 0.5f, level->random->nextFloat() * 0.1f + 0.9f);
551 }
552 if (useItem->getUseAnimation() == UseAnim_eat)
553 {
554 for (int i = 0; i < count; i++)
555 {
556 Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0);
557
558 d->xRot(-xRot * PI / 180);
559 d->yRot(-yRot * PI / 180);
560
561 Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6);
562 p->xRot(-xRot * PI / 180);
563 p->yRot(-yRot * PI / 180);
564 p = p->add(x, y + getHeadHeight(), z);
565
566 level->addParticle(PARTICLE_ICONCRACK(useItem->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z);
567 }
568
569 // 4J Stu - Was L"mob.eat" which doesnt exist
570 playSound(eSoundType_RANDOM_EAT, 0.5f + 0.5f * random->nextInt(2), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
571 }
572}
573
574void Player::completeUsingItem()
575{
576 if (useItem != NULL)
577 {
578 spawnEatParticles(useItem, 16);
579
580 int oldCount = useItem->count;
581 shared_ptr<ItemInstance> itemInstance = useItem->useTimeDepleted(level, dynamic_pointer_cast<Player>(shared_from_this()));
582 if (itemInstance != useItem || (itemInstance != NULL && itemInstance->count != oldCount))
583 {
584 inventory->items[inventory->selected] = itemInstance;
585 if (itemInstance->count == 0)
586 {
587 inventory->items[inventory->selected] = nullptr;
588 }
589 }
590 stopUsingItem();
591 }
592}
593
594void Player::handleEntityEvent(byte id)
595{
596 if (id == EntityEvent::USE_ITEM_COMPLETE)
597 {
598 completeUsingItem();
599 }
600 else
601 {
602 LivingEntity::handleEntityEvent(id);
603 }
604}
605
606bool Player::isImmobile()
607{
608 return getHealth() <= 0 || isSleeping();
609}
610
611void Player::closeContainer()
612{
613 containerMenu = inventoryMenu;
614}
615
616void Player::ride(shared_ptr<Entity> e)
617{
618 if (riding != NULL && e == NULL)
619 {
620 if (!level->isClientSide) findStandUpPosition(riding);
621
622 if (riding != NULL)
623 {
624 riding->rider = weak_ptr<Entity>();
625 }
626 riding = nullptr;
627
628 return;
629 }
630 LivingEntity::ride(e);
631}
632
633void Player::setPlayerDefaultSkin(EDefaultSkins skin)
634{
635#ifndef _CONTENT_PACKAGE
636 wprintf(L"Setting default skin to %d for player %ls\n", skin, name.c_str() );
637#endif
638 m_skinIndex = skin;
639}
640
641void Player::setCustomSkin(DWORD skinId)
642{
643#ifndef _CONTENT_PACKAGE
644 wprintf(L"Attempting to set skin to %08X for player %ls\n", skinId, name.c_str() );
645#endif
646 EDefaultSkins playerSkin = eDefaultSkins_ServerSelected;
647
648 // reset the idle
649 setIsIdle(false);
650
651 setAnimOverrideBitmask(getSkinAnimOverrideBitmask(skinId));
652 if( !GET_IS_DLC_SKIN_FROM_BITMASK(skinId) )
653 {
654 // GET_UGC_SKIN_ID_FROM_BITMASK will always be zero - this was for a possible custom skin editor skin
655 DWORD ugcSkinIndex = GET_UGC_SKIN_ID_FROM_BITMASK(skinId);
656 DWORD defaultSkinIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(skinId);
657 if( ugcSkinIndex == 0 && defaultSkinIndex > 0 )
658 {
659 playerSkin = (EDefaultSkins) defaultSkinIndex;
660 }
661 }
662
663 if( playerSkin == eDefaultSkins_ServerSelected)
664 {
665 playerSkin = (EDefaultSkins)(m_playerIndex + 1);
666 }
667
668 // We always set a default skin, since we may be waiting for the player's custom skin to be transmitted
669 setPlayerDefaultSkin( playerSkin );
670
671 m_dwSkinId = skinId;
672 this->customTextureUrl = app.getSkinPathFromId(skinId);
673
674 // set the new player additional boxes
675 /*vector<ModelPart *> *pvModelParts=app.GetAdditionalModelParts(m_dwSkinId);
676
677 if(pvModelParts==NULL)
678 {
679 // we don't have the data from the dlc skin yet
680 app.DebugPrintf("Couldn't get model parts for skin %X\n",m_dwSkinId);
681
682 // do we have it from the DLC pack?
683 DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(this->customTextureUrl);
684
685 if(pDLCSkinFile!=NULL)
686 {
687 DWORD dwBoxC=pDLCSkinFile->getAdditionalBoxesCount();
688 if(dwBoxC!=0)
689 {
690 app.DebugPrintf("Got model parts from DLCskin for skin %X\n",m_dwSkinId);
691 pvModelParts=app.SetAdditionalSkinBoxes(m_dwSkinId,pDLCSkinFile->getAdditionalBoxes());
692 this->SetAdditionalModelParts(pvModelParts);
693 }
694 else
695 {
696 this->SetAdditionalModelParts(NULL);
697 }
698 app.SetAnimOverrideBitmask(pDLCSkinFile->getSkinID(),pDLCSkinFile->getAnimOverrideBitmask());
699 }
700 else
701 {
702 this->SetAdditionalModelParts(NULL);
703 }
704 }
705 else
706 {
707 app.DebugPrintf("Got model parts from app.GetAdditionalModelParts for skin %X\n",m_dwSkinId);
708
709 this->SetAdditionalModelParts(pvModelParts);
710 }*/
711
712 // reset the check for model parts
713 m_bCheckedForModelParts=false;
714 m_bCheckedDLCForModelParts=false;
715 this->SetAdditionalModelParts(NULL);
716
717
718}
719
720unsigned int Player::getSkinAnimOverrideBitmask(DWORD skinId)
721{
722 unsigned long bitmask = 0L;
723 if( GET_IS_DLC_SKIN_FROM_BITMASK(skinId) )
724 {
725 // Temp check for anim override
726 switch( GET_DLC_SKIN_ID_FROM_BITMASK(skinId) )
727 {
728 case 0x2://SP1_ZOMBIE:
729 case 0x3://SP1_HEROBRINE:
730 case 0xc8://SP3_ZOMBIE_PIGMAN:
731 case 0xc9://SP3_ZOMBIE_HEROBRINE:
732 case 0x1f8: // SPH_4JMUMMY
733 case 0x220: // SPH_AOT_MUMMY
734 case 0x23a: // SPH_CLIMAX_ZOMBIEBUSINESSMAN
735 case 0x23d: // SPH_CLIMAX_EVILROBOT
736 case 0x247: // SPH_CLIMAX_ZOMBIE
737 case 0x194: // SOA_DEADLIGHT_SKINNY_ZOMBIE
738 case 0x195: // SOA_DEADLIGHT_FEMALE_ZOMBIE
739 bitmask = 1<<HumanoidModel::eAnim_ArmsOutFront;
740 break;
741 case 0x1fa://SPH_GHOST:
742 bitmask = 1<<HumanoidModel::eAnim_ArmsOutFront | 1<<HumanoidModel::eAnim_NoLegAnim;
743 break;
744 case 0x1f4://SPH_GRIMREAPER:
745 bitmask = 1<<HumanoidModel::eAnim_ArmsDown | 1<<HumanoidModel::eAnim_NoLegAnim;
746 break;
747 case 0x1f7: // SPH_4J_FRANKENSTEIN
748 //bitmask = 1<<HumanoidModel::eAnim_HasIdle;
749 break;
750 break;
751 default:
752 // This is not one of the prefined skins
753 // Does the app have an anim override for this skin?
754 bitmask=app.GetAnimOverrideBitmask(skinId);
755 break;
756 }
757 }
758 return bitmask;
759}
760
761void Player::setXuid(PlayerUID xuid)
762{
763 m_xuid = xuid;
764#ifdef _XBOX_ONE
765 // 4J Stu - For XboxOne (and probably in the future all other platforms) we store a UUID for the player to use as the owner key for tamed animals
766 // This should just be a string version of the xuid
767
768 setUUID( xuid.toString() );
769#endif
770}
771
772void Player::setCustomCape(DWORD capeId)
773{
774#ifndef _CONTENT_PACKAGE
775 wprintf(L"Attempting to set cape to %08X for player %s\n", capeId, name.c_str() );
776#endif
777
778 m_dwCapeId = capeId;
779
780 if(capeId > 0)
781 {
782 this->customTextureUrl2 = Player::getCapePathFromId(capeId);
783 }
784 else
785 {
786 MOJANG_DATA *pMojangData=app.GetMojangDataForXuid(getOnlineXuid());
787 if(pMojangData)
788 {
789 // Cape
790 if(pMojangData->wchCape)
791 {
792 this->customTextureUrl2= pMojangData->wchCape;
793 }
794 else
795 {
796 if(app.DefaultCapeExists())
797 {
798 this->customTextureUrl2= wstring(L"Special_Cape.png");
799 }
800 else
801 {
802 this->customTextureUrl2= wstring(L"");
803 }
804 }
805
806 }
807 else
808 {
809 // if there is a custom default cloak, then set it here
810 if(app.DefaultCapeExists())
811 {
812 this->customTextureUrl2= wstring(L"Special_Cape.png");
813 }
814 else
815 {
816 this->customTextureUrl2 =wstring(L"");
817 }
818 }
819 }
820}
821
822DWORD Player::getCapeIdFromPath(const wstring &cape)
823{
824 bool dlcCape = false;
825 unsigned int capeId = 0;
826
827 if(cape.size() >= 14)
828 {
829 dlcCape = cape.substr(0,3).compare(L"dlc") == 0;
830
831 wstring capeValue = cape.substr(7,cape.size());
832 capeValue = capeValue.substr(0,capeValue.find_first_of(L'.'));
833
834 std::wstringstream ss;
835 // 4J Stu - dlc skins are numbered using decimal to make it easier for artists/people to number manually
836 // Everything else is numbered using hex
837 if(dlcCape)
838 ss << std::dec << capeValue.c_str();
839 else
840 ss << std::hex << capeValue.c_str();
841 ss >> capeId;
842
843 capeId = MAKE_SKIN_BITMASK(dlcCape, capeId);
844 }
845 return capeId;
846}
847
848wstring Player::getCapePathFromId(DWORD capeId)
849{
850 // 4J Stu - This function maps the encoded DWORD we store in the player profile
851 // to a filename that is stored as a memory texture and shared between systems in game
852 wchar_t chars[256];
853 if( GET_IS_DLC_SKIN_FROM_BITMASK(capeId) )
854 {
855 // 4J Stu - DLC skins are numbered using decimal rather than hex to make it easier to number manually
856 swprintf(chars,256,L"dlccape%08d.png",GET_DLC_SKIN_ID_FROM_BITMASK(capeId));
857
858 }
859 else
860 {
861 DWORD ugcCapeIndex = GET_UGC_SKIN_ID_FROM_BITMASK(capeId);
862 DWORD defaultCapeIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(capeId);
863 if( ugcCapeIndex == 0 )
864 {
865 swprintf(chars,256,L"defcape%08X.png",defaultCapeIndex);
866 }
867 else
868 {
869 swprintf(chars,256,L"ugccape%08X.png",ugcCapeIndex);
870 }
871 }
872 return chars;
873}
874
875void Player::ChangePlayerSkin()
876{
877
878 if(app.vSkinNames.size()>0)
879 {
880
881 m_uiPlayerCurrentSkin++;
882 if(m_uiPlayerCurrentSkin>app.vSkinNames.size())
883 {
884 m_uiPlayerCurrentSkin=0;
885 this->customTextureUrl=L"";
886 }
887 else
888 {
889 if(m_uiPlayerCurrentSkin>0)
890 {
891 // change this players custom texture url
892 this->customTextureUrl=app.vSkinNames[m_uiPlayerCurrentSkin-1];
893 }
894 }
895 }
896}
897
898void Player::prepareCustomTextures()
899{
900 MOJANG_DATA *pMojangData=app.GetMojangDataForXuid(getOnlineXuid());
901
902 if(pMojangData)
903 {
904 // Skin
905 if(pMojangData->wchSkin)
906 {
907 this->customTextureUrl= pMojangData->wchSkin;
908 }
909
910 // 4J Stu - Don't update the cape here, it gets set elsewhere
911 // Cape
912 //if(pMojangData->wchCape)
913 //{
914 // this->customTextureUrl2= pMojangData->wchCape;
915 //}
916 //else
917 //{
918 // if(app.DefaultCapeExists())
919 // {
920 // this->customTextureUrl2= wstring(L"Default_Cape.png");
921 // }
922 // else
923 // {
924 // this->customTextureUrl2= wstring(L"");
925 // }
926 //}
927
928 }
929 else
930 {
931 // 4J Stu - Don't update the cape here, it gets set elsewhere
932 // if there is a custom default cloak, then set it here
933 //if(app.DefaultCapeExists())
934 //{
935 // this->customTextureUrl2= wstring(L"Default_Cape.png");
936 //}
937 //else
938 //{
939 // this->customTextureUrl2 =wstring(L"");
940 //}
941 }
942
943 /*cloakTexture = wstring(L"http://s3.amazonaws.com/MinecraftCloaks/").append( name ).append( L".png" );*/
944 //this->customTextureUrl2 = cloakTexture;
945}
946
947void Player::rideTick()
948{
949 if (!level->isClientSide && isSneaking())
950 {
951 ride(nullptr);
952 setSneaking(false);
953 return;
954 }
955
956 double preX = x, preY = y, preZ = z;
957 float preYRot = yRot, preXRot = xRot;
958
959 LivingEntity::rideTick();
960 oBob = bob;
961 bob = 0;
962
963 checkRidingStatistiscs(x - preX, y - preY, z - preZ);
964
965 // riding can be set to null inside 'Entity::rideTick()'.
966 if ( riding != NULL && (riding->GetType() & eTYPE_PIG) == eTYPE_PIG )
967 {
968 // 4J Stu - I don't know why we would want to do this, but it means that the players head is locked in position and can't move around
969 //xRot = preXRot;
970 //yRot = preYRot;
971
972 shared_ptr<Pig> pig = dynamic_pointer_cast<Pig>(riding);
973 yBodyRot = pig->yBodyRot;
974
975 while (yBodyRot - yBodyRotO < -180)
976 yBodyRotO -= 360;
977 while (yBodyRot - yBodyRotO >= 180)
978 yBodyRotO += 360;
979 }
980}
981
982
983void Player::resetPos()
984{
985 heightOffset = 1.62f;
986 setSize(0.6f, 1.8f);
987 LivingEntity::resetPos();
988 setHealth(getMaxHealth());
989 deathTime = 0;
990}
991
992void Player::serverAiStep()
993{
994 LivingEntity::serverAiStep();
995 updateSwingTime();
996}
997
998
999void Player::aiStep()
1000{
1001 if (jumpTriggerTime > 0) jumpTriggerTime--;
1002
1003 if (level->difficulty == Difficulty::PEACEFUL && getHealth() < getMaxHealth() && level->getGameRules()->getBoolean(GameRules::RULE_NATURAL_REGENERATION))
1004 {
1005 if (tickCount % 20 * 12 == 0) heal(1);
1006 }
1007 inventory->tick();
1008 oBob = bob;
1009
1010 LivingEntity::aiStep();
1011
1012 AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
1013 if (!level->isClientSide) speed->setBaseValue(abilities.getWalkingSpeed());
1014 flyingSpeed = defaultFlySpeed;
1015 if (isSprinting())
1016 {
1017 flyingSpeed += defaultFlySpeed * 0.3f;
1018 }
1019
1020 setSpeed((float) speed->getValue());
1021
1022 float tBob = (float) sqrt(xd * xd + zd * zd);
1023
1024 // 4J added - we were getting a NaN with zero xd & zd
1025 if(( xd * xd + zd * zd ) < 0.00001f )
1026 {
1027 tBob = 0.0f;
1028 }
1029
1030 float tTilt = (float) atan(-yd * 0.2f) * 15.0f;
1031 if (tBob > 0.1f) tBob = 0.1f;
1032 if (!onGround || getHealth() <= 0) tBob = 0;
1033 if (onGround || getHealth() <= 0) tTilt = 0;
1034
1035 bob += (tBob - bob) * 0.4f;
1036
1037 tilt += (tTilt - tilt) * 0.8f;
1038
1039 if (getHealth() > 0)
1040 {
1041 AABB *pickupArea = NULL;
1042 if (riding != NULL && !riding->removed)
1043 {
1044 // if the player is riding, also touch entities under the
1045 // pig/horse
1046 pickupArea = bb->minmax(riding->bb)->grow(1, 0, 1);
1047 }
1048 else
1049 {
1050 pickupArea = bb->grow(1, .5, 1);
1051 }
1052
1053 vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), pickupArea);
1054 if (entities != NULL)
1055 {
1056 AUTO_VAR(itEnd, entities->end());
1057 for (AUTO_VAR(it, entities->begin()); it != itEnd; it++)
1058 {
1059 shared_ptr<Entity> e = *it; //entities->at(i);
1060 if (!e->removed)
1061 {
1062 touch(e);
1063 }
1064 }
1065 }
1066 }
1067}
1068
1069
1070void Player::touch(shared_ptr<Entity> entity)
1071{
1072 entity->playerTouch( dynamic_pointer_cast<Player>( shared_from_this() ) );
1073}
1074
1075int Player::getScore()
1076{
1077 return entityData->getInteger(DATA_SCORE_ID);
1078}
1079
1080void Player::setScore(int value)
1081{
1082 entityData->set(DATA_SCORE_ID, value);
1083}
1084
1085void Player::increaseScore(int amount)
1086{
1087 int score = getScore();
1088 entityData->set(DATA_SCORE_ID, score + amount);
1089}
1090
1091void Player::die(DamageSource *source)
1092{
1093 LivingEntity::die(source);
1094 setSize(0.2f, 0.2f);
1095 setPos(x, y, z);
1096 yd = 0.1f;
1097
1098 // 4J - TODO need to use a xuid
1099 if ( app.isXuidNotch( m_xuid ) )
1100 {
1101 drop(shared_ptr<ItemInstance>( new ItemInstance(Item::apple, 1) ), true);
1102 }
1103 if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
1104 {
1105 inventory->dropAll();
1106 }
1107
1108 if (source != NULL)
1109 {
1110 xd = -Mth::cos((hurtDir + yRot) * PI / 180) * 0.1f;
1111 zd = -Mth::sin((hurtDir + yRot) * PI / 180) * 0.1f;
1112 }
1113 else
1114 {
1115 xd = zd = 0;
1116 }
1117 heightOffset = 0.1f;
1118}
1119
1120void Player::awardKillScore(shared_ptr<Entity> victim, int awardPoints)
1121{
1122 increaseScore(awardPoints);
1123 vector<Objective *> *objectives = getScoreboard()->findObjectiveFor(ObjectiveCriteria::KILL_COUNT_ALL);
1124
1125 //if (victim instanceof Player)
1126 //{
1127 // awardStat(Stats::playerKills, 1);
1128 // objectives.addAll(getScoreboard().findObjectiveFor(ObjectiveCriteria::KILL_COUNT_PLAYERS));
1129 //}
1130 //else
1131 //{
1132 // awardStat(Stats::mobKills, 1);
1133 //}
1134
1135 if(objectives)
1136 {
1137 for (AUTO_VAR(it,objectives->begin()); it != objectives->end(); ++it)
1138 {
1139 Objective *objective = *it;
1140 Score *score = getScoreboard()->getPlayerScore(getAName(), objective);
1141 score->increment();
1142 }
1143 }
1144}
1145
1146bool Player::isShootable()
1147{
1148 return true;
1149}
1150
1151bool Player::isCreativeModeAllowed()
1152{
1153 return true;
1154}
1155
1156shared_ptr<ItemEntity> Player::drop(bool all)
1157{
1158 return drop(inventory->removeItem(inventory->selected, all && inventory->getSelected() != NULL ? inventory->getSelected()->count : 1), false);
1159}
1160
1161shared_ptr<ItemEntity> Player::drop(shared_ptr<ItemInstance> item)
1162{
1163 return drop(item, false);
1164}
1165
1166shared_ptr<ItemEntity> Player::drop(shared_ptr<ItemInstance> item, bool randomly)
1167{
1168 if (item == NULL) return nullptr;
1169 if (item->count == 0) return nullptr;
1170
1171 shared_ptr<ItemEntity> thrownItem = shared_ptr<ItemEntity>( new ItemEntity(level, x, y - 0.3f + getHeadHeight(), z, item) );
1172 thrownItem->throwTime = 20 * 2;
1173
1174 thrownItem->setThrower(getName());
1175
1176 float pow = 0.1f;
1177 if (randomly)
1178 {
1179 float _pow = random->nextFloat() * 0.5f;
1180 float dir = random->nextFloat() * PI * 2;
1181 thrownItem->xd = -sin(dir) * _pow;
1182 thrownItem->zd = cos(dir) * _pow;
1183 thrownItem->yd = 0.2f;
1184
1185 }
1186 else
1187 {
1188 pow = 0.3f;
1189 thrownItem->xd = -sin(yRot / 180 * PI) * cos(xRot / 180 * PI) * pow;
1190 thrownItem->zd = cos(yRot / 180 * PI) * cos(xRot / 180 * PI) * pow;
1191 thrownItem->yd = -sin(xRot / 180 * PI) * pow + 0.1f;
1192 pow = 0.02f;
1193
1194 float dir = random->nextFloat() * PI * 2;
1195 pow *= random->nextFloat();
1196 thrownItem->xd += cos(dir) * pow;
1197 thrownItem->yd += (random->nextFloat() - random->nextFloat()) * 0.1f;
1198 thrownItem->zd += sin(dir) * pow;
1199 }
1200
1201 reallyDrop(thrownItem);
1202
1203 return thrownItem;
1204}
1205
1206
1207void Player::reallyDrop(shared_ptr<ItemEntity> thrownItem)
1208{
1209 level->addEntity(thrownItem);
1210}
1211
1212
1213float Player::getDestroySpeed(Tile *tile, bool hasProperTool)
1214{
1215 float speed = inventory->getDestroySpeed(tile);
1216
1217 if (speed > 1)
1218 {
1219 int efficiency = EnchantmentHelper::getDiggingBonus(dynamic_pointer_cast<LivingEntity>(shared_from_this()));
1220 shared_ptr<ItemInstance> item = inventory->getSelected();
1221
1222 if (efficiency > 0 && item != NULL)
1223 {
1224 float boost = efficiency * efficiency + 1;
1225
1226 if (item->canDestroySpecial(tile) || speed > 1)
1227 {
1228 speed += boost;
1229 }
1230 else
1231 {
1232 speed += boost * 0.08f;
1233 }
1234 }
1235 }
1236
1237 if (hasEffect(MobEffect::digSpeed))
1238 {
1239 speed *= 1.0f + (getEffect(MobEffect::digSpeed)->getAmplifier() + 1) * .2f;
1240 }
1241 if (hasEffect(MobEffect::digSlowdown))
1242 {
1243 speed *= 1.0f - (getEffect(MobEffect::digSlowdown)->getAmplifier() + 1) * .2f;
1244 }
1245
1246 if (isUnderLiquid(Material::water) && !EnchantmentHelper::hasWaterWorkerBonus(dynamic_pointer_cast<LivingEntity>(shared_from_this()))) speed /= 5;
1247
1248 // 4J Stu - onGround is set to true on the client when we are flying, which means
1249 // the dig speed is out of sync with the server. Removing this speed change when
1250 // flying so that we always dig as the same speed
1251 //if (!onGround) speed /= 5;
1252
1253 return speed;
1254}
1255
1256bool Player::canDestroy(Tile *tile)
1257{
1258 return inventory->canDestroy(tile);
1259}
1260
1261void Player::readAdditionalSaveData(CompoundTag *entityTag)
1262{
1263 LivingEntity::readAdditionalSaveData(entityTag);
1264 ListTag<CompoundTag> *inventoryList = (ListTag<CompoundTag> *) entityTag->getList(L"Inventory");
1265 inventory->load(inventoryList);
1266 inventory->selected = entityTag->getInt(L"SelectedItemSlot");
1267 m_isSleeping = entityTag->getBoolean(L"Sleeping");
1268 sleepCounter = entityTag->getShort(L"SleepTimer");
1269
1270 experienceProgress = entityTag->getFloat(L"XpP");
1271 experienceLevel = entityTag->getInt(L"XpLevel");
1272 totalExperience = entityTag->getInt(L"XpTotal");
1273 setScore(entityTag->getInt(L"Score"));
1274
1275 if (m_isSleeping)
1276 {
1277 bedPosition = new Pos( Mth::floor(x), Mth::floor(y), Mth::floor(z));
1278 stopSleepInBed(true, true, false);
1279 }
1280
1281 if (entityTag->contains(L"SpawnX") && entityTag->contains(L"SpawnY") && entityTag->contains(L"SpawnZ"))
1282 {
1283 respawnPosition = new Pos(entityTag->getInt(L"SpawnX"), entityTag->getInt(L"SpawnY"), entityTag->getInt(L"SpawnZ"));
1284 respawnForced = entityTag->getBoolean(L"SpawnForced");
1285 }
1286
1287 foodData.readAdditionalSaveData(entityTag);
1288 abilities.loadSaveData(entityTag);
1289
1290 if (entityTag->contains(L"EnderItems"))
1291 {
1292 ListTag<CompoundTag> *enderItemsList = (ListTag<CompoundTag> *) entityTag->getList(L"EnderItems");
1293 enderChestInventory->setItemsByTag(enderItemsList);
1294 }
1295
1296 // 4J Added
1297 m_uiGamePrivileges = entityTag->getInt(L"GamePrivileges");
1298}
1299
1300void Player::addAdditonalSaveData(CompoundTag *entityTag)
1301{
1302 LivingEntity::addAdditonalSaveData(entityTag);
1303 entityTag->put(L"Inventory", inventory->save(new ListTag<CompoundTag>()));
1304 entityTag->putInt(L"SelectedItemSlot", inventory->selected);
1305 entityTag->putBoolean(L"Sleeping", m_isSleeping);
1306 entityTag->putShort(L"SleepTimer", (short) sleepCounter);
1307
1308 entityTag->putFloat(L"XpP", experienceProgress);
1309 entityTag->putInt(L"XpLevel", experienceLevel);
1310 entityTag->putInt(L"XpTotal", totalExperience);
1311 entityTag->putInt(L"Score", getScore());
1312
1313 if (respawnPosition != NULL)
1314 {
1315 entityTag->putInt(L"SpawnX", respawnPosition->x);
1316 entityTag->putInt(L"SpawnY", respawnPosition->y);
1317 entityTag->putInt(L"SpawnZ", respawnPosition->z);
1318 entityTag->putBoolean(L"SpawnForced", respawnForced);
1319 }
1320
1321 foodData.addAdditonalSaveData(entityTag);
1322 abilities.addSaveData(entityTag);
1323
1324 entityTag->put(L"EnderItems", enderChestInventory->createTag());
1325
1326 // 4J Added
1327 entityTag->putInt(L"GamePrivileges",m_uiGamePrivileges);
1328
1329}
1330
1331bool Player::openContainer(shared_ptr<Container> container)
1332{
1333 return true;
1334}
1335
1336bool Player::openHopper(shared_ptr<HopperTileEntity> container)
1337{
1338 return true;
1339}
1340
1341bool Player::openHopper(shared_ptr<MinecartHopper> container)
1342{
1343 return true;
1344}
1345
1346bool Player::openHorseInventory(shared_ptr<EntityHorse> horse, shared_ptr<Container> container)
1347{
1348 return true;
1349}
1350
1351bool Player::startEnchanting(int x, int y, int z, const wstring &name)
1352{
1353 return true;
1354}
1355
1356bool Player::startRepairing(int x, int y, int z)
1357{
1358 return true;
1359}
1360
1361bool Player::startCrafting(int x, int y, int z)
1362{
1363 return true;
1364}
1365
1366bool Player::openFireworks(int x, int y, int z)
1367{
1368 return true;
1369}
1370
1371float Player::getHeadHeight()
1372{
1373 return 0.12f;
1374}
1375
1376
1377void Player::setDefaultHeadHeight()
1378{
1379 heightOffset = 1.62f;
1380}
1381
1382bool Player::hurt(DamageSource *source, float dmg)
1383{
1384 if (isInvulnerable()) return false;
1385 if ( hasInvulnerablePrivilege() || (abilities.invulnerable && !source->isBypassInvul()) ) return false;
1386
1387 // 4J-JEV: Fix for PSVita: #3987 - [IN GAME] The user can take damage/die, when attempting to re-enter fly mode when falling from a height.
1388 if ( source == DamageSource::fall && isAllowedToFly() && abilities.flying ) return false;
1389
1390 noActionTime = 0;
1391 if (getHealth() <= 0) return false;
1392
1393 if (isSleeping() && !level->isClientSide)
1394 {
1395 stopSleepInBed(true, true, false);
1396 }
1397
1398 if ( source->scalesWithDifficulty() )
1399 {
1400 if (level->difficulty == Difficulty::PEACEFUL) dmg = 0;
1401 if (level->difficulty == Difficulty::EASY) dmg = dmg / 2 + 1;
1402 if (level->difficulty == Difficulty::HARD) dmg = dmg * 3 / 2;
1403 }
1404
1405 if (dmg == 0) return false;
1406
1407 shared_ptr<Entity> attacker = source->getEntity();
1408 if ( attacker != NULL && attacker->instanceof(eTYPE_ARROW) )
1409 {
1410 shared_ptr<Arrow> arrow = dynamic_pointer_cast<Arrow>(attacker);
1411 if ( arrow->owner != NULL)
1412 {
1413 attacker = arrow->owner;
1414 }
1415 }
1416
1417 return LivingEntity::hurt(source, dmg);
1418}
1419
1420bool Player::canHarmPlayer(shared_ptr<Player> target)
1421{
1422 Team *team = getTeam();
1423 Team *otherTeam = target->getTeam();
1424
1425 if (team == NULL)
1426 {
1427 return true;
1428 }
1429 if (!team->isAlliedTo(otherTeam))
1430 {
1431 return true;
1432 }
1433 return team->isAllowFriendlyFire();
1434}
1435
1436bool Player::canHarmPlayer(wstring targetName)
1437{
1438 return true;
1439}
1440
1441void Player::hurtArmor(float damage)
1442{
1443 inventory->hurtArmor(damage);
1444}
1445
1446int Player::getArmorValue()
1447{
1448 return inventory->getArmorValue();
1449}
1450
1451float Player::getArmorCoverPercentage()
1452{
1453 int count = 0;
1454 for (int i = 0; i < inventory->armor.length; i++)
1455 {
1456 if (inventory->armor[i] != NULL) {
1457 count++;
1458 }
1459 }
1460 return (float) count / (float) inventory->armor.length;
1461}
1462
1463void Player::actuallyHurt(DamageSource *source, float dmg)
1464{
1465 if (isInvulnerable()) return;
1466 if (!source->isBypassArmor() && isBlocking() && dmg > 0)
1467 {
1468 dmg = (1 + dmg) * .5f;
1469 }
1470 dmg = getDamageAfterArmorAbsorb(source, dmg);
1471 dmg = getDamageAfterMagicAbsorb(source, dmg);
1472
1473 float originalDamage = dmg;
1474 dmg = max(dmg - getAbsorptionAmount(), 0.0f);
1475 setAbsorptionAmount(getAbsorptionAmount() - (originalDamage - dmg));
1476 if (dmg == 0) return;
1477
1478 causeFoodExhaustion(source->getFoodExhaustion());
1479 float oldHealth = getHealth();
1480 setHealth(getHealth() - dmg);
1481 getCombatTracker()->recordDamage(source, oldHealth, dmg);
1482}
1483
1484bool Player::openFurnace(shared_ptr<FurnaceTileEntity> container)
1485{
1486 return true;
1487}
1488
1489bool Player::openTrap(shared_ptr<DispenserTileEntity> container)
1490{
1491 return true;
1492}
1493
1494void Player::openTextEdit(shared_ptr<TileEntity> sign)
1495{
1496}
1497
1498bool Player::openBrewingStand(shared_ptr<BrewingStandTileEntity> brewingStand)
1499{
1500 return true;
1501}
1502
1503bool Player::openBeacon(shared_ptr<BeaconTileEntity> beacon)
1504{
1505 return true;
1506}
1507
1508bool Player::openTrading(shared_ptr<Merchant> traderTarget, const wstring &name)
1509{
1510 return true;
1511}
1512
1513/**
1514* Opens an iteminstance-dependent user interface.
1515*
1516* @param itemInstance
1517*/
1518void Player::openItemInstanceGui(shared_ptr<ItemInstance> itemInstance)
1519{
1520}
1521
1522bool Player::interact(shared_ptr<Entity> entity)
1523{
1524 shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
1525
1526 shared_ptr<ItemInstance> item = getSelectedItem();
1527 shared_ptr<ItemInstance> itemClone = (item != NULL) ? item->copy() : nullptr;
1528 if ( entity->interact(thisPlayer) )
1529 {
1530 // [EB]: Added rude check to see if we're still talking about the
1531 // same item; this code caused bucket->milkbucket to be deleted because
1532 // the milkbuckets' stack got decremented to 0.
1533 if (item != NULL && item == getSelectedItem())
1534 {
1535 if (item->count <= 0 && !abilities.instabuild)
1536 {
1537 removeSelectedItem();
1538 }
1539 else if (item->count < itemClone->count && abilities.instabuild)
1540 {
1541 item->count = itemClone->count;
1542 }
1543 }
1544 return true;
1545 }
1546
1547 if ( (item != NULL) && entity->instanceof(eTYPE_LIVINGENTITY) )
1548 {
1549 // 4J - PC Comments
1550 // Hack to prevent item stacks from decrementing if the player has
1551 // the ability to instabuild
1552 if(this->abilities.instabuild) item = itemClone;
1553 if(item->interactEnemy(thisPlayer, dynamic_pointer_cast<LivingEntity>(entity)))
1554 {
1555 // 4J - PC Comments
1556 // Don't remove the item in hand if the player has the ability
1557 // to instabuild
1558 if ( (item->count <= 0) && !abilities.instabuild)
1559 {
1560 removeSelectedItem();
1561 }
1562 return true;
1563 }
1564 }
1565 return false;
1566}
1567
1568shared_ptr<ItemInstance> Player::getSelectedItem()
1569{
1570 return inventory->getSelected();
1571}
1572
1573void Player::removeSelectedItem()
1574{
1575 inventory->setItem(inventory->selected, nullptr);
1576}
1577
1578double Player::getRidingHeight()
1579{
1580 return heightOffset - 0.5f;
1581}
1582
1583void Player::attack(shared_ptr<Entity> entity)
1584{
1585 if (!entity->isAttackable())
1586 {
1587 return;
1588 }
1589
1590 if (entity->skipAttackInteraction(shared_from_this()))
1591 {
1592 return;
1593 }
1594
1595 float dmg = (float) getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->getValue();
1596
1597 int knockback = 0;
1598 float magicBoost = 0;
1599
1600 if ( entity->instanceof(eTYPE_LIVINGENTITY) )
1601 {
1602 shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
1603 shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(entity);
1604 magicBoost = EnchantmentHelper::getDamageBonus(thisPlayer, mob);
1605 knockback += EnchantmentHelper::getKnockbackBonus(thisPlayer, mob);
1606 }
1607 if (isSprinting())
1608 {
1609 knockback += 1;
1610 }
1611
1612 if (dmg > 0 || magicBoost > 0)
1613 {
1614 bool bCrit = fallDistance > 0 && !onGround && !onLadder() && !isInWater() && !hasEffect(MobEffect::blindness) && (riding == NULL) && entity->instanceof(eTYPE_LIVINGENTITY);
1615 if (bCrit && dmg > 0)
1616 {
1617 dmg *= 1.5f;
1618 }
1619 dmg += magicBoost;
1620
1621 // Ensure we put the entity on fire if we're hitting with a
1622 // fire-enchanted weapon
1623 bool setOnFireTemporatily = false;
1624 int fireAspect = EnchantmentHelper::getFireAspect(dynamic_pointer_cast<LivingEntity>(shared_from_this()));
1625 if ( entity->instanceof(eTYPE_MOB) && fireAspect > 0 && !entity->isOnFire())
1626 {
1627 setOnFireTemporatily = true;
1628 entity->setOnFire(1);
1629 }
1630
1631 DamageSource *damageSource = DamageSource::playerAttack(dynamic_pointer_cast<Player>(shared_from_this()));
1632 bool wasHurt = entity->hurt(damageSource, dmg);
1633 delete damageSource;
1634 if (wasHurt)
1635 {
1636 if (knockback > 0)
1637 {
1638 entity->push(-Mth::sin(yRot * PI / 180) * knockback * .5f, 0.1, Mth::cos(yRot * PI / 180) * knockback * .5f);
1639 xd *= 0.6;
1640 zd *= 0.6;
1641 setSprinting(false);
1642 }
1643
1644 if (bCrit)
1645 {
1646 crit(entity);
1647 }
1648 if (magicBoost > 0)
1649 {
1650 magicCrit(entity);
1651 }
1652
1653 if (dmg >= 18)
1654 {
1655 awardStat(GenericStats::overkill(),GenericStats::param_overkill(dmg));
1656 }
1657 setLastHurtMob(entity);
1658
1659
1660 if ( entity->instanceof(eTYPE_LIVINGENTITY) )
1661 {
1662 shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(entity);
1663 ThornsEnchantment::doThornsAfterAttack(shared_from_this(), mob, random);
1664 }
1665 }
1666
1667 shared_ptr<ItemInstance> item = getSelectedItem();
1668 shared_ptr<Entity> hurtTarget = entity;
1669 if ( entity->instanceof(eTYPE_MULTIENTITY_MOB_PART) )
1670 {
1671 shared_ptr<Entity> multiMob = dynamic_pointer_cast<Entity>((dynamic_pointer_cast<MultiEntityMobPart>(entity))->parentMob.lock());
1672 if ( (multiMob != NULL) && multiMob->instanceof(eTYPE_LIVINGENTITY) )
1673 {
1674 hurtTarget = dynamic_pointer_cast<LivingEntity>( multiMob );
1675 }
1676 }
1677 if ( (item != NULL) && hurtTarget->instanceof(eTYPE_LIVINGENTITY) )
1678 {
1679 item->hurtEnemy(dynamic_pointer_cast<LivingEntity>(hurtTarget), dynamic_pointer_cast<Player>( shared_from_this() ) );
1680 if (item->count <= 0)
1681 {
1682 removeSelectedItem();
1683 }
1684 }
1685 if ( entity->instanceof(eTYPE_LIVINGENTITY) )
1686 {
1687 //awardStat(Stats.damageDealt, (int) Math.round(dmg * 10));
1688
1689 if (fireAspect > 0 && wasHurt)
1690 {
1691 entity->setOnFire(fireAspect * 4);
1692 }
1693 else if (setOnFireTemporatily)
1694 {
1695 entity->clearFire();
1696 }
1697 }
1698
1699 causeFoodExhaustion(FoodConstants::EXHAUSTION_ATTACK);
1700 }
1701
1702 // if (SharedConstants::INGAME_DEBUG_OUTPUT)
1703 // {
1704 // //sendMessage(ChatMessageComponent.forPlainText("DMG " + dmg + ", " + magicBoost + ", " + knockback));
1705 // }
1706}
1707
1708void Player::crit(shared_ptr<Entity> entity)
1709{
1710}
1711
1712void Player::magicCrit(shared_ptr<Entity> entity)
1713{
1714}
1715
1716void Player::respawn()
1717{
1718 deathFadeCounter=0;
1719}
1720
1721
1722void Player::animateRespawn(shared_ptr<Player> player, Level *level)
1723{
1724
1725 for (int i = 0; i < 45; i++)
1726 {
1727 float angle = i * PI * 4.0f / 25.0f;
1728 float xo = Mth::cos(angle) * 0.7f;
1729 float zo = Mth::sin(angle) * 0.7f;
1730
1731 level->addParticle(eParticleType_netherportal, player->x + xo, player->y - player->heightOffset + 1.62f - i * .05f, player->z + zo, 0, 0, 0);
1732 }
1733
1734}
1735
1736Slot *Player::getInventorySlot(int slotId)
1737{
1738 return NULL;
1739}
1740
1741void Player::remove()
1742{
1743 LivingEntity::remove();
1744 inventoryMenu->removed( dynamic_pointer_cast<Player>( shared_from_this() ) );
1745 if (containerMenu != NULL)
1746 {
1747 containerMenu->removed( dynamic_pointer_cast<Player>( shared_from_this() ) );
1748 }
1749}
1750
1751bool Player::isInWall()
1752{
1753 return !m_isSleeping && LivingEntity::isInWall();
1754}
1755
1756bool Player::isLocalPlayer()
1757{
1758 return false;
1759}
1760
1761Player::BedSleepingResult Player::startSleepInBed(int x, int y, int z, bool bTestUse)
1762{
1763 if (!level->isClientSide || bTestUse)
1764 {
1765 if (isSleeping() || !isAlive())
1766 {
1767 return OTHER_PROBLEM;
1768 }
1769
1770 if (!level->dimension->isNaturalDimension())
1771 {
1772 // may not sleep in this dimension
1773 return NOT_POSSIBLE_HERE;
1774 }
1775
1776 // 4J-PB - I'm going to move the position of these tests below
1777 // The distance check should be before the day check, otherwise you can use the bed in daytime from far away
1778 // and you'll get the message about only sleeping at night
1779
1780 if (abs(this->x - x) > 3 || abs(this->y - y) > 2 || abs(this->z - z) > 3)
1781 {
1782 // too far away
1783 return TOO_FAR_AWAY;
1784 }
1785
1786 if (!bTestUse)
1787 {
1788 // 4J-PB - We still want the tooltip for Sleep
1789
1790 double hRange = 8;
1791 double vRange = 5;
1792 vector<shared_ptr<Entity> > *monsters = level->getEntitiesOfClass(typeid(Monster), AABB::newTemp(x - hRange, y - vRange, z - hRange, x + hRange, y + vRange, z + hRange));
1793 if (!monsters->empty())
1794 {
1795 delete monsters;
1796 return NOT_SAFE;
1797 }
1798 delete monsters;
1799 }
1800
1801 // This causes a message to be displayed, so we do want to show the tooltip in test mode
1802 if (!bTestUse && level->isDay())
1803 {
1804 // may not sleep during day
1805 return NOT_POSSIBLE_NOW;
1806 }
1807 }
1808
1809 if(bTestUse)
1810 {
1811 // 4J-PB - we're just testing use, and we get here, then the bed can be used
1812 return OK;
1813 }
1814
1815 if (isRiding())
1816 {
1817 ride(nullptr);
1818 }
1819
1820 setSize(0.2f, 0.2f);
1821 heightOffset = .2f;
1822 if (level->hasChunkAt(x, y, z))
1823 {
1824
1825
1826 int data = level->getData(x, y, z);
1827 int direction = BedTile::getDirection(data);
1828 float xo = .5f, zo = .5f;
1829
1830 switch (direction)
1831 {
1832 case Direction::SOUTH:
1833 zo = .9f;
1834 break;
1835 case Direction::NORTH:
1836 zo = .1f;
1837 break;
1838 case Direction::WEST:
1839 xo = .1f;
1840 break;
1841 case Direction::EAST:
1842 xo = .9f;
1843 break;
1844 }
1845 setBedOffset(direction);
1846 setPos(x + xo, y + 15.0f / 16.0f, z + zo);
1847 }
1848 else
1849 {
1850 setPos(x + .5f, y + 15.0f / 16.0f, z + .5f);
1851 }
1852 m_isSleeping = true;
1853 sleepCounter = 0;
1854 bedPosition = new Pos(x, y, z);
1855 xd = zd = yd = 0;
1856
1857 if (!level->isClientSide)
1858 {
1859 level->updateSleepingPlayerList();
1860 }
1861
1862 return OK;
1863}
1864
1865
1866void Player::setBedOffset(int bedDirection)
1867{
1868 // place position on pillow and feet at bottom
1869 bedOffsetX = 0;
1870 bedOffsetZ = 0;
1871
1872 switch (bedDirection)
1873 {
1874 case Direction::SOUTH:
1875 bedOffsetZ = -1.8f;
1876 break;
1877 case Direction::NORTH:
1878 bedOffsetZ = 1.8f;
1879 break;
1880 case Direction::WEST:
1881 bedOffsetX = 1.8f;
1882 break;
1883 case Direction::EAST:
1884 bedOffsetX = -1.8f;
1885 break;
1886 }
1887}
1888
1889
1890/**
1891*
1892* @param forcefulWakeUp
1893* If the player has been forced to wake up. When this happens,
1894* the client will skip the wake-up animation. For example, when
1895* the player is hurt or the bed is destroyed.
1896* @param updateLevelList
1897* If the level's sleeping player list needs to be updated. This
1898* is usually the case.
1899* @param saveRespawnPoint
1900* TODO
1901*/
1902void Player::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint)
1903{
1904
1905 setSize(0.6f, 1.8f);
1906 setDefaultHeadHeight();
1907
1908 Pos *pos = bedPosition;
1909 Pos *standUp = bedPosition;
1910 if (pos != NULL && level->getTile(pos->x, pos->y, pos->z) == Tile::bed_Id)
1911 {
1912 BedTile::setOccupied(level, pos->x, pos->y, pos->z, false);
1913
1914 standUp = BedTile::findStandUpPosition(level, pos->x, pos->y, pos->z, 0);
1915 if (standUp == NULL)
1916 {
1917 standUp = new Pos(pos->x, pos->y + 1, pos->z);
1918 }
1919 setPos(standUp->x + .5f, standUp->y + heightOffset + .1f, standUp->z + .5f);
1920 }
1921
1922 m_isSleeping = false;
1923 if (!level->isClientSide && updateLevelList)
1924 {
1925 level->updateSleepingPlayerList();
1926 }
1927 if (forcefulWakeUp)
1928 {
1929 sleepCounter = 0;
1930 }
1931 else
1932 {
1933 sleepCounter = SLEEP_DURATION;
1934 }
1935 if (saveRespawnPoint)
1936 {
1937 setRespawnPosition(bedPosition, false);
1938 }
1939}
1940
1941
1942bool Player::checkBed()
1943{
1944 return (level->getTile(bedPosition->x, bedPosition->y, bedPosition->z) == Tile::bed_Id);
1945}
1946
1947
1948Pos *Player::checkBedValidRespawnPosition(Level *level, Pos *pos, bool forced)
1949{
1950 // make sure the chunks around the bed exist
1951 ChunkSource *chunkSource = level->getChunkSource();
1952 chunkSource->create((pos->x - 3) >> 4, (pos->z - 3) >> 4);
1953 chunkSource->create((pos->x + 3) >> 4, (pos->z - 3) >> 4);
1954 chunkSource->create((pos->x - 3) >> 4, (pos->z + 3) >> 4);
1955 chunkSource->create((pos->x + 3) >> 4, (pos->z + 3) >> 4);
1956
1957 // make sure the bed is still standing
1958 if (level->getTile(pos->x, pos->y, pos->z) != Tile::bed_Id)
1959 {
1960 Material *bottomMaterial = level->getMaterial(pos->x, pos->y, pos->z);
1961 Material *topMaterial = level->getMaterial(pos->x, pos->y + 1, pos->z);
1962 bool freeFeet = !bottomMaterial->isSolid() && !bottomMaterial->isLiquid();
1963 bool freeHead = !topMaterial->isSolid() && !topMaterial->isLiquid();
1964
1965 if (forced && freeFeet && freeHead)
1966 {
1967 return pos;
1968 }
1969 return NULL;
1970 }
1971 // make sure the bed still has a stand-up position
1972 Pos *standUp = BedTile::findStandUpPosition(level, pos->x, pos->y, pos->z, 0);
1973 return standUp;
1974}
1975
1976float Player::getSleepRotation()
1977{
1978 if (bedPosition != NULL)
1979 {
1980 int data = level->getData(bedPosition->x, bedPosition->y, bedPosition->z);
1981 int direction = BedTile::getDirection(data);
1982
1983 switch (direction)
1984 {
1985 case Direction::SOUTH:
1986 return 90;
1987 case Direction::WEST:
1988 return 0;
1989 case Direction::NORTH:
1990 return 270;
1991 case Direction::EAST:
1992 return 180;
1993 }
1994 }
1995 return 0;
1996}
1997
1998bool Player::isSleeping()
1999{
2000 return m_isSleeping;
2001}
2002
2003bool Player::isSleepingLongEnough()
2004{
2005 return m_isSleeping && sleepCounter >= SLEEP_DURATION;
2006}
2007
2008int Player::getSleepTimer()
2009{
2010 return sleepCounter;
2011}
2012
2013// 4J-PB - added for death fade
2014int Player::getDeathFadeTimer()
2015{
2016 return deathFadeCounter;
2017}
2018
2019bool Player::getPlayerFlag(int flag)
2020{
2021 return (entityData->getByte(DATA_PLAYER_FLAGS_ID) & (1 << flag)) != 0;
2022}
2023
2024void Player::setPlayerFlag(int flag, bool value)
2025{
2026 byte currentValue = entityData->getByte(DATA_PLAYER_FLAGS_ID);
2027 if (value)
2028 {
2029 entityData->set(DATA_PLAYER_FLAGS_ID, (byte) (currentValue | (1 << flag)));
2030 }
2031 else
2032 {
2033 entityData->set(DATA_PLAYER_FLAGS_ID, (byte) (currentValue & ~(1 << flag)));
2034 }
2035}
2036
2037
2038/**
2039* This method is currently only relevant to client-side players. It will
2040* try to load the messageId from the language file and display it to the
2041* client.
2042*/
2043void Player::displayClientMessage(int messageId)
2044{
2045
2046}
2047
2048Pos *Player::getRespawnPosition()
2049{
2050 return respawnPosition;
2051}
2052
2053bool Player::isRespawnForced()
2054{
2055 return respawnForced;
2056}
2057
2058void Player::setRespawnPosition(Pos *respawnPosition, bool forced)
2059{
2060 if (respawnPosition != NULL)
2061 {
2062 this->respawnPosition = new Pos(*respawnPosition);
2063 respawnForced = forced;
2064 }
2065 else
2066 {
2067 this->respawnPosition = NULL;
2068 respawnForced = false;
2069 }
2070}
2071
2072void Player::awardStat(Stat *stat, byteArray paramBlob)
2073{
2074 if (paramBlob.data != NULL)
2075 {
2076 delete [] paramBlob.data;
2077 }
2078}
2079
2080
2081void Player::jumpFromGround()
2082{
2083 LivingEntity::jumpFromGround();
2084
2085 // 4J Stu - This seems to have been missed from 1.7.3, but do we care?
2086 //awardStat(Stats::jump, 1);
2087
2088 if (isSprinting())
2089 {
2090 causeFoodExhaustion(FoodConstants::EXHAUSTION_SPRINT_JUMP);
2091 }
2092 else
2093 {
2094 causeFoodExhaustion(FoodConstants::EXHAUSTION_JUMP);
2095 }
2096}
2097
2098
2099void Player::travel(float xa, float ya)
2100{
2101 double preX = x, preY = y, preZ = z;
2102
2103 if (abilities.flying && riding == NULL)
2104 {
2105 double ydo = yd;
2106 float ofs = flyingSpeed;
2107 flyingSpeed = abilities.getFlyingSpeed();
2108 LivingEntity::travel(xa, ya);
2109 yd = ydo * 0.6;
2110 flyingSpeed = ofs;
2111 }
2112 else
2113 {
2114 LivingEntity::travel(xa, ya);
2115 }
2116
2117 checkMovementStatistiscs(x - preX, y - preY, z - preZ);
2118}
2119
2120float Player::getSpeed()
2121{
2122 return (float) getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->getValue();
2123}
2124
2125void Player::checkMovementStatistiscs(double dx, double dy, double dz)
2126{
2127
2128 if (riding != NULL)
2129 {
2130 return;
2131 }
2132 if (isUnderLiquid(Material::water))
2133 {
2134 int distance = (int) Math::round(sqrt(dx * dx + dy * dy + dz * dz) * 100.0f);
2135 if (distance > 0)
2136 {
2137 //awardStat(Stats::diveOneCm, distance);
2138 causeFoodExhaustion(FoodConstants::EXHAUSTION_SWIM * distance * .01f);
2139 }
2140 }
2141 else if (isInWater())
2142 {
2143 int horizontalDistance = (int) Math::round(sqrt(dx * dx + dz * dz) * 100.0f);
2144 if (horizontalDistance > 0)
2145 {
2146 distanceSwim += horizontalDistance;
2147 if( distanceSwim >= 100 )
2148 {
2149 int newDistance = distanceSwim - (distanceSwim % 100);
2150 distanceSwim -= newDistance;
2151 awardStat( GenericStats::swimOneM(), GenericStats::param_swim(newDistance/100) );
2152 }
2153 causeFoodExhaustion(FoodConstants::EXHAUSTION_SWIM * horizontalDistance * .01f);
2154 }
2155 }
2156 else if (onLadder())
2157 {
2158 if (dy > 0)
2159 {
2160 distanceClimb += (int) Math::round(dy * 100.0f);
2161 if( distanceClimb >= 100 )
2162 {
2163 int newDistance = distanceClimb - (distanceClimb % 100);
2164 distanceClimb -= newDistance;
2165 awardStat( GenericStats::climbOneM(), GenericStats::param_climb(newDistance/100) );
2166 }
2167 }
2168 }
2169 else if (onGround)
2170 {
2171 int horizontalDistance = (int) Math::round(sqrt(dx * dx + dz * dz) * 100.0f);
2172 if (horizontalDistance > 0)
2173 {
2174 distanceWalk += horizontalDistance;
2175 if( distanceWalk >= 100 )
2176 {
2177 int newDistance = distanceWalk - (distanceWalk % 100);
2178 distanceWalk -= newDistance;
2179 awardStat( GenericStats::walkOneM(), GenericStats::param_walk(newDistance/100) );
2180 }
2181 if (isSprinting())
2182 {
2183 causeFoodExhaustion(FoodConstants::EXHAUSTION_SPRINT * horizontalDistance * .01f);
2184 }
2185 else
2186 {
2187 causeFoodExhaustion(FoodConstants::EXHAUSTION_WALK * horizontalDistance * .01f);
2188 }
2189 }
2190 }
2191}
2192
2193
2194void Player::checkRidingStatistiscs(double dx, double dy, double dz)
2195{
2196 if (riding != NULL)
2197 {
2198 int distance = (int) Math::round(sqrt(dx * dx + dy * dy + dz * dz) * 100.0f);
2199 if (distance > 0)
2200 {
2201 if ( riding->instanceof(eTYPE_MINECART) )
2202 {
2203 distanceMinecart += distance;
2204 if( distanceMinecart >= 100 )
2205 {
2206 int newDistance = distanceMinecart - (distanceMinecart % 100);
2207 distanceMinecart -= newDistance;
2208 awardStat( GenericStats::minecartOneM(), GenericStats::param_minecart(newDistance/100) );
2209 }
2210
2211 int dist = 0;
2212 if (minecartAchievementPos == NULL)
2213 {
2214 minecartAchievementPos = new Pos(Mth::floor(x), Mth::floor(y), Mth::floor(z));
2215 }
2216 // 4J-PB - changed this because our world isn't big enough to go 1000m
2217 else
2218 {
2219 // 4-JEV, changed slightly to add extra parameters for event on durango.
2220 int dist = minecartAchievementPos->dist(Mth::floor(x), Mth::floor(y), Mth::floor(z));
2221#ifdef _XBOX_ONE
2222 // 4J-PB - send the event to cause the progress bar to increase on XB1
2223 if (m_bAwardedOnARail==false)
2224 {
2225 if(dist < 500)
2226 {
2227 if((dist>0) && (dist%100==0))
2228 {
2229 awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist));
2230 }
2231 }
2232 else
2233 {
2234 awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist));
2235 m_bAwardedOnARail=true;
2236 }
2237 }
2238#else
2239 if ((m_bAwardedOnARail==false) && (dist >= 500))
2240 {
2241 awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist));
2242 m_bAwardedOnARail=true;
2243 }
2244#endif
2245 }
2246
2247 }
2248 else if ( riding->instanceof(eTYPE_BOAT) )
2249 {
2250 distanceBoat += distance;
2251 if( distanceBoat >= 100 )
2252 {
2253 int newDistance = distanceBoat - (distanceBoat % 100);
2254 distanceBoat -= newDistance;
2255 awardStat(GenericStats::boatOneM(), GenericStats::param_boat(newDistance/100) );
2256 }
2257 }
2258 else if ( riding->instanceof(eTYPE_PIG) )
2259 {
2260 distancePig += distance;
2261 if( distancePig >= 100 )
2262 {
2263 int newDistance = distancePig - (distancePig % 100);
2264 distancePig -= newDistance;
2265 awardStat(GenericStats::pigOneM(), GenericStats::param_pig(newDistance/100) );
2266 }
2267 }
2268 }
2269 }
2270}
2271
2272
2273void Player::causeFallDamage(float distance)
2274{
2275 if (abilities.mayfly) return;
2276
2277 if (distance >= 2)
2278 {
2279 distanceFall += (int) Math::round(distance * 100.0);
2280 if( distanceFall >= 100 )
2281 {
2282 int newDistance = distanceFall - (distanceFall % 100);
2283 distanceFall -= newDistance;
2284 awardStat(GenericStats::fallOneM(), GenericStats::param_fall(newDistance/100) );
2285 }
2286 }
2287 LivingEntity::causeFallDamage(distance);
2288}
2289
2290
2291void Player::killed(shared_ptr<LivingEntity> mob)
2292{
2293 // 4J-PB - added the lavaslime enemy - fix for #64007 - TU7: Code: Achievements: TCR#073: Killing Magma Cubes doesn't unlock "Monster Hunter" Achievement.
2294 if( mob->instanceof(eTYPE_ENEMY) || mob->GetType() == eTYPE_GHAST || mob->GetType() == eTYPE_SLIME || mob->GetType() == eTYPE_LAVASLIME || mob->GetType() == eTYPE_ENDERDRAGON)
2295 {
2296 awardStat(GenericStats::killEnemy(), GenericStats::param_noArgs());
2297
2298 switch( mob->GetType() )
2299 {
2300 case eTYPE_CREEPER:
2301 awardStat(GenericStats::killsCreeper(), GenericStats::param_noArgs());
2302 break;
2303 case eTYPE_SKELETON:
2304 if( mob->isRiding() && mob->riding->GetType() == eTYPE_SPIDER )
2305 awardStat(GenericStats::killsSpiderJockey(), GenericStats::param_noArgs());
2306 else
2307 awardStat(GenericStats::killsSkeleton(), GenericStats::param_noArgs());
2308 break;
2309 case eTYPE_SPIDER:
2310 if( mob->rider.lock() != NULL && mob->rider.lock()->GetType() == eTYPE_SKELETON )
2311 awardStat(GenericStats::killsSpiderJockey(), GenericStats::param_noArgs());
2312 else
2313 awardStat(GenericStats::killsSpider(), GenericStats::param_noArgs());
2314 break;
2315 case eTYPE_ZOMBIE:
2316 awardStat(GenericStats::killsZombie(), GenericStats::param_noArgs());
2317 break;
2318 case eTYPE_PIGZOMBIE:
2319 if( level->dimension->id == 0 )
2320 awardStat(GenericStats::killsZombiePigman(), GenericStats::param_noArgs());
2321 else
2322 awardStat(GenericStats::killsNetherZombiePigman(), GenericStats::param_noArgs());
2323 break;
2324 case eTYPE_GHAST:
2325 awardStat(GenericStats::killsGhast(), GenericStats::param_noArgs());
2326 break;
2327 case eTYPE_SLIME:
2328 awardStat(GenericStats::killsSlime(), GenericStats::param_noArgs());
2329 break;
2330 case eTYPE_ENDERDRAGON:
2331 awardStat(GenericStats::killsEnderdragon(), GenericStats::param_noArgs());
2332 break;
2333 }
2334 }
2335 else if( mob->GetType() == eTYPE_COW )
2336 {
2337 awardStat(GenericStats::killCow(), GenericStats::param_noArgs());
2338 }
2339}
2340
2341void Player::makeStuckInWeb()
2342{
2343 if (!abilities.flying) LivingEntity::makeStuckInWeb();
2344}
2345
2346Icon *Player::getItemInHandIcon(shared_ptr<ItemInstance> item, int layer)
2347{
2348 Icon *icon = LivingEntity::getItemInHandIcon(item, layer);
2349 if (item->id == Item::fishingRod->id && fishing != NULL)
2350 {
2351 icon = Item::fishingRod->getEmptyIcon();
2352 }
2353 else if (item->getItem()->hasMultipleSpriteLayers())
2354 {
2355 return item->getItem()->getLayerIcon(item->getAuxValue(), layer);
2356 }
2357 else if (useItem != NULL && item->id == Item::bow_Id)
2358 {
2359 int ticksHeld = (item->getUseDuration() - useItemDuration);
2360 if (ticksHeld >= BowItem::MAX_DRAW_DURATION - 2)
2361 {
2362 return Item::bow->getDrawnIcon(2);
2363 }
2364 if (ticksHeld > (2 * BowItem::MAX_DRAW_DURATION) / 3)
2365 {
2366 return Item::bow->getDrawnIcon(1);
2367 }
2368 if (ticksHeld > 0)
2369 {
2370 return Item::bow->getDrawnIcon(0);
2371 }
2372 }
2373 return icon;
2374}
2375
2376shared_ptr<ItemInstance> Player::getArmor(int pos)
2377{
2378 return inventory->getArmor(pos);
2379}
2380
2381void Player::increaseXp(int i)
2382{
2383 increaseScore(i);
2384 int max = INT_MAX - totalExperience;
2385 if (i > max)
2386 {
2387 i = max;
2388 }
2389 experienceProgress += (float) i / getXpNeededForNextLevel();
2390 totalExperience += i;
2391 while (experienceProgress >= 1)
2392 {
2393 experienceProgress = (experienceProgress - 1) * getXpNeededForNextLevel();
2394 giveExperienceLevels(1);
2395 experienceProgress /= getXpNeededForNextLevel();
2396 }
2397}
2398
2399void Player::giveExperienceLevels(int amount)
2400{
2401 experienceLevel += amount;
2402 if (experienceLevel < 0)
2403 {
2404 experienceLevel = 0;
2405 experienceProgress = 0;
2406 totalExperience = 0;
2407 }
2408
2409 if (amount > 0 && experienceLevel % 5 == 0 && lastLevelUpTime < tickCount - SharedConstants::TICKS_PER_SECOND * 5.0f)
2410 {
2411 float vol = experienceLevel > 30 ? 1 : experienceLevel / 30.0f;
2412 level->playEntitySound(shared_from_this(), eSoundType_RANDOM_LEVELUP, vol * 0.75f, 1);
2413 lastLevelUpTime = tickCount;
2414 }
2415}
2416
2417int Player::getXpNeededForNextLevel()
2418{
2419 // Update xp calculations from 1.3
2420 if (experienceLevel >= 30)
2421 {
2422 return 17 + 15 * 3 + (experienceLevel - 30) * 7;
2423 }
2424 if (experienceLevel >= 15)
2425 {
2426 return 17 + (experienceLevel - 15) * 3;
2427 }
2428 return 17;
2429}
2430
2431/**
2432* This method adds on to the player's exhaustion, which may decrease the
2433* player's food level.
2434*
2435* @param amount
2436* Amount of exhaustion to add, between 0 and 20 (setting it to
2437* 20 will guarantee that at least 1, and at most 4, food points
2438* are deducted). See FoodConstants for cost suggestions.
2439*/
2440void Player::causeFoodExhaustion(float amount)
2441{
2442 if( isAllowedToIgnoreExhaustion() || ( isAllowedToFly() && abilities.flying) ) return;
2443 if (abilities.invulnerable || hasInvulnerablePrivilege() ) return;
2444
2445 // 4J Stu - Added 1.8.2 bug fix (TU6) - If players cannot eat, then their food bar should not decrease due to exhaustion
2446 if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0) return;
2447
2448 if (!level->isClientSide)
2449 {
2450 foodData.addExhaustion(amount);
2451 }
2452}
2453
2454FoodData *Player::getFoodData()
2455{
2456 return &foodData;
2457}
2458
2459bool Player::canEat(bool magicalItem)
2460{
2461 return (magicalItem || foodData.needsFood()) && !abilities.invulnerable && !hasInvulnerablePrivilege();
2462}
2463
2464bool Player::isHurt()
2465{
2466 return getHealth() > 0 && getHealth() < getMaxHealth();
2467}
2468
2469void Player::startUsingItem(shared_ptr<ItemInstance> instance, int duration)
2470{
2471 if (instance == useItem) return;
2472 useItem = instance;
2473 useItemDuration = duration;
2474 if (!level->isClientSide)
2475 {
2476 setUsingItemFlag(true);
2477 }
2478
2479 // 4J-JEV, hook for ItemUsed event, and ironbelly achievement.
2480 awardStat(GenericStats::itemsUsed(instance->getItem()->id),
2481 GenericStats::param_itemsUsed(dynamic_pointer_cast<Player>(shared_from_this()),instance));
2482
2483#if (!defined _DURANGO) && (defined _EXTENDED_ACHIEVEMENTS)
2484 if ( (instance->getItem()->id == Item::rotten_flesh_Id) && (getFoodData()->getFoodLevel() == 0) )
2485 awardStat(GenericStats::ironBelly(), GenericStats::param_ironBelly());
2486#endif
2487}
2488
2489bool Player::mayDestroyBlockAt(int x, int y, int z)
2490{
2491 if (abilities.mayBuild)
2492 {
2493 return true;
2494 }
2495 int t = level->getTile(x, y, z);
2496 if (t > 0) {
2497 Tile *tile = Tile::tiles[t];
2498
2499 if (tile->material->isDestroyedByHand())
2500 {
2501 return true;
2502 }
2503 else if (getSelectedItem() != NULL)
2504 {
2505 shared_ptr<ItemInstance> carried = getSelectedItem();
2506
2507 if (carried->canDestroySpecial(tile) || carried->getDestroySpeed(tile) > 1)
2508 {
2509 return true;
2510 }
2511 }
2512 }
2513 return false;
2514}
2515
2516bool Player::mayUseItemAt(int x, int y, int z, int face, shared_ptr<ItemInstance> item)
2517{
2518 if (abilities.mayBuild)
2519 {
2520 return true;
2521 }
2522 if (item != NULL)
2523 {
2524 return item->mayBePlacedInAdventureMode();
2525 }
2526 return false;
2527}
2528
2529int Player::getExperienceReward(shared_ptr<Player> killedBy)
2530{
2531 if (level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY)) return 0;
2532 int reward = experienceLevel * 7;
2533 if (reward > 100)
2534 {
2535 return 100;
2536 }
2537 return reward;
2538}
2539
2540bool Player::isAlwaysExperienceDropper()
2541{
2542 // players always drop experience
2543 return true;
2544}
2545
2546wstring Player::getAName()
2547{
2548 return name;
2549}
2550
2551bool Player::shouldShowName()
2552{
2553 return true;
2554}
2555
2556void Player::restoreFrom(shared_ptr<Player> oldPlayer, bool restoreAll)
2557{
2558 if(restoreAll)
2559 {
2560 inventory->replaceWith(oldPlayer->inventory);
2561
2562 setHealth(oldPlayer->getHealth());
2563 foodData = oldPlayer->foodData;
2564
2565 experienceLevel = oldPlayer->experienceLevel;
2566 totalExperience = oldPlayer->totalExperience;
2567 experienceProgress = oldPlayer->experienceProgress;
2568
2569 setScore(oldPlayer->getScore());
2570 portalEntranceDir = oldPlayer->portalEntranceDir;
2571 }
2572 else if (level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
2573 {
2574 inventory->replaceWith(oldPlayer->inventory);
2575
2576 experienceLevel = oldPlayer->experienceLevel;
2577 totalExperience = oldPlayer->totalExperience;
2578 experienceProgress = oldPlayer->experienceProgress;
2579 setScore(oldPlayer->getScore());
2580 }
2581 enderChestInventory = oldPlayer->enderChestInventory;
2582}
2583
2584bool Player::makeStepSound()
2585{
2586 return !abilities.flying;
2587}
2588
2589void Player::onUpdateAbilities()
2590{
2591}
2592
2593void Player::setGameMode(GameType *mode)
2594{
2595}
2596
2597wstring Player::getName()
2598{
2599 return name;
2600}
2601
2602wstring Player::getDisplayName()
2603{
2604 //PlayerTeam.formatNameForTeam(getTeam(), name);
2605
2606 // If player display name is not set, return name
2607 return m_displayName.size() > 0 ? m_displayName : name;
2608}
2609
2610wstring Player::getNetworkName()
2611{
2612 // 4J: We can only transmit gamertag in network packets
2613 return name;
2614}
2615
2616Level *Player::getCommandSenderWorld()
2617{
2618 return level;
2619}
2620
2621shared_ptr<PlayerEnderChestContainer> Player::getEnderChestInventory()
2622{
2623 return enderChestInventory;
2624}
2625
2626shared_ptr<ItemInstance> Player::getCarried(int slot)
2627{
2628 if (slot == 0) return inventory->getSelected();
2629 return inventory->armor[slot - 1];
2630}
2631
2632shared_ptr<ItemInstance> Player::getCarriedItem()
2633{
2634 return inventory->getSelected();
2635}
2636
2637void Player::setEquippedSlot(int slot, shared_ptr<ItemInstance> item)
2638{
2639 inventory->armor[slot] = item;
2640}
2641
2642bool Player::isInvisibleTo(shared_ptr<Player> player)
2643{
2644 return isInvisible();
2645}
2646
2647ItemInstanceArray Player::getEquipmentSlots()
2648{
2649 return inventory->armor;
2650}
2651
2652bool Player::isCapeHidden()
2653{
2654 return getPlayerFlag(FLAG_HIDE_CAPE);
2655}
2656
2657bool Player::isPushedByWater()
2658{
2659 return !abilities.flying;
2660}
2661
2662Scoreboard *Player::getScoreboard()
2663{
2664 return level->getScoreboard();
2665}
2666
2667Team *Player::getTeam()
2668{
2669 return getScoreboard()->getPlayersTeam(name);
2670}
2671
2672void Player::setAbsorptionAmount(float absorptionAmount)
2673{
2674 if (absorptionAmount < 0) absorptionAmount = 0;
2675 getEntityData()->set(DATA_PLAYER_ABSORPTION_ID, absorptionAmount);
2676}
2677
2678float Player::getAbsorptionAmount()
2679{
2680 return getEntityData()->getFloat(DATA_PLAYER_ABSORPTION_ID);
2681}
2682
2683int Player::getTexture()
2684{
2685 switch(m_skinIndex)
2686 {
2687 case eDefaultSkins_Skin0:
2688 return TN_MOB_CHAR; // 4J - was L"/mob/char.png";
2689 case eDefaultSkins_Skin1:
2690 return TN_MOB_CHAR1; // 4J - was L"/mob/char1.png";
2691 case eDefaultSkins_Skin2:
2692 return TN_MOB_CHAR2; // 4J - was L"/mob/char2.png";
2693 case eDefaultSkins_Skin3:
2694 return TN_MOB_CHAR3; // 4J - was L"/mob/char3.png";
2695 case eDefaultSkins_Skin4:
2696 return TN_MOB_CHAR4; // 4J - was L"/mob/char4.png";
2697 case eDefaultSkins_Skin5:
2698 return TN_MOB_CHAR5; // 4J - was L"/mob/char5.png";
2699 case eDefaultSkins_Skin6:
2700 return TN_MOB_CHAR6; // 4J - was L"/mob/char6.png";
2701 case eDefaultSkins_Skin7:
2702 return TN_MOB_CHAR7; // 4J - was L"/mob/char7.png";
2703
2704 default:
2705 return TN_MOB_CHAR; // 4J - was L"/mob/char.png";
2706 }
2707}
2708
2709int Player::hash_fnct(const shared_ptr<Player> k)
2710{
2711 // TODO 4J Stu - Should we just be using the pointers and hashing them?
2712#ifdef __PS3__
2713 return (int)boost::hash_value( k->name ); // 4J Stu - Names are completely unique?
2714#else
2715 return (int)std::hash<wstring>{}(k->name); // 4J Stu - Names are completely unique?
2716#endif //__PS3__
2717}
2718
2719bool Player::eq_test(const shared_ptr<Player> x, const shared_ptr<Player> y)
2720{
2721 // TODO 4J Stu - Should we just be using the pointers and comparing them for equality?
2722 return x->name.compare( y->name ) == 0; // 4J Stu - Names are completely unique?
2723}
2724
2725
2726unsigned int Player::getPlayerGamePrivilege(EPlayerGamePrivileges privilege)
2727{
2728 return Player::getPlayerGamePrivilege(m_uiGamePrivileges,privilege);
2729}
2730
2731unsigned int Player::getPlayerGamePrivilege(unsigned int uiGamePrivileges, EPlayerGamePrivileges privilege)
2732{
2733 if( privilege == ePlayerGamePrivilege_All )
2734 {
2735 return uiGamePrivileges;
2736 }
2737 else if (privilege < ePlayerGamePrivilege_MAX )
2738 {
2739 return uiGamePrivileges&(1<<privilege);
2740 }
2741 return 0;
2742}
2743
2744void Player::setPlayerGamePrivilege(EPlayerGamePrivileges privilege, unsigned int value)
2745{
2746 Player::setPlayerGamePrivilege(m_uiGamePrivileges,privilege,value);
2747}
2748
2749void Player::setPlayerGamePrivilege(unsigned int &uiGamePrivileges, EPlayerGamePrivileges privilege, unsigned int value)
2750{
2751 if( privilege == ePlayerGamePrivilege_All )
2752 {
2753 uiGamePrivileges = value;
2754 }
2755 else if(privilege ==ePlayerGamePrivilege_HOST)
2756 {
2757 if(value == 0)
2758 {
2759 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Op,0);
2760 }
2761 else
2762 {
2763 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Op,1);
2764 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleInvisible,1);
2765 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleFly,1);
2766 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleClassicHunger,1);
2767 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanTeleport,1);
2768 }
2769 }
2770 else if (privilege < ePlayerGamePrivilege_MAX )
2771 {
2772 if(value!=0)
2773 {
2774 uiGamePrivileges|=(1<<privilege);
2775 }
2776 else
2777 {
2778 // Some privileges will turn other things off as well
2779 switch(privilege)
2780 {
2781 case ePlayerGamePrivilege_CanToggleInvisible:
2782 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Invisible,0);
2783 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Invulnerable,0);
2784 break;
2785 case ePlayerGamePrivilege_CanToggleFly:
2786 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanFly,0);
2787 break;
2788 case ePlayerGamePrivilege_CanToggleClassicHunger:
2789 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_ClassicHunger,0);
2790 break;
2791 case ePlayerGamePrivilege_Op:
2792 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleInvisible,0);
2793 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleFly,0);
2794 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleClassicHunger,0);
2795 Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanTeleport,0);
2796 break;
2797 }
2798 // off
2799 uiGamePrivileges&=~(1<<privilege);
2800 }
2801 }
2802}
2803
2804bool Player::isAllowedToUse(Tile *tile)
2805{
2806 bool allowed = true;
2807 if(tile != NULL && app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
2808 {
2809 allowed = false;
2810
2811 if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches) != 0)
2812 {
2813 switch(tile->id)
2814 {
2815 case Tile::door_wood_Id:
2816 case Tile::button_stone_Id:
2817 case Tile::button_wood_Id:
2818 case Tile::lever_Id:
2819 case Tile::fenceGate_Id:
2820 case Tile::trapdoor_Id:
2821 allowed = true;
2822 break;
2823 }
2824 }
2825
2826 if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers) != 0)
2827 {
2828 switch(tile->id)
2829 {
2830 case Tile::chest_Id:
2831 case Tile::furnace_Id:
2832 case Tile::furnace_lit_Id:
2833 case Tile::dispenser_Id:
2834 case Tile::brewingStand_Id:
2835 case Tile::enchantTable_Id:
2836 case Tile::workBench_Id:
2837 case Tile::anvil_Id:
2838 case Tile::enderChest_Id:
2839 allowed = true;
2840 break;
2841 }
2842 }
2843
2844 if(!allowed && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) == 0)
2845 {
2846 switch(tile->id)
2847 {
2848 case Tile::door_wood_Id:
2849 case Tile::button_stone_Id:
2850 case Tile::button_wood_Id:
2851 case Tile::lever_Id:
2852 case Tile::fenceGate_Id:
2853 case Tile::trapdoor_Id:
2854 case Tile::chest_Id:
2855 case Tile::furnace_Id:
2856 case Tile::furnace_lit_Id:
2857 case Tile::dispenser_Id:
2858 case Tile::brewingStand_Id:
2859 case Tile::enchantTable_Id:
2860 case Tile::workBench_Id:
2861 case Tile::anvil_Id:
2862 case Tile::enderChest_Id:
2863 allowed = false;
2864 break;
2865 default:
2866 allowed = true;
2867 break;
2868 }
2869 }
2870 }
2871
2872 return allowed;
2873}
2874
2875bool Player::isAllowedToUse(shared_ptr<ItemInstance> item)
2876{
2877 bool allowed = true;
2878 if(item != NULL && app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
2879 {
2880 if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0)
2881 {
2882 allowed = false;
2883 }
2884
2885 // 4J Stu - TU8 Players should always be able to eat food items, even if the build option is turned of
2886 switch(item->id)
2887 {
2888 // food
2889 case Item::mushroomStew_Id:
2890 case Item::apple_Id:
2891 case Item::bread_Id:
2892 case Item::porkChop_raw_Id:
2893 case Item::porkChop_cooked_Id:
2894 case Item::apple_gold_Id:
2895 case Item::fish_raw_Id:
2896 case Item::fish_cooked_Id:
2897 case Item::cookie_Id:
2898 case Item::beef_cooked_Id:
2899 case Item::beef_raw_Id:
2900 case Item::chicken_cooked_Id:
2901 case Item::chicken_raw_Id:
2902 case Item::melon_Id:
2903 case Item::rotten_flesh_Id:
2904 // bow
2905 case Item::bow_Id:
2906 case Item::sword_diamond_Id:
2907 case Item::sword_gold_Id:
2908 case Item::sword_iron_Id:
2909 case Item::sword_stone_Id:
2910 case Item::sword_wood_Id:
2911 allowed = true;
2912 break;
2913 }
2914 }
2915
2916 return allowed;
2917}
2918
2919bool Player::isAllowedToInteract(shared_ptr<Entity> target)
2920{
2921 bool allowed = true;
2922 if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
2923 {
2924 if (target->instanceof(eTYPE_MINECART))
2925 {
2926 if (getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers) == 0)
2927 {
2928 shared_ptr<Minecart> minecart = dynamic_pointer_cast<Minecart>( target );
2929 if (minecart->getType() == Minecart::TYPE_CHEST)
2930 allowed = false;
2931 }
2932
2933 }
2934 else
2935 {
2936 if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0)
2937 {
2938 allowed = false;
2939 }
2940
2941 if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine) != 0)
2942 {
2943 allowed = false;
2944 }
2945 }
2946 }
2947
2948 return allowed;
2949}
2950
2951bool Player::isAllowedToMine()
2952{
2953 bool allowed = true;
2954 if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
2955 {
2956 if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine) != 0)
2957 {
2958 allowed = false;
2959 }
2960 }
2961 return allowed;
2962}
2963
2964bool Player::isAllowedToAttackPlayers()
2965{
2966 bool allowed = true;
2967 if( hasInvisiblePrivilege() || ((app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackPlayers)) )
2968 {
2969 allowed = false;
2970 }
2971 return allowed;
2972}
2973
2974bool Player::isAllowedToAttackAnimals()
2975{
2976 bool allowed = true;
2977 if( (app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackAnimals) )
2978 {
2979 allowed = false;
2980 }
2981 return allowed;
2982}
2983
2984bool Player::isAllowedToHurtEntity(shared_ptr<Entity> target)
2985{
2986 bool allowed = true;
2987
2988 if(!isAllowedToMine())
2989 {
2990 switch(target->GetType())
2991 {
2992 case eTYPE_HANGING_ENTITY:
2993 case eTYPE_PAINTING:
2994 case eTYPE_ITEM_FRAME:
2995
2996 // 4J-JEV: Fix for #88212,
2997 // Untrusted players shouldn't be able to damage minecarts or boats.
2998 case eTYPE_BOAT:
2999 case eTYPE_MINECART:
3000
3001 allowed = false;
3002 break;
3003 };
3004 }
3005 return allowed;
3006}
3007
3008bool Player::isAllowedToFly()
3009{
3010 bool allowed = false;
3011 if(app.GetGameHostOption(eGameHostOption_HostCanFly) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanFly) != 0)
3012 {
3013 allowed = true;
3014 }
3015 return allowed;
3016}
3017
3018bool Player::isAllowedToIgnoreExhaustion()
3019{
3020 bool allowed = false;
3021 if( (app.GetGameHostOption(eGameHostOption_HostCanChangeHunger) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_ClassicHunger) != 0) ||
3022 (isAllowedToFly() && abilities.flying) )
3023 {
3024 allowed = true;
3025 }
3026 return allowed;
3027}
3028
3029bool Player::isAllowedToTeleport()
3030{
3031 bool allowed = false;
3032 if( isModerator() && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanTeleport) != 0)
3033 {
3034 allowed = true;
3035 }
3036 return allowed;
3037}
3038
3039bool Player::hasInvisiblePrivilege()
3040{
3041 bool enabled = false;
3042 if(app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invisible) != 0)
3043 {
3044 enabled = true;
3045 }
3046 return enabled;
3047}
3048
3049bool Player::hasInvulnerablePrivilege()
3050{
3051 bool enabled = false;
3052 if(app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invulnerable) != 0)
3053 {
3054 enabled = true;
3055 }
3056 return enabled;
3057}
3058
3059bool Player::isModerator()
3060{
3061 return getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op) != 0;
3062}
3063
3064void Player::enableAllPlayerPrivileges(unsigned int &uigamePrivileges, bool enable)
3065{
3066 Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotMine, enable?0:1);
3067 Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotBuild, enable?0:1);
3068 Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotAttackPlayers, enable?0:1);
3069 Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotAttackAnimals, enable?0:1);
3070 Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches, enable?1:0);
3071 Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CanUseContainers, enable?1:0);
3072}
3073
3074void Player::enableAllPlayerPrivileges(bool enable)
3075{
3076 Player::enableAllPlayerPrivileges(m_uiGamePrivileges,enable);
3077}
3078
3079bool Player::canCreateParticles()
3080{
3081 return !hasInvisiblePrivilege();
3082}
3083
3084vector<ModelPart *> *Player::GetAdditionalModelParts()
3085{
3086 if(m_ppAdditionalModelParts==NULL && !m_bCheckedForModelParts)
3087 {
3088 bool hasCustomTexture = !customTextureUrl.empty();
3089 bool customTextureIsDefaultSkin = customTextureUrl.substr(0,3).compare(L"def") == 0;
3090
3091 // see if we can find the parts
3092 m_ppAdditionalModelParts=app.GetAdditionalModelParts(m_dwSkinId);
3093
3094 // If it's a default texture (which has no parts), we have the parts, or we already have the texture (in which case we should have parts if there are any) then we are done
3095 if(!hasCustomTexture || customTextureIsDefaultSkin || m_ppAdditionalModelParts != NULL || app.IsFileInMemoryTextures(customTextureUrl))
3096 {
3097 m_bCheckedForModelParts=true;
3098 }
3099 if(m_ppAdditionalModelParts == NULL && !m_bCheckedDLCForModelParts)
3100 {
3101 m_bCheckedDLCForModelParts = true;
3102
3103 // we don't have the data from the dlc skin yet
3104 app.DebugPrintf("m_bCheckedForModelParts Couldn't get model parts for skin %X\n",m_dwSkinId);
3105
3106 // do we have it from the DLC pack?
3107 DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(this->customTextureUrl);
3108
3109 if(pDLCSkinFile!=NULL)
3110 {
3111 DWORD dwBoxC=pDLCSkinFile->getAdditionalBoxesCount();
3112 if(dwBoxC!=0)
3113 {
3114 app.DebugPrintf("m_bCheckedForModelParts Got model parts from DLCskin for skin %X\n",m_dwSkinId);
3115 m_ppAdditionalModelParts=app.SetAdditionalSkinBoxes(m_dwSkinId,pDLCSkinFile->getAdditionalBoxes());
3116 }
3117
3118 app.SetAnimOverrideBitmask(pDLCSkinFile->getSkinID(),pDLCSkinFile->getAnimOverrideBitmask());
3119
3120 m_bCheckedForModelParts=true;
3121 }
3122 }
3123
3124 if(m_bCheckedForModelParts) setAnimOverrideBitmask(getSkinAnimOverrideBitmask(m_dwSkinId));
3125 }
3126 return m_ppAdditionalModelParts;
3127}
3128
3129void Player::SetAdditionalModelParts(vector<ModelPart *> *ppAdditionalModelParts)
3130{
3131 m_ppAdditionalModelParts=ppAdditionalModelParts;
3132}
3133
3134#if defined(__PS3__) || defined(__ORBIS__)
3135
3136Player::ePlayerNameValidState Player::GetPlayerNameValidState(void)
3137{
3138 return m_ePlayerNameValidState;
3139}
3140
3141void Player::SetPlayerNameValidState(bool bState)
3142{
3143 if(bState)
3144 {
3145 m_ePlayerNameValidState=ePlayerNameValid_True;
3146 }
3147 else
3148 {
3149 m_ePlayerNameValidState=ePlayerNameValid_False;
3150
3151 }
3152}
3153#endif