the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2
3#include "com.mojang.nbt.h"
4#include "net.minecraft.world.level.tile.h"
5#include "net.minecraft.world.item.h"
6#include "net.minecraft.world.phys.h"
7#include "net.minecraft.world.level.h"
8#include "net.minecraft.world.level.storage.h"
9#include "net.minecraft.world.entity.player.h"
10#include "net.minecraft.world.entity.h"
11#include "net.minecraft.world.entity.projectile.h"
12#include "net.minecraft.world.damagesource.h"
13#include "net.minecraft.world.entity.monster.h"
14#include "net.minecraft.world.entity.ai.attributes.h"
15#include "Random.h"
16#include "Animal.h"
17
18Animal::Animal(Level *level) : AgableMob( level )
19{
20// inLove = 0; // 4J removed - now synched data
21 loveTime = 0;
22 loveCause = shared_ptr<Player>();
23
24 setDespawnProtected();
25}
26
27void Animal::defineSynchedData()
28{
29 AgableMob::defineSynchedData();
30
31 entityData->define(DATA_IN_LOVE, (int)0); // 4J added
32}
33
34void Animal::serverAiMobStep()
35{
36 if (getAge() != 0) setInLoveValue(0);
37 AgableMob::serverAiMobStep();
38}
39
40
41void Animal::aiStep()
42{
43 AgableMob::aiStep();
44
45 if (getAge() != 0) setInLoveValue(0);
46
47 if (getInLoveValue() > 0)
48 {
49 setInLoveValue(getInLoveValue()-1);
50 if (getInLoveValue() % 10 == 0)
51 {
52 double xa = random->nextGaussian() * 0.02;
53 double ya = random->nextGaussian() * 0.02;
54 double za = random->nextGaussian() * 0.02;
55 level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
56 }
57 }
58 else
59 {
60 loveTime = 0;
61 }
62
63 updateDespawnProtectedState(); // 4J added
64}
65
66void Animal::checkHurtTarget(shared_ptr<Entity> target, float d)
67{
68 // 4J-JEV: Changed from dynamic cast to use eINSTANCEOF
69 if ( target->instanceof(eTYPE_PLAYER) )
70 {
71 if (d < 3)
72 {
73 double xd = target->x - x;
74 double zd = target->z - z;
75 yRot = (float) (atan2(zd, xd) * 180 / PI) - 90;
76
77 holdGround = true;
78 }
79
80 shared_ptr<Player> p = dynamic_pointer_cast<Player>(target);
81 if (p->getSelectedItem() == NULL || !isFood(p->getSelectedItem()))
82 {
83 attackTarget = nullptr;
84 }
85
86 }
87 // 4J-JEV: Changed from dynamic cast to use eINSTANCEOF
88 else if ( target->instanceof(eTYPE_ANIMAL) )
89 {
90 shared_ptr<Animal> a = dynamic_pointer_cast<Animal>(target);
91 if (getAge() > 0 && a->getAge() < 0)
92 {
93 if (d < 2.5)
94 {
95 holdGround = true;
96 }
97 }
98 else if (getInLoveValue() > 0 && a->getInLoveValue() > 0)
99 {
100 if (a->attackTarget == NULL) a->attackTarget = shared_from_this();
101
102 if (a->attackTarget == shared_from_this() && d < 3.5)
103 {
104 a->setInLoveValue(a->getInLoveValue()+1);
105 setInLoveValue(getInLoveValue()+1);
106 loveTime++;
107 if (loveTime % 4 == 0)
108 {
109 level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, 0, 0, 0);
110 }
111
112 if (loveTime == 20 * 3) breedWith(a);
113 }
114 else loveTime = 0;
115 }
116 else
117 {
118 loveTime = 0;
119 attackTarget = nullptr;
120 }
121
122 }
123}
124
125
126void Animal::breedWith(shared_ptr<Animal> target)
127{
128 shared_ptr<AgableMob> offspring = getBreedOffspring(target);
129
130 setInLoveValue(0);
131 loveTime = 0;
132 attackTarget = nullptr;
133 target->attackTarget = nullptr;
134 target->loveTime = 0;
135 target->setInLoveValue(0);
136
137 // 4J - we have offspring of NULL returned when we have hit our limits of spawning any particular type of animal. In these cases try and do everything we can apart from actually
138 // spawning the entity.
139 if (offspring != NULL)
140 {
141 // Only want to set the age to this +ve value if something is actually spawned, as during this period the animal will attempt to follow offspring and ignore players.
142 setAge(5 * 60 * 20);
143 target->setAge(5 * 60 * 20);
144
145 offspring->setAge(-20 * 60 * 20);
146 offspring->moveTo(x, y, z, yRot, xRot);
147 offspring->setDespawnProtected();
148 for (int i = 0; i < 7; i++)
149 {
150 double xa = random->nextGaussian() * 0.02;
151 double ya = random->nextGaussian() * 0.02;
152 double za = random->nextGaussian() * 0.02;
153 level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
154 }
155 level->addEntity(offspring);
156
157 level->addEntity( shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, random->nextInt(4) + 1) ) );
158 }
159
160 setDespawnProtected();
161}
162
163float Animal::getWalkTargetValue(int x, int y, int z)
164{
165 if (level->getTile(x, y - 1, z) == Tile::grass_Id) return 10;
166 return level->getBrightness(x, y, z) - 0.5f;
167}
168
169bool Animal::hurt(DamageSource *dmgSource, float dmg)
170{
171 if (isInvulnerable()) return false;
172 if (dynamic_cast<EntityDamageSource *>(dmgSource) != NULL)
173 {
174 shared_ptr<Entity> source = dmgSource->getDirectEntity();
175
176 // 4J-JEV: Changed from dynamic cast to use eINSTANCEOF
177 if ( source->instanceof(eTYPE_PLAYER) && !dynamic_pointer_cast<Player>(source)->isAllowedToAttackAnimals() )
178 {
179 return false;
180 }
181
182 if ( (source != NULL) && source->instanceof(eTYPE_ARROW) )
183 {
184 shared_ptr<Arrow> arrow = dynamic_pointer_cast<Arrow>(source);
185
186 // 4J: Check that the arrow's owner can attack animals (dispenser arrows are not owned)
187 if (arrow->owner != NULL && arrow->owner->instanceof(eTYPE_PLAYER) && !dynamic_pointer_cast<Player>(arrow->owner)->isAllowedToAttackAnimals() )
188 {
189 return false;
190 }
191 }
192 }
193
194 fleeTime = 20 * 3;
195
196 if (!useNewAi())
197 {
198 AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
199 if (speed->getModifier(eModifierId_MOB_FLEEING) == NULL)
200 {
201 speed->addModifier(new AttributeModifier(*Animal::SPEED_MODIFIER_FLEEING));
202 }
203 }
204
205 attackTarget = nullptr;
206 setInLoveValue(0);
207
208 return AgableMob::hurt(dmgSource, dmg);
209}
210
211void Animal::addAdditonalSaveData(CompoundTag *tag)
212{
213 AgableMob::addAdditonalSaveData(tag);
214 tag->putInt(L"InLove", getInLoveValue());
215}
216
217void Animal::readAdditionalSaveData(CompoundTag *tag)
218{
219 AgableMob::readAdditionalSaveData(tag);
220 setInLoveValue(tag->getInt(L"InLove"));
221 setDespawnProtected();
222}
223
224shared_ptr<Entity> Animal::findAttackTarget()
225{
226 if (fleeTime > 0) return nullptr;
227
228 float r = 8;
229 if (getInLoveValue() > 0)
230 {
231 vector<shared_ptr<Entity> > *others = level->getEntitiesOfClass(typeid(*this), bb->grow(r, r, r));
232 //for (int i = 0; i < others->size(); i++)
233 for(AUTO_VAR(it, others->begin()); it != others->end(); ++it)
234 {
235 shared_ptr<Animal> p = dynamic_pointer_cast<Animal>(*it);
236 if (p != shared_from_this() && p->getInLoveValue() > 0)
237 {
238 delete others;
239 return p;
240 }
241 }
242 delete others;
243 }
244 else
245 {
246 if (getAge() == 0)
247 {
248 vector<shared_ptr<Entity> > *players = level->getEntitiesOfClass(typeid(Player), bb->grow(r, r, r));
249 //for (int i = 0; i < players.size(); i++)
250 for(AUTO_VAR(it, players->begin()); it != players->end(); ++it)
251 {
252 setDespawnProtected();
253
254 shared_ptr<Player> p = dynamic_pointer_cast<Player>(*it);
255 if (p->getSelectedItem() != NULL && this->isFood(p->getSelectedItem()))
256 {
257 delete players;
258 return p;
259 }
260 }
261 delete players;
262 }
263 else if (getAge() > 0)
264 {
265 vector<shared_ptr<Entity> > *others = level->getEntitiesOfClass(typeid(*this), bb->grow(r, r, r));
266 //for (int i = 0; i < others.size(); i++)
267 for(AUTO_VAR(it, others->begin()); it != others->end(); ++it)
268 {
269 shared_ptr<Animal> p = dynamic_pointer_cast<Animal>(*it);
270 if (p != shared_from_this() && p->getAge() < 0)
271 {
272 delete others;
273 return p;
274 }
275 }
276 delete others;
277 }
278 }
279 return nullptr;
280}
281
282bool Animal::canSpawn()
283{
284 int xt = Mth::floor(x);
285 int yt = Mth::floor(bb->y0);
286 int zt = Mth::floor(z);
287 return level->getTile(xt, yt - 1, zt) == Tile::grass_Id && level->getDaytimeRawBrightness(xt, yt, zt) > 8 && AgableMob::canSpawn();
288}
289
290int Animal::getAmbientSoundInterval()
291{
292 return 20 * 6;
293}
294
295bool Animal::removeWhenFarAway()
296{
297 return !isDespawnProtected(); // 4J changed - was false
298}
299
300int Animal::getExperienceReward(shared_ptr<Player> killedBy)
301{
302 return 1 + level->random->nextInt(3);
303}
304
305bool Animal::isFood(shared_ptr<ItemInstance> itemInstance)
306{
307 return itemInstance->id == Item::wheat_Id;
308}
309
310bool Animal::mobInteract(shared_ptr<Player> player)
311{
312 shared_ptr<ItemInstance> item = player->inventory->getSelected();
313 if (item != NULL && isFood(item) && getAge() == 0 && getInLoveValue() <= 0)
314 {
315 if (!player->abilities.instabuild)
316 {
317 item->count--;
318 if (item->count <= 0)
319 {
320 player->inventory->setItem(player->inventory->selected, nullptr);
321 }
322 }
323
324
325 // 4J-PB - If we can't produce another animal through breeding because of the spawn limits, display a message here
326 if(!level->isClientSide)
327 {
328 switch(GetType())
329 {
330 case eTYPE_CHICKEN:
331 if( !level->canCreateMore(eTYPE_CHICKEN, Level::eSpawnType_Breed) )
332 {
333 player->displayClientMessage(IDS_MAX_CHICKENS_BRED );
334 return false;
335 }
336 break;
337 case eTYPE_WOLF:
338 if( !level->canCreateMore(eTYPE_WOLF, Level::eSpawnType_Breed) )
339 {
340 player->displayClientMessage(IDS_MAX_WOLVES_BRED );
341 return false;
342 }
343 break;
344 case eTYPE_MUSHROOMCOW:
345 if( !level->canCreateMore(eTYPE_MUSHROOMCOW, Level::eSpawnType_Breed) )
346 {
347 player->displayClientMessage(IDS_MAX_MUSHROOMCOWS_BRED );
348 return false;
349 }
350 break;
351 default:
352 if((GetType() & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK)
353 {
354 if( !level->canCreateMore(GetType(), Level::eSpawnType_Breed) )
355 {
356 player->displayClientMessage(IDS_MAX_PIGS_SHEEP_COWS_CATS_BRED );
357
358 return false;
359 }
360 }
361 else if( instanceof(eTYPE_MONSTER) )
362 {
363
364 }
365 break;
366 }
367 setInLove(player);
368 }
369 setInLove();
370
371 return true;
372 }
373 return AgableMob::mobInteract(player);
374}
375
376// 4J added
377int Animal::getInLoveValue()
378{
379 return entityData->getInteger(DATA_IN_LOVE);
380}
381
382void Animal::setInLoveValue(int value)
383{
384 entityData->set(DATA_IN_LOVE, value);
385}
386
387// 4J added
388void Animal::setInLove(shared_ptr<Player> player)
389{
390 loveCause = player;
391 setInLoveValue(20*30);
392}
393
394shared_ptr<Player> Animal::getLoveCause()
395{
396 return loveCause.lock();
397}
398
399void Animal::setInLove()
400{
401 entityData->set(DATA_IN_LOVE, 20 * 30);
402
403 attackTarget = nullptr;
404 level->broadcastEntityEvent(shared_from_this(), EntityEvent::IN_LOVE_HEARTS);
405}
406
407bool Animal::isInLove()
408{
409 return entityData->getInteger(DATA_IN_LOVE) > 0;
410}
411
412void Animal::resetLove() {
413 entityData->set(DATA_IN_LOVE, 0);
414}
415
416bool Animal::canMate(shared_ptr<Animal> partner)
417{
418 if (partner == shared_from_this()) return false;
419 if (typeid(*partner) != typeid(*this)) return false;
420 return isInLove() && partner->isInLove();
421}
422
423void Animal::handleEntityEvent(byte id)
424{
425 if (id == EntityEvent::IN_LOVE_HEARTS)
426 {
427 for (int i = 0; i < 7; i++)
428 {
429 double xa = random->nextGaussian() * 0.02;
430 double ya = random->nextGaussian() * 0.02;
431 double za = random->nextGaussian() * 0.02;
432 level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
433 }
434 }
435 else
436 {
437 AgableMob::handleEntityEvent(id);
438 }
439}
440
441void Animal::updateDespawnProtectedState()
442{
443 if( level->isClientSide ) return;
444
445 if( m_isDespawnProtected )
446 {
447 int xt = Mth::floor(x);
448 int zt = Mth::floor(z);
449
450 if ( xt > m_maxWanderX ) m_maxWanderX = xt;
451 if ( xt < m_minWanderX ) m_minWanderX = xt;
452 if ( zt > m_maxWanderZ ) m_maxWanderZ = zt;
453 if ( zt < m_minWanderZ ) m_minWanderZ = zt;
454
455 if( ( ( m_maxWanderX - m_minWanderX ) > MAX_WANDER_DISTANCE ) ||
456 ( ( m_maxWanderZ - m_minWanderZ ) > MAX_WANDER_DISTANCE ) )
457 {
458// printf("Unprotecting : %d to %d, %d to %d\n", m_minWanderX, m_maxWanderX, m_minWanderZ, m_maxWanderZ );
459 m_isDespawnProtected = false;
460 }
461
462/*
463 if( isExtraWanderingEnabled() )
464 {
465 printf("%d: %d %d, %d\n",entityId,m_maxWanderX - m_minWanderX, m_maxWanderZ - m_minWanderZ, getWanderingQuadrant());
466 }
467 */
468 }
469}
470
471bool Animal::isDespawnProtected()
472{
473 return m_isDespawnProtected;
474}
475
476void Animal::setDespawnProtected()
477{
478 if( level && level->isClientSide ) return;
479
480 int xt = Mth::floor(x);
481 int zt = Mth::floor(z);
482
483 m_minWanderX = xt;
484 m_maxWanderX = xt;
485 m_minWanderZ = zt;
486 m_maxWanderZ = zt;
487
488 m_isDespawnProtected = true;
489}