diff options
Diffstat (limited to '')
-rw-r--r-- | source/Pawn.cpp | 272 |
1 files changed, 227 insertions, 45 deletions
diff --git a/source/Pawn.cpp b/source/Pawn.cpp index da878bf49..8d530abb4 100644 --- a/source/Pawn.cpp +++ b/source/Pawn.cpp @@ -21,8 +21,10 @@ CLASS_DEFINITION( cPawn, cEntity ) -cPawn::cPawn() +cPawn::cPawn(void) : cEntity( 0, 0, 0 ) + , m_Health(1) + , m_MaxHealth(1) , m_LastPosX( 0.0 ) , m_LastPosY( 0.0 ) , m_LastPosZ( 0.0 ) @@ -30,7 +32,6 @@ cPawn::cPawn() , m_bBurnable(true) , m_MetaData(NORMAL) { - SetMaxHealth(20); } @@ -39,40 +40,83 @@ cPawn::cPawn() cPawn::~cPawn() { + // Nothing needed yet +} + + + + +void cPawn::Heal(int a_HitPoints) +{ + m_Health += a_HitPoints; + if (m_Health > m_MaxHealth) + { + m_Health = m_MaxHealth; + } } -void cPawn::Heal( int a_Health ) +void cPawn::TakeDamage(cPawn & a_Attacker) { - (void)a_Health; + int RawDamage = a_Attacker.GetRawDamageAgainst(*this); + + TakeDamage(dtAttack, &a_Attacker, RawDamage, a_Attacker.GetKnockbackAmountAgainst(*this)); } -void cPawn::TakeDamage(int a_Damage, cEntity * a_Instigator) +void cPawn::TakeDamage(eDamageType a_DamageType, cPawn * a_Attacker, int a_RawDamage, double a_KnockbackAmount) +{ + int FinalDamage = a_RawDamage - GetArmorCoverAgainst(a_Attacker, a_DamageType, a_RawDamage); + TakeDamage(a_DamageType, a_Attacker, a_RawDamage, FinalDamage, a_KnockbackAmount); +} + + + + + +void cPawn::TakeDamage(eDamageType a_DamageType, cPawn * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount) { TakeDamageInfo TDI; - TDI.Damage = a_Damage; - TDI.Instigator = a_Instigator; - cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_TAKE_DAMAGE, 2, this, &TDI); + TDI.DamageType = a_DamageType; + TDI.Attacker = a_Attacker; + TDI.RawDamage = a_RawDamage; + TDI.FinalDamage = a_FinalDamage; + Vector3d Heading; + Heading.x = sin(m_Rot.x); + Heading.y = 0.4; // TODO: adjust the amount of "up" knockback when testing + Heading.z = cos(m_Rot.x); + TDI.Knockback = Heading * a_KnockbackAmount; + DoTakeDamage(TDI); +} + - if (TDI.Damage == 0) + + + +void cPawn::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI)) { return; } + if (m_Health <= 0) { // Can't take damage if already dead return; } - m_Health -= (short)TDI.Damage; + m_Health -= (short)a_TDI.FinalDamage; + + // TODO: Apply damage to armor + if (m_Health < 0) { m_Health = 0; @@ -82,7 +126,7 @@ void cPawn::TakeDamage(int a_Damage, cEntity * a_Instigator) if (m_Health <= 0) { - KilledBy(TDI.Instigator); + KilledBy(a_TDI.Attacker); } } @@ -90,15 +134,23 @@ void cPawn::TakeDamage(int a_Damage, cEntity * a_Instigator) -void cPawn::KilledBy(cEntity * a_Killer) +void cPawn::KilledBy(cPawn * a_Killer) { + short OldHealth = m_Health; m_Health = 0; - if( cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_KILLED, 2, this, a_Killer ) ) + if (cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_KILLED, 2, this, a_Killer)) { - return; // Give plugins a chance to 'unkill' the pawn. + // Plugin wants to 'unkill' the pawn. Set health back and abort + m_Health = OldHealth; + return; } + // Drop loot: + cItems Drops; + GetDrops(Drops, a_Killer); + m_World->SpawnItemPickups(Drops, m_Pos.x, m_Pos.y, m_Pos.z); + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD); } @@ -106,18 +158,147 @@ void cPawn::KilledBy(cEntity * a_Killer) -void cPawn::TeleportToEntity(cEntity * a_Entity) +int cPawn::GetRawDamageAgainst(const cPawn & a_Receiver) { - TeleportTo(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ()); + // Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items + // Ref: http://www.minecraftwiki.net/wiki/Damage#Dealing_damage as of 2012_12_20 + switch (this->GetEquippedWeapon().m_ItemType) + { + case E_ITEM_WOODEN_SWORD: return 4; + case E_ITEM_GOLD_SWORD: return 4; + case E_ITEM_STONE_SWORD: return 5; + case E_ITEM_IRON_SWORD: return 6; + case E_ITEM_DIAMOND_SWORD: return 7; + + case E_ITEM_WOODEN_AXE: return 3; + case E_ITEM_GOLD_AXE: return 3; + case E_ITEM_STONE_AXE: return 4; + case E_ITEM_IRON_AXE: return 5; + case E_ITEM_DIAMOND_AXE: return 6; + + case E_ITEM_WOODEN_PICKAXE: return 2; + case E_ITEM_GOLD_PICKAXE: return 2; + case E_ITEM_STONE_PICKAXE: return 3; + case E_ITEM_IRON_PICKAXE: return 4; + case E_ITEM_DIAMOND_PICKAXE: return 5; + + case E_ITEM_WOODEN_SHOVEL: return 1; + case E_ITEM_GOLD_SHOVEL: return 1; + case E_ITEM_STONE_SHOVEL: return 2; + case E_ITEM_IRON_SHOVEL: return 3; + case E_ITEM_DIAMOND_SHOVEL: return 4; + } + // All other equipped items give a damage of 1: + return 1; } -void cPawn::TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) +int cPawn::GetArmorCoverAgainst(const cPawn * a_Attacker, eDamageType a_DamageType, int a_Damage) { - SetPosition( a_PosX, a_PosY, a_PosZ ); + // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover + + // Filter out damage types that are not protected by armor: + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20 + switch (a_DamageType) + { + case dtOnFire: + case dtSuffocating: + case dtDrowning: // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected + case dtStarving: + case dtInVoid: + case dtPoisoning: + case dtPotionOfHarming: + case dtFalling: + case dtLightning: + { + return 0; + } + } + + // Add up all armor points: + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20 + int ArmorValue = 0; + switch (GetEquippedHelmet().m_ItemType) + { + case E_ITEM_LEATHER_CAP: ArmorValue += 1; break; + case E_ITEM_GOLD_HELMET: ArmorValue += 2; break; + case E_ITEM_CHAIN_HELMET: ArmorValue += 2; break; + case E_ITEM_IRON_HELMET: ArmorValue += 2; break; + case E_ITEM_DIAMOND_HELMET: ArmorValue += 3; break; + } + switch (GetEquippedChestplate().m_ItemType) + { + case E_ITEM_LEATHER_TUNIC: ArmorValue += 3; break; + case E_ITEM_GOLD_CHESTPLATE: ArmorValue += 5; break; + case E_ITEM_CHAIN_CHESTPLATE: ArmorValue += 5; break; + case E_ITEM_IRON_CHESTPLATE: ArmorValue += 6; break; + case E_ITEM_DIAMOND_CHESTPLATE: ArmorValue += 8; break; + } + switch (GetEquippedLeggings().m_ItemType) + { + case E_ITEM_LEATHER_PANTS: ArmorValue += 2; break; + case E_ITEM_GOLD_LEGGINGS: ArmorValue += 3; break; + case E_ITEM_CHAIN_LEGGINGS: ArmorValue += 4; break; + case E_ITEM_IRON_LEGGINGS: ArmorValue += 5; break; + case E_ITEM_DIAMOND_LEGGINGS: ArmorValue += 6; break; + } + switch (GetEquippedBoots().m_ItemType) + { + case E_ITEM_LEATHER_BOOTS: ArmorValue += 1; break; + case E_ITEM_GOLD_BOOTS: ArmorValue += 1; break; + case E_ITEM_CHAIN_BOOTS: ArmorValue += 1; break; + case E_ITEM_IRON_BOOTS: ArmorValue += 2; break; + case E_ITEM_DIAMOND_BOOTS: ArmorValue += 3; break; + } + + // TODO: Special armor cases, such as wool, saddles, dog's collar + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Mob_armor as of 2012_12_20 + + // Now ArmorValue is in [0, 20] range, which corresponds to [0, 80%] protection. Calculate the hitpoints from that: + return a_Damage * (ArmorValue * 4) / 100; +} + + + + + +double cPawn::GetKnockbackAmountAgainst(const cPawn & a_Receiver) +{ + // Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit + + // TODO: Enchantments + return 1; +} + + + + + +void cPawn::GetDrops(cItems & a_Drops, cPawn * a_Killer) +{ + UNUSED(a_Drops); + UNUSED(a_Killer); +} + + + + + +void cPawn::TeleportToEntity(cEntity & a_Entity) +{ + TeleportTo(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ()); +} + + + + + +void cPawn::TeleportTo(double a_PosX, double a_PosY, double a_PosZ) +{ + SetPosition(a_PosX, a_PosY, a_PosZ); GetWorld()->BroadcastTeleportEntity(*this); } @@ -155,9 +336,9 @@ void cPawn::SetMetaData(MetaData a_MetaData) //----Change Entity MetaData void cPawn::CheckMetaDataBurn() { - char Block = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y, (int) m_Pos.z); - char BlockAbove = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y + 1, (int) m_Pos.z); - char BlockBelow = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y - 1, (int) m_Pos.z); + BLOCKTYPE Block = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y, (int) m_Pos.z); + BLOCKTYPE BlockAbove = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y + 1, (int) m_Pos.z); + BLOCKTYPE BlockBelow = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y - 1, (int) m_Pos.z); if ( (GetMetaData() == BURNING) && @@ -184,35 +365,36 @@ void cPawn::CheckMetaDataBurn() -//What to do if On fire +// What to do if On fire void cPawn::InStateBurning(float a_Dt) { m_FireDamageInterval += a_Dt; - char Block = GetWorld()->GetBlock( (int)m_Pos.x, (int)m_Pos.y, (int)m_Pos.z ); - char BlockAbove = GetWorld()->GetBlock( (int)m_Pos.x, (int)m_Pos.y + 1, (int)m_Pos.z ); - if (m_FireDamageInterval > 800) + if (m_FireDamageInterval < 800) { + return; + } - m_FireDamageInterval = 0; - TakeDamage(1, this); + BLOCKTYPE Block = GetWorld()->GetBlock((int)m_Pos.x, (int)m_Pos.y, (int)m_Pos.z); + BLOCKTYPE BlockAbove = GetWorld()->GetBlock((int)m_Pos.x, (int)m_Pos.y + 1, (int)m_Pos.z); + m_FireDamageInterval = 0; + TakeDamage(dtOnFire, NULL, 1, 0); - m_BurnPeriod++; - if (IsBlockLava(Block) || Block == E_BLOCK_FIRE - || IsBlockLava(BlockAbove) || BlockAbove == E_BLOCK_FIRE) - { - m_BurnPeriod = 0; - TakeDamage(6, this); - } - else - { - TakeDamage(1, this); - } - - if (m_BurnPeriod > 7) - { - SetMetaData(NORMAL); - m_BurnPeriod = 0; - } + m_BurnPeriod++; + if ( + IsBlockLava(Block) || + (Block == E_BLOCK_FIRE) || + IsBlockLava(BlockAbove) || + (BlockAbove == E_BLOCK_FIRE) + ) + { + m_BurnPeriod = 0; + TakeDamage(dtLavaContact, NULL, 6, 0); + } + + if (m_BurnPeriod > 7) + { + SetMetaData(NORMAL); + m_BurnPeriod = 0; } } @@ -224,7 +406,7 @@ void cPawn::SetMaxHealth(short a_MaxHealth) { this->m_MaxHealth = a_MaxHealth; - //Reset health + // Reset health m_Health = a_MaxHealth; } |