void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) { if (m_ClientHandle != NULL) { if (m_ClientHandle->IsDestroyed()) { // This should not happen, because destroying a client will remove it from the world, but just in case m_ClientHandle = NULL; return; } if (!m_ClientHandle->IsPlaying()) { // We're not yet in the game, ignore everything return; } } if (!a_Chunk.IsValid()) { // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) return; } super::Tick(a_Dt, a_Chunk); // Handle charging the bow: if (m_IsChargingBow) { m_BowCharge += 1; } //handle updating experience if (m_bDirtyExperience) { SendExperience(); } if (m_bDirtyPosition) { // Apply food exhaustion from movement: ApplyFoodExhaustionFromMovement(); cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); BroadcastMovementUpdate(m_ClientHandle); m_ClientHandle->StreamChunks(); } else { BroadcastMovementUpdate(m_ClientHandle); } if (m_Health > 0) // make sure player is alive { m_World->CollectPickupsByPlayer(this); if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) { FinishEating(); } HandleFood(); } if (m_IsFishing) { HandleFloater(); } // Update items (e.g. Maps) m_Inventory.UpdateItems(); // Send Player List (Once per m_LastPlayerListTime/1000 ms) cTimer t1; if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) { m_World->SendPlayerList(this); m_LastPlayerListTime = t1.GetNowTime(); } if (IsFlying()) { m_LastGroundHeight = (float)GetPosY(); } }
bool cPlayer::LoadFromDisk() { LoadPermissionsFromDisk(); // Log player permissions, cause it's what the cool kids do LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) { if( itr->second ) LOG(" - %s", itr->first.c_str() ); } AString SourceFile; Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); cFile f; if (!f.Open(SourceFile, cFile::fmRead)) { // This is a new player whom we haven't seen yet, bail out, let them have the defaults return false; } AString buffer; if (f.ReadRestOfFile(buffer) != f.GetSize()) { LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); return false; } f.Close(); //cool kids play nice Json::Value root; Json::Reader reader; if (!reader.parse(buffer, root, false)) { LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); } Json::Value & JSON_PlayerPosition = root["position"]; if (JSON_PlayerPosition.size() == 3) { SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); m_LastPosX = GetPosX(); m_LastPosY = GetPosY(); m_LastPosZ = GetPosZ(); m_LastFoodPos = GetPosition(); } Json::Value & JSON_PlayerRotation = root["rotation"]; if (JSON_PlayerRotation.size() == 3) { SetYaw ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); } m_Health = root.get("health", 0).asInt(); m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt(); m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); m_LifetimeTotalXp = (short) root.get("xpTotal", 0).asInt(); m_CurrentXp = (short) root.get("xpCurrent", 0).asInt(); m_IsFlying = root.get("isflying", 0).asBool(); m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); if (m_GameMode == eGameMode_Creative) { m_CanFly = true; } m_Inventory.LoadFromJson(root["inventory"]); m_LoadedWorldName = root.get("world", "world").asString(); LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() ); return true; }
bool cPlayer::SaveToDisk() { cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); // create the JSON data Json::Value JSON_PlayerPosition; JSON_PlayerPosition.append(Json::Value(GetPosX())); JSON_PlayerPosition.append(Json::Value(GetPosY())); JSON_PlayerPosition.append(Json::Value(GetPosZ())); Json::Value JSON_PlayerRotation; JSON_PlayerRotation.append(Json::Value(GetYaw())); JSON_PlayerRotation.append(Json::Value(GetPitch())); JSON_PlayerRotation.append(Json::Value(GetRoll())); Json::Value JSON_Inventory; m_Inventory.SaveToJson(JSON_Inventory); Json::Value root; root["position"] = JSON_PlayerPosition; root["rotation"] = JSON_PlayerRotation; root["inventory"] = JSON_Inventory; root["health"] = m_Health; root["xpTotal"] = m_LifetimeTotalXp; root["xpCurrent"] = m_CurrentXp; root["air"] = m_AirLevel; root["food"] = m_FoodLevel; root["foodSaturation"] = m_FoodSaturationLevel; root["foodTickTimer"] = m_FoodTickTimer; root["foodExhaustion"] = m_FoodExhaustionLevel; root["world"] = GetWorld()->GetName(); root["isflying"] = IsFlying(); if (m_GameMode == GetWorld()->GetGameMode()) { root["gamemode"] = (int) eGameMode_NotSet; } else { root["gamemode"] = (int) m_GameMode; } Json::StyledWriter writer; std::string JsonData = writer.write(root); AString SourceFile; Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); cFile f; if (!f.Open(SourceFile, cFile::fmWrite)) { LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); return false; } if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) { LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); return false; } return true; }
bool cMinecart::DoTakeDamage(TakeDamageInfo & TDI) { if ((TDI.Attacker != nullptr) && TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative()) { Destroy(); TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative SetInvulnerableTicks(0); return super::DoTakeDamage(TDI); // No drops for creative } m_LastDamage = TDI.FinalDamage; if (!super::DoTakeDamage(TDI)) { return false; } m_World->BroadcastEntityMetadata(*this); if (GetHealth() <= 0) { Destroy(); cItems Drops; switch (m_Payload) { case mpNone: { Drops.push_back(cItem(E_ITEM_MINECART, 1, 0)); break; } case mpChest: { Drops.push_back(cItem(E_ITEM_CHEST_MINECART, 1, 0)); break; } case mpFurnace: { Drops.push_back(cItem(E_ITEM_FURNACE_MINECART, 1, 0)); break; } case mpTNT: { Drops.push_back(cItem(E_ITEM_MINECART_WITH_TNT, 1, 0)); break; } case mpHopper: { Drops.push_back(cItem(E_ITEM_MINECART_WITH_HOPPER, 1, 0)); break; } default: { ASSERT(!"Unhandled minecart type when spawning pickup!"); return true; } } m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); } return true; }
/// Moves pickups from above this hopper into it. Returns true if the contents have changed. bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) { UNUSED(a_CurrentTick); class cHopperPickupSearchCallback : public cEntityCallback { public: cHopperPickupSearchCallback(const Vector3i & a_Pos, cItemGrid & a_Contents) : m_Pos(a_Pos), m_bFoundPickupsAbove(false), m_Contents(a_Contents) { } virtual bool Item(cEntity * a_Entity) override { ASSERT(a_Entity != NULL); if (!a_Entity->IsPickup() || a_Entity->IsDestroyed()) { return false; } Vector3f EntityPos = a_Entity->GetPosition(); Vector3f BlockPos(m_Pos.x + 0.5f, (float)m_Pos.y + 1, m_Pos.z + 0.5f); // One block above hopper, and search from center outwards double Distance = (EntityPos - BlockPos).Length(); if (Distance < 0.5) { if (TrySuckPickupIn((cPickup *)a_Entity)) { return false; } } return false; } bool TrySuckPickupIn(cPickup * a_Pickup) { cItem & Item = a_Pickup->GetItem(); for (int i = 0; i < ContentsWidth * ContentsHeight; i++) { if (m_Contents.IsSlotEmpty(i)) { m_bFoundPickupsAbove = true; m_Contents.SetSlot(i, Item); a_Pickup->Destroy(); // Kill pickup return true; } else if (m_Contents.GetSlot(i).IsEqual(Item) && !m_Contents.GetSlot(i).IsFullStack()) { m_bFoundPickupsAbove = true; int PreviousCount = m_Contents.GetSlot(i).m_ItemCount; Item.m_ItemCount -= m_Contents.ChangeSlotCount(i, Item.m_ItemCount) - PreviousCount; // Set count to however many items were added if (Item.IsEmpty()) { a_Pickup->Destroy(); // Kill pickup if all items were added } return true; } } return false; } bool FoundPickupsAbove(void) const { return m_bFoundPickupsAbove; } protected: Vector3i m_Pos; bool m_bFoundPickupsAbove; cItemGrid & m_Contents; }; cHopperPickupSearchCallback HopperPickupSearchCallback(Vector3i(GetPosX(), GetPosY(), GetPosZ()), m_Contents); a_Chunk.ForEachEntity(HopperPickupSearchCallback); return HopperPickupSearchCallback.FoundPickupsAbove(); }
void cMinecartWithChest::Destroyed() { cItems Pickups; m_Contents.CopyToItems(Pickups); GetWorld()->SpawnItemPickups(Pickups, GetPosX(), GetPosY() + 1, GetPosZ(), 4); }
void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta) { // Initialise to 'slow down' values int AccelDecelSpeed = -2; int AccelDecelNegSpeed = 2; if ((a_RailMeta & 0x8) == 0x8) { // Rail powered - set variables to 'speed up' values AccelDecelSpeed = 1; AccelDecelNegSpeed = -1; } switch (a_RailMeta & 0x07) { case E_META_RAIL_ZM_ZP: // NORTHSOUTH { SetYaw(270); SetPosY(floor(GetPosY()) + 0.55); SetSpeedY(0); SetSpeedX(0); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); if (EntCol || BlckCol) { return; } if (GetSpeedZ() != NO_SPEED) { if (GetSpeedZ() > NO_SPEED) { AddSpeedZ(AccelDecelSpeed); } else { AddSpeedZ(AccelDecelNegSpeed); } } break; } case E_META_RAIL_XM_XP: // EASTWEST { SetYaw(180); SetPosY(floor(GetPosY()) + 0.55); SetSpeedY(NO_SPEED); SetSpeedZ(NO_SPEED); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); if (EntCol || BlckCol) { return; } if (GetSpeedX() != NO_SPEED) { if (GetSpeedX() > NO_SPEED) { AddSpeedX(AccelDecelSpeed); } else { AddSpeedX(AccelDecelNegSpeed); } } break; } case E_META_RAIL_ASCEND_XM: // ASCEND EAST { SetYaw(180); SetSpeedZ(NO_SPEED); if (GetSpeedX() >= NO_SPEED) { if (GetSpeedX() <= MAX_SPEED) { AddSpeedX(AccelDecelSpeed); SetSpeedY(-GetSpeedX()); } } else { AddSpeedX(AccelDecelNegSpeed); SetSpeedY(-GetSpeedX()); } break; } case E_META_RAIL_ASCEND_XP: // ASCEND WEST { SetYaw(180); SetSpeedZ(NO_SPEED); if (GetSpeedX() > NO_SPEED) { AddSpeedX(AccelDecelSpeed); SetSpeedY(GetSpeedX()); } else { if (GetSpeedX() >= MAX_SPEED_NEGATIVE) { AddSpeedX(AccelDecelNegSpeed); SetSpeedY(GetSpeedX()); } } break; } case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH { SetYaw(270); SetSpeedX(NO_SPEED); if (GetSpeedZ() >= NO_SPEED) { if (GetSpeedZ() <= MAX_SPEED) { AddSpeedZ(AccelDecelSpeed); SetSpeedY(-GetSpeedZ()); } } else { AddSpeedZ(AccelDecelNegSpeed); SetSpeedY(-GetSpeedZ()); } break; } case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH { SetYaw(270); SetSpeedX(NO_SPEED); if (GetSpeedZ() > NO_SPEED) { AddSpeedZ(AccelDecelSpeed); SetSpeedY(GetSpeedZ()); } else { if (GetSpeedZ() >= MAX_SPEED_NEGATIVE) { AddSpeedZ(AccelDecelNegSpeed); SetSpeedY(GetSpeedZ()); } } break; } default: ASSERT(!"Unhandled powered rail metadata!"); break; } }
void CGrunt::Update(float fElapsedTime) { // Update common properties across all enemies CEnemy::Update(fElapsedTime); // if this enemy is cc'd don't if (m_pEState->GetTimer() == true) { m_pEState->CEntityState::Update(fElapsedTime); return; } m_fUpdateOldPos += fElapsedTime; if( cooldown <= 0.0f ) punching = false; if( punching == true ) cooldown -= fElapsedTime; if (GetAnimInfo()->GetAnimationName() == "Grunt_Attack_Animation") return; if(GetTarget() != nullptr) { float tar_pos_x = GetTarget()->GetPosX(); float tar_pos_y = GetTarget()->GetPosY(); if(tar_pos_x > GetPosX()) { //set Grunt's animation's facing to the right m_bFlipped = true; } else { // Set Grunt's animation to face to the left m_bFlipped = false; } SetFlipped( m_bFlipped ); if(m_fMoveAway <= 0) { tar_pos_x += (m_bFlipped) ? -66 : 66; //Simple Pathing twards the player if(tar_pos_y != GetPosY() )//Above the Player { float min_Distance = (float)(GetTarget()->GetWidth()/2 + GetWidth()/2); if(GetPosX() + min_Distance > tar_pos_x && GetPosX() - min_Distance < tar_pos_x) { if( tar_pos_x < GetPosX()) SetVelX(speed * fElapsedTime); else SetVelX(-speed * fElapsedTime); } else { if( tar_pos_y < GetPosY()) SetVelY(-speed * fElapsedTime); else SetVelY(speed * fElapsedTime); if( tar_pos_x < GetPosX()) SetVelX(-speed * fElapsedTime); else SetVelX(speed * fElapsedTime); } } else { SetVelY(0); if( tar_pos_x < GetPosX()) SetVelX(-speed * fElapsedTime); else SetVelX(speed * fElapsedTime); } //stop 'bouncing' int threshold = 5; if(tar_pos_x - GetPosX() < threshold && tar_pos_x - GetPosX() > -1 * threshold) SetVelX(0); if(tar_pos_y - GetPosY() < threshold && tar_pos_y - GetPosY() > -1 * threshold) SetVelY(0); } else//update move away { m_fMoveAway -= fElapsedTime; if(m_bEvadeUp) SetVelY(-speed * fElapsedTime); else SetVelY(speed * fElapsedTime); } //Check Colider if(TileManager::GetInstance()->CheckBlockedTiles(GetPosX(), GetPosY(), GetVelX(), 0)) { SetPosX(GetPosX() + GetVelX()); if(TileManager::GetInstance()->CheckBlockedTiles(GetPosX(), GetPosY(), 0, GetVelY())) SetPosY(GetPosY() + GetVelY()); } else { if( GetPosY() >= 600 ) SetPosY( (GetPosY() - (GetPosY() - 599)) ); else SetPosY(GetPosY() + 1); SetVelX(0); SetVelY(0); } if(GetPosX() < 0) SetPosX(0); if(punching == false && GetPosX() - tar_pos_x < 10 && GetPosX() - tar_pos_x > - 10 ) { if(GetPosY() - tar_pos_y < 32 && GetPosY() - tar_pos_y > -32) { GetAnimInfo()->SetAnimationName("Grunt_Attack_Animation"); punching = true; cooldown = 3.0f; } } //Set/use Move out of the way if(m_fUpdateOldPos >= 1.0f) { if(m_fPosXOld + 5 > GetPosX() && m_fPosXOld - 5 < GetPosX()) { if(m_fPosYOld + 5 > GetPosY() && m_fPosYOld - 5 < GetPosY()) { if(cooldown <= 0) { m_bEvadeUp = !m_bEvadeUp; if(m_fMoveAway <= 0) m_fMoveAway = 4.0f; } } } m_fPosXOld = GetPosX(); m_fPosYOld = GetPosY(); m_fUpdateOldPos = 0.0f; } } }
void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); if (!IsTicking()) { // The base class tick destroyed us return; } if (!m_bIsMouthOpen) { if (m_World->GetTickRandomNumber(50) == 25) { m_bIsMouthOpen = true; } } else { if (m_World->GetTickRandomNumber(10) == 5) { m_bIsMouthOpen = false; } } if ((m_Attachee != nullptr) && (!m_bIsTame)) { if (m_TameAttemptTimes < m_TimesToTame) { if (m_World->GetTickRandomNumber(50) == 25) { m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST)); m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST)); m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST)); m_Attachee->Detach(); m_bIsRearing = true; } } else { // TODO: emit hearts here m_bIsTame = true; } } if (m_bIsRearing) { if (m_RearTickCount == 20) { m_bIsRearing = false; m_RearTickCount = 0; } else { m_RearTickCount++; } } m_World->BroadcastEntityMetadata(*this); }
void MapMgr::ChangeObjectLocation(Object* obj) { /* if ( !obj ) return; // crashfix */ ARCEMU_ASSERT(obj != NULL); // Items and containers are of no interest for us if(obj->IsItem() || obj->IsContainer() || obj->GetMapMgr() != this) { return; } Player* plObj = NULL; ByteBuffer* buf = 0; if(obj->IsPlayer()) { plObj = TO< Player* >(obj); } Object* curObj; float fRange = 0.0f; /////////////////////////////////////// // Update in-range data for old objects /////////////////////////////////////// if(obj->HasInRangeObjects()) { for(Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(); iter != obj->GetInRangeSetEnd();) { curObj = *iter; ++iter; if(curObj->IsPlayer() && plObj != NULL && plObj->transporter_info.guid && plObj->transporter_info.guid == TO< Player* >(curObj)->transporter_info.guid) fRange = 0.0f; // unlimited distance for people on same boat else if(curObj->GetTypeFromGUID() == HIGHGUID_TYPE_TRANSPORTER) fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.) //If the object announcing its position is a transport, or other special object, then deleting it from visible objects should be avoided. - By: VLack else if(obj->IsGameObject() && (TO< GameObject* >(obj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId()) fRange = 0.0f; //If the object we're checking for possible removal is a transport or other special object, and we are players on the same map, don't remove it... else if(plObj && curObj->IsGameObject() && (TO< GameObject* >(curObj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId()) fRange = 0.0f; else if(curObj->IsPlayer() && TO< Player* >(curObj)->GetFarsightTarget() == obj->GetGUID()) fRange = 0.0f;//Mind Vision, Eye of Kilrogg else fRange = m_UpdateDistance; // normal distance if(fRange > 0.0f && (curObj->GetDistance2dSq(obj) > fRange)) { if(plObj != NULL) plObj->RemoveIfVisible(curObj->GetGUID()); if(curObj->IsPlayer()) TO< Player* >(curObj)->RemoveIfVisible(obj->GetGUID()); curObj->RemoveInRangeObject(obj); if(obj->GetMapMgr() != this) { /* Something removed us. */ return; } obj->RemoveInRangeObject(curObj); } } } /////////////////////////// // Get new cell coordinates /////////////////////////// if(obj->GetMapMgr() != this) { /* Something removed us. */ return; } if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minX || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { if(plObj != NULL) { if(plObj->GetBindMapId() != GetMapId()) { plObj->SafeTeleport(plObj->GetBindMapId(), 0, plObj->GetBindPositionX(), plObj->GetBindPositionY(), plObj->GetBindPositionZ(), 0); plObj->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); return; } else { obj->GetPositionV()->ChangeCoords(plObj->GetBindPositionX(), plObj->GetBindPositionY(), plObj->GetBindPositionZ(), 0); plObj->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); plObj->SendTeleportAckMsg(plObj->GetPosition()); } } else { obj->GetPositionV()->ChangeCoords(0, 0, 0, 0); } } uint32 cellX = GetPosX(obj->GetPositionX()); uint32 cellY = GetPosY(obj->GetPositionY()); if(cellX >= _sizeX || cellY >= _sizeY) { return; } MapCell* objCell = GetCell(cellX, cellY); MapCell* pOldCell = obj->GetMapCell(); if(objCell == NULL) { objCell = Create(cellX, cellY); objCell->Init(cellX, cellY, this); } ARCEMU_ASSERT(objCell != NULL); // If object moved cell if(objCell != pOldCell) { // THIS IS A HACK! // Current code, if a creature on a long waypoint path moves from an active // cell into an inactive one, it will disable itself and will never return. // This is to prevent cpu leaks. I will think of a better solution very soon :P if(!objCell->IsActive() && !plObj && obj->IsActive()) obj->Deactivate(this); if(pOldCell != NULL) pOldCell->RemoveObject(obj); objCell->AddObject(obj); obj->SetMapCell(objCell); // if player we need to update cell activity // radius = 2 is used in order to update both // old and new cells if(obj->IsPlayer()) { // have to unlock/lock here to avoid a deadlock situation. UpdateCellActivity(cellX, cellY, 2); if(pOldCell != NULL) { // only do the second check if there's -/+ 2 difference if(abs((int)cellX - (int)pOldCell->_x) > 2 || abs((int)cellY - (int)pOldCell->_y) > 2) { UpdateCellActivity(pOldCell->_x, pOldCell->_y, 2); } } } } ////////////////////////////////////// // Update in-range set for new objects ////////////////////////////////////// uint32 endX = cellX <= _sizeX ? cellX + 1 : (_sizeX - 1); uint32 endY = cellY <= _sizeY ? cellY + 1 : (_sizeY - 1); uint32 startX = cellX > 0 ? cellX - 1 : 0; uint32 startY = cellY > 0 ? cellY - 1 : 0; uint32 posX, posY; MapCell* cell; //If the object announcing it's position is a special one, then it should do so in a much wider area - like the distance between the two transport towers in Orgrimmar, or more. - By: VLack if(obj->IsGameObject() && (TO< GameObject* >(obj)->GetOverrides() & GAMEOBJECT_ONMOVEWIDE)) { endX = cellX + 5 <= _sizeX ? cellX + 6 : (_sizeX - 1); endY = cellY + 5 <= _sizeY ? cellY + 6 : (_sizeY - 1); startX = cellX > 5 ? cellX - 6 : 0; startY = cellY > 5 ? cellY - 6 : 0; } for(posX = startX; posX <= endX; ++posX) { for(posY = startY; posY <= endY; ++posY) { cell = GetCell(posX, posY); if(cell) UpdateInRangeSet(obj, plObj, cell, &buf); } } if(buf) delete buf; }
bool CGUIInstance::Update() { // reset render info m_VertexInfo.clear(); if (m_ItemRef) { m_ItemRef->Update(); m_ItemRef->GetRenderParams(m_VertexInfo, GetPosX(), GetPosY(), GetWidth(), GetHeight(), m_State, m_fAngle, m_iColor); vector<GUIFONTINFO> newFontData; m_ItemRef->GetFontParams(newFontData, GetPosX(), GetPosY(), GetWidth(), GetHeight(), m_State); for (UINT i=0; i<newFontData.size(); i++) { DWORD dwNewPrimaryFont = newFontData[i].dwPrimaryFontName; DWORD dwNewSecondaryFont = newFontData[i].dwSecondaryFontName; DWORD dwNewText = newFontData[i].dwText; if (i >= m_pFont.size()) { // create new fonts! CREATEFONTINTERFACE cfi; static DWORD msgHash_CreateFont = CHashString(_T("CreateFont")).GetUniqueID(); EngineGetToolBox()->SendMessage(msgHash_CreateFont, sizeof(CREATEFONTINTERFACE), &cfi); if (cfi.m_FontInterface == NULL) { EngineGetToolBox()->Log(LOGERROR, _T("Instance could not create a new font!\n")); assert(cfi.m_FontInterface); return false; } m_pFont.push_back(cfi.m_FontInterface); m_pFont[i]->UseKerning(false); m_pFont[i]->SetKerningScale( 1.0f ); m_pFont[i]->SetVisible( true ); m_pFont[i]->SetLeadingScale( 1.0f ); } if (i >= m_FontInfo.size()) { m_pFont[i]->SetFont(m_ToolBox->GetHashString(dwNewPrimaryFont), newFontData[i].iFontSize); m_pFont[i]->SetFontBold(m_ToolBox->GetHashString(dwNewSecondaryFont), newFontData[i].iFontSize); m_pFont[i]->SetRenderRect(newFontData[i].renderRect.left, newFontData[i].renderRect.top, newFontData[i].renderRect.right, newFontData[i].renderRect.bottom); StdString colorAdjust = MultiplyTextColor(m_ToolBox->GetHashString(dwNewText)); m_pFont[i]->SetText(colorAdjust); if (newFontData[i].bCentered) { float centerX = GetWidth() / 2.f; float centerY = GetHeight() / 2.f; StdString cleanStr = RemoveFormatting(m_ToolBox->GetHashString(dwNewText)); int textWidth = m_pFont[i]->GetFontPixWidth(cleanStr); int textHeight = newFontData[i].iFontSize * m_pFont[i]->GetNumLines(); float textPosX = centerX - ((float)textWidth / 2.f) + newFontData[i].iXOffset; float textPosY = centerY - ((float)textHeight / 2.f) + newFontData[i].iYOffset; m_pFont[i]->SetOffset(textPosX, textPosY); } else { m_pFont[i]->SetOffset((float)newFontData[i].iXOffset, (float)newFontData[i].iYOffset); } m_pFont[i]->SetPageOffset(m_iPageOffsetX, m_iPageOffsetY); m_pFont[i]->SetWidthRatio(m_fWidthRatio); m_pFont[i]->SetHeightRatio(m_fHeightRatio); m_pFont[i]->SetZoomFactor(m_fZoomFactor); if (newFontData[i].bUseShadow) { m_pFont[i]->EnableDropShadow(true); m_pFont[i]->SetShadowVals(newFontData[i].iShadowColor, newFontData[i].iShadowXScale, newFontData[i].iShadowYScale, newFontData[i].iShadowXOffset, newFontData[i].iShadowYOffset); } GUIFONTINFO entry; entry.dwPrimaryFontName = newFontData[i].dwPrimaryFontName; entry.dwSecondaryFontName = newFontData[i].dwSecondaryFontName; entry.dwText = newFontData[i].dwText; entry.iFontSize = newFontData[i].iFontSize; entry.bCentered = newFontData[i].bCentered; entry.iXOffset = newFontData[i].iXOffset; entry.iYOffset = newFontData[i].iYOffset; entry.renderRect = newFontData[i].renderRect; entry.clippingRect = newFontData[i].clippingRect; entry.bUseClippingRect = newFontData[i].bUseClippingRect; entry.bRender = true; entry.bUseShadow = newFontData[i].bUseShadow; entry.iShadowColor = newFontData[i].iShadowColor; entry.iShadowXScale = newFontData[i].iShadowXScale; entry.iShadowYScale = newFontData[i].iShadowYScale; entry.iShadowXOffset = newFontData[i].iShadowXOffset; entry.iShadowYOffset = newFontData[i].iShadowYOffset; m_FontInfo.push_back(entry); } // compare elements else { if (m_FontInfo[i].dwPrimaryFontName != newFontData[i].dwPrimaryFontName) { m_pFont[i]->SetFont(m_ToolBox->GetHashString(dwNewPrimaryFont), newFontData[i].iFontSize); m_FontInfo[i].dwPrimaryFontName = newFontData[i].dwPrimaryFontName; m_FontInfo[i].iFontSize = newFontData[i].iFontSize; } if (m_FontInfo[i].dwSecondaryFontName != newFontData[i].dwSecondaryFontName) { m_pFont[i]->SetFontBold(m_ToolBox->GetHashString(dwNewSecondaryFont), newFontData[i].iFontSize); m_FontInfo[i].dwSecondaryFontName = newFontData[i].dwSecondaryFontName; m_FontInfo[i].iFontSize = newFontData[i].iFontSize; } if (m_FontInfo[i].iFontSize != newFontData[i].iFontSize) { m_pFont[i]->SetFont(m_ToolBox->GetHashString(dwNewPrimaryFont), newFontData[i].iFontSize); m_pFont[i]->SetFontBold(m_ToolBox->GetHashString(dwNewSecondaryFont), newFontData[i].iFontSize); m_FontInfo[i].iFontSize = newFontData[i].iFontSize; } if (m_FontInfo[i].dwText != newFontData[i].dwText) { StdString colorAdjust = MultiplyTextColor(m_ToolBox->GetHashString(dwNewText)); m_pFont[i]->SetText(colorAdjust); m_FontInfo[i].dwText = newFontData[i].dwText; if (newFontData[i].bCentered) { float centerX = GetWidth() / 2.f; float centerY = GetHeight() / 2.f; StdString cleanStr = RemoveFormatting(m_ToolBox->GetHashString(dwNewText)); int textWidth = m_pFont[i]->GetFontPixWidth(cleanStr); int textHeight = m_FontInfo[i].iFontSize * m_pFont[i]->GetNumLines(); float textPosX = centerX - ((float)textWidth / 2.f) + m_FontInfo[i].iXOffset; float textPosY = centerY - ((float)textHeight / 2.f) + m_FontInfo[i].iYOffset; m_pFont[i]->SetOffset(textPosX, textPosY); } } if (m_FontInfo[i].bCentered != newFontData[i].bCentered) { m_FontInfo[i].bCentered = newFontData[i].bCentered; if (newFontData[i].bCentered) { float centerX = GetWidth() / 2.f; float centerY = GetHeight() / 2.f; StdString cleanStr = RemoveFormatting(m_ToolBox->GetHashString(dwNewText)); int textWidth = m_pFont[i]->GetFontPixWidth(cleanStr); int textHeight = m_FontInfo[i].iFontSize * m_pFont[i]->GetNumLines(); float textPosX = centerX - ((float)textWidth / 2.f) + newFontData[i].iXOffset; float textPosY = centerY - ((float)textHeight / 2.f) + newFontData[i].iYOffset; m_pFont[i]->SetOffset(textPosX, textPosY); } else { m_pFont[i]->SetOffset((float)newFontData[i].iXOffset, (float)newFontData[i].iYOffset); } } if ((m_FontInfo[i].iXOffset != newFontData[i].iXOffset) || (m_FontInfo[i].iYOffset != newFontData[i].iYOffset)) { if (!m_FontInfo[i].bCentered) { m_pFont[i]->SetOffset((float)newFontData[i].iXOffset, (float)newFontData[i].iYOffset); } m_FontInfo[i].iXOffset = newFontData[i].iXOffset; m_FontInfo[i].iYOffset = newFontData[i].iYOffset; } if ((m_FontInfo[i].renderRect.bottom != newFontData[i].renderRect.bottom) || (m_FontInfo[i].renderRect.top != newFontData[i].renderRect.top) || (m_FontInfo[i].renderRect.left != newFontData[i].renderRect.left) || (m_FontInfo[i].renderRect.right != newFontData[i].renderRect.right)) { m_pFont[i]->SetRenderRect(newFontData[i].renderRect.left, newFontData[i].renderRect.top, newFontData[i].renderRect.right, newFontData[i].renderRect.bottom); m_FontInfo[i].renderRect = newFontData[i].renderRect; if (newFontData[i].bCentered) { float centerX = GetWidth() / 2.f; float centerY = GetHeight() / 2.f; StdString cleanStr = RemoveFormatting(m_ToolBox->GetHashString(dwNewText)); int textWidth = m_pFont[i]->GetFontPixWidth(cleanStr); int textHeight = m_FontInfo[i].iFontSize * m_pFont[i]->GetNumLines(); float textPosX = centerX - ((float)textWidth / 2.f) + newFontData[i].iXOffset; float textPosY = centerY - ((float)textHeight / 2.f) + newFontData[i].iYOffset; m_pFont[i]->SetOffset(textPosX, textPosY); } else { m_pFont[i]->SetOffset((float)newFontData[i].iXOffset, (float)newFontData[i].iYOffset); } } if (m_FontInfo[i].bUseShadow != newFontData[i].bUseShadow) { m_FontInfo[i].bUseShadow = newFontData[i].bUseShadow; m_FontInfo[i].iShadowColor = newFontData[i].iShadowColor; m_FontInfo[i].iShadowXScale = newFontData[i].iShadowXScale; m_FontInfo[i].iShadowYScale = newFontData[i].iShadowYScale; m_FontInfo[i].iShadowXOffset = newFontData[i].iShadowXOffset; m_FontInfo[i].iShadowYOffset = newFontData[i].iShadowYOffset; if (m_FontInfo[i].bUseShadow) { m_pFont[i]->EnableDropShadow(true); m_pFont[i]->SetShadowVals(m_FontInfo[i].iShadowColor, m_FontInfo[i].iShadowXScale, m_FontInfo[i].iShadowYScale, m_FontInfo[i].iShadowXOffset, m_FontInfo[i].iShadowYOffset); } else { m_pFont[i]->EnableDropShadow(false); } } else if(m_FontInfo[i].bUseShadow) { m_FontInfo[i].iShadowColor = newFontData[i].iShadowColor; m_FontInfo[i].iShadowXScale = newFontData[i].iShadowXScale; m_FontInfo[i].iShadowYScale = newFontData[i].iShadowYScale; m_FontInfo[i].iShadowXOffset = newFontData[i].iShadowXOffset; m_FontInfo[i].iShadowYOffset = newFontData[i].iShadowYOffset; m_pFont[i]->SetShadowVals(m_FontInfo[i].iShadowColor, m_FontInfo[i].iShadowXScale, m_FontInfo[i].iShadowYScale, m_FontInfo[i].iShadowXOffset, m_FontInfo[i].iShadowYOffset); } m_FontInfo[i].clippingRect = newFontData[i].clippingRect; m_FontInfo[i].bUseClippingRect = newFontData[i].bUseClippingRect; m_FontInfo[i].bRender = true; } if (m_bColorChanged) { StdString colorChanged = MultiplyTextColor(m_ToolBox->GetHashString(dwNewText)); m_pFont[i]->SetText(colorChanged); } } // disable any fonts that haven't been updated for (UINT j=newFontData.size(); j<m_FontInfo.size(); j++) { m_FontInfo[j].bRender = false; } } return true; }
void MapMgr::RemoveObject(Object* obj, bool free_guid) { ///////////// // Assertions ///////////// ARCEMU_ASSERT(obj != NULL); ARCEMU_ASSERT(obj->GetMapId() == _mapId); //ARCEMU_ASSERT( obj->GetPositionX() > _minX && obj->GetPositionX() < _maxX); //ARCEMU_ASSERT( obj->GetPositionY() > _minY && obj->GetPositionY() < _maxY); ARCEMU_ASSERT(_cells != NULL); if(obj->IsActive()) obj->Deactivate(this); //there is a very small chance that on double player ports on same update player is added to multiple insertpools but not removed //one clear example was the double port proc when exploiting double resurrect m_objectinsertlock.Acquire(); m_objectinsertpool.erase(obj); m_objectinsertlock.Release(); _updates.erase(obj); obj->ClearUpdateMask(); /////////////////////////////////////// // Remove object from all needed places /////////////////////////////////////// switch(obj->GetTypeFromGUID()) { case HIGHGUID_TYPE_UNIT: case HIGHGUID_TYPE_VEHICLE: ARCEMU_ASSERT(obj->GetUIdFromGUID() <= m_CreatureHighGuid); CreatureStorage[ obj->GetUIdFromGUID() ] = NULL; if(TO_CREATURE(obj)->m_spawn != NULL) { _sqlids_creatures.erase(TO_CREATURE(obj)->m_spawn->id); } if(free_guid) _reusable_guids_creature.push_back(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_PET: if(pet_iterator != m_PetStorage.end() && pet_iterator->second->GetGUID() == obj->GetGUID()) ++pet_iterator; m_PetStorage.erase(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_DYNAMICOBJECT: m_DynamicObjectStorage.erase(obj->GetLowGUID()); break; case HIGHGUID_TYPE_GAMEOBJECT: ARCEMU_ASSERT(obj->GetUIdFromGUID() <= m_GOHighGuid); GOStorage[ obj->GetUIdFromGUID() ] = NULL; if(TO_GAMEOBJECT(obj)->m_spawn != NULL) { _sqlids_gameobjects.erase(TO_GAMEOBJECT(obj)->m_spawn->id); } if(free_guid) _reusable_guids_gameobject.push_back(obj->GetUIdFromGUID()); break; } // That object types are not map objects. TODO: add AI groups here? if(obj->IsItem() || obj->IsContainer()) { return; } if(obj->IsCorpse()) { m_corpses.erase(TO< Corpse* >(obj)); } MapCell* cell = GetCell(obj->GetMapCellX(), obj->GetMapCellY()); if(cell == NULL) { /* set the map cell correctly */ if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { cell = this->GetCellByCoords(obj->GetPositionX(), obj->GetPositionY()); obj->SetMapCell(cell); } } if(cell != NULL) { // Remove object from cell cell->RemoveObject(obj); // Unset object's cell obj->SetMapCell(NULL); } Player* plObj = NULL; // Clear any updates pending if(obj->IsPlayer()) { plObj = TO_PLAYER(obj); _processQueue.erase(plObj); plObj->ClearAllPendingUpdates(); } obj->RemoveSelfFromInrangeSets(); // Clear object's in-range set obj->ClearInRangeSet(); // If it's a player - update his nearby cells if(!_shutdown && obj->IsPlayer()) { // get x/y if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { uint32 x = GetPosX(obj->GetPositionX()); uint32 y = GetPosY(obj->GetPositionY()); UpdateCellActivity(x, y, 2); } m_PlayerStorage.erase(TO< Player* >(obj)->GetLowGUID()); } // Remove the session from our set if it is a player. if(obj->IsPlayer()) { for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) { plObj->PushOutOfRange((*itr)->GetNewGUID()); } // Setting an instance ID here will trigger the session to be removed // by MapMgr::run(). :) plObj->GetSession()->SetInstance(0); // Add it to the global session set. // Don't "re-add" to session if it is being deleted. if(!plObj->GetSession()->bDeleted) sWorld.AddGlobalSession(plObj->GetSession()); } if(!HasPlayers()) { if(this->pInstance != NULL && this->pInstance->m_persistent) this->pInstance->m_creatorGroup = 0; if(!InactiveMoveTime && !forced_expire && GetMapInfo()->type != INSTANCE_NULL) { InactiveMoveTime = UNIXTIME + (MAPMGR_INACTIVE_MOVE_TIME * 60); Log.Debug("MapMgr", "Instance %u is now idle. (%s)", m_instanceID, GetBaseMap()->GetName()); } } }
void MapMgr::PushObject(Object* obj) { ///////////// // Assertions ///////////// ARCEMU_ASSERT(obj != NULL); // That object types are not map objects. TODO: add AI groups here? if(obj->IsItem() || obj->IsContainer()) { // mark object as updatable and exit return; } if(obj->IsCorpse()) { m_corpses.insert(TO< Corpse* >(obj)); } obj->ClearInRangeSet(); ARCEMU_ASSERT(obj->GetMapId() == _mapId); if(!(obj->GetPositionX() < _maxX && obj->GetPositionX() > _minX) || !(obj->GetPositionY() < _maxY && obj->GetPositionY() > _minY)) { if(obj->IsPlayer()) { Player* plr = TO< Player* >(obj); if(plr->GetBindMapId() != GetMapId()) { plr->SafeTeleport(plr->GetBindMapId(), 0, plr->GetBindPositionX(), plr->GetBindPositionY(), plr->GetBindPositionZ(), 0); plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); return; } else { obj->GetPositionV()->ChangeCoords(plr->GetBindPositionX(), plr->GetBindPositionY(), plr->GetBindPositionZ(), 0); plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); plr->SendTeleportAckMsg(plr->GetPosition()); } } else { obj->GetPositionV()->ChangeCoords(0, 0, 0, 0); } } ARCEMU_ASSERT(obj->GetPositionY() < _maxY && obj->GetPositionY() > _minY); ARCEMU_ASSERT(_cells != NULL); /////////////////////// // Get cell coordinates /////////////////////// uint32 x = GetPosX(obj->GetPositionX()); uint32 y = GetPosY(obj->GetPositionY()); if(x >= _sizeX || y >= _sizeY) { if(obj->IsPlayer()) { Player* plr = TO< Player* >(obj); if(plr->GetBindMapId() != GetMapId()) { plr->SafeTeleport(plr->GetBindMapId(), 0, plr->GetBindPositionX(), plr->GetBindPositionY(), plr->GetBindPositionZ(), 0); plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); return; } else { obj->GetPositionV()->ChangeCoords(plr->GetBindPositionX(), plr->GetBindPositionY(), plr->GetBindPositionZ(), 0); plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); plr->SendTeleportAckMsg(plr->GetPosition()); } } else { obj->GetPositionV()->ChangeCoords(0, 0, 0, 0); } x = GetPosX(obj->GetPositionX()); y = GetPosY(obj->GetPositionY()); } MapCell* objCell = GetCell(x, y); if(objCell == NULL) { objCell = Create(x, y); objCell->Init(x, y, this); } ARCEMU_ASSERT(objCell != NULL); uint32 endX = (x <= _sizeX) ? x + 1 : (_sizeX - 1); uint32 endY = (y <= _sizeY) ? y + 1 : (_sizeY - 1); uint32 startX = x > 0 ? x - 1 : 0; uint32 startY = y > 0 ? y - 1 : 0; uint32 posX, posY; MapCell* cell; //MapCell::ObjectSet::iterator iter; ByteBuffer* buf = 0; uint32 count; Player* plObj; if(obj->IsPlayer()) plObj = TO< Player* >(obj); else plObj = NULL; if(plObj != NULL) { LOG_DETAIL("Creating player " I64FMT " for himself.", obj->GetGUID()); ByteBuffer pbuf(10000); count = plObj->BuildCreateUpdateBlockForPlayer(&pbuf, plObj); plObj->PushCreationData(&pbuf, count); } ////////////////////// // Build in-range data ////////////////////// for(posX = startX; posX <= endX; posX++) { for(posY = startY; posY <= endY; posY++) { cell = GetCell(posX, posY); if(cell) { UpdateInRangeSet(obj, plObj, cell, &buf); } } } //Add to the cell's object list objCell->AddObject(obj); obj->SetMapCell(objCell); //Add to the mapmanager's object list if(plObj != NULL) { m_PlayerStorage[plObj->GetLowGUID()] = plObj; UpdateCellActivity(x, y, 2); } else { switch(obj->GetTypeFromGUID()) { case HIGHGUID_TYPE_PET: m_PetStorage[obj->GetUIdFromGUID()] = TO< Pet* >(obj); break; case HIGHGUID_TYPE_UNIT: case HIGHGUID_TYPE_VEHICLE: { ARCEMU_ASSERT(obj->GetUIdFromGUID() <= m_CreatureHighGuid); CreatureStorage[ obj->GetUIdFromGUID() ] = TO< Creature* >(obj); if(TO_CREATURE(obj)->m_spawn != NULL) { _sqlids_creatures.insert(make_pair(TO_CREATURE(obj)->m_spawn->id, TO_CREATURE(obj))); } } break; case HIGHGUID_TYPE_GAMEOBJECT: { GOStorage[ obj->GetUIdFromGUID() ] = TO< GameObject* >(obj); if(TO_GAMEOBJECT(obj)->m_spawn != NULL) { _sqlids_gameobjects.insert(make_pair(TO_GAMEOBJECT(obj)->m_spawn->id, TO_GAMEOBJECT(obj))); } } break; case HIGHGUID_TYPE_DYNAMICOBJECT: m_DynamicObjectStorage[obj->GetLowGUID()] = (DynamicObject*)obj; break; } } // Handle activation of that object. if(objCell->IsActive() && obj->CanActivate()) obj->Activate(this); // Add the session to our set if it is a player. if(plObj != NULL) { Sessions.insert(plObj->GetSession()); // Change the instance ID, this will cause it to be removed from the world thread (return value 1) plObj->GetSession()->SetInstance(GetInstanceID()); /* Add the map wide objects */ if(_mapWideStaticObjects.size()) { uint32 globalcount = 0; if(!buf) buf = new ByteBuffer(300); for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) { count = (*itr)->BuildCreateUpdateBlockForPlayer(buf, plObj); globalcount += count; } //VLack: It seems if we use the same buffer then it is a BAD idea to try and push created data one by one, add them at once! // If you try to add them one by one, then as the buffer already contains data, they'll end up repeating some object. // Like 6 object updates for Deeprun Tram, but the built package will contain these entries: 2AFD0, 2AFD0, 2AFD1, 2AFD0, 2AFD1, 2AFD2 if(globalcount > 0) plObj->PushCreationData(buf, globalcount); } } if(buf) delete buf; if(plObj != NULL && InactiveMoveTime && !forced_expire) InactiveMoveTime = 0; }
void CBase::Update(float fElapsedTime) { SetPosX( GetPosX() + GetVelX() * fElapsedTime ); SetPosY( GetPosY() + GetVelY() * fElapsedTime ); }
cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : super(etPlayer, 0.6, 1.8) , m_bVisible(true) , m_FoodLevel(MAX_FOOD_LEVEL) , m_FoodSaturationLevel(5) , m_FoodTickTimer(0) , m_FoodExhaustionLevel(0) , m_FoodPoisonedTicksRemaining(0) , m_LastJumpHeight(0) , m_LastGroundHeight(0) , m_bTouchGround(false) , m_Stance(0.0) , m_Inventory(*this) , m_CurrentWindow(NULL) , m_InventoryWindow(NULL) , m_Color('-') , m_GameMode(eGameMode_NotSet) , m_IP("") , m_ClientHandle(a_Client) , m_NormalMaxSpeed(1.0) , m_SprintingMaxSpeed(1.3) , m_FlyingMaxSpeed(1.0) , m_IsCrouched(false) , m_IsSprinting(false) , m_IsFlying(false) , m_IsSwimming(false) , m_IsSubmerged(false) , m_IsFishing(false) , m_CanFly(false) , m_EatingFinishTick(-1) , m_LifetimeTotalXp(0) , m_CurrentXp(0) , m_bDirtyExperience(false) , m_IsChargingBow(false) , m_BowCharge(0) , m_FloaterID(-1) , m_Team(NULL) { LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", a_PlayerName.c_str(), a_Client->GetIPString().c_str(), this, GetUniqueID() ); m_InventoryWindow = new cInventoryWindow(*this); m_CurrentWindow = m_InventoryWindow; m_InventoryWindow->OpenedByPlayer(*this); SetMaxHealth(MAX_HEALTH); m_Health = MAX_HEALTH; cTimer t1; m_LastPlayerListTime = t1.GetNowTime(); m_TimeLastTeleportPacket = 0; m_PlayerName = a_PlayerName; m_bDirtyPosition = true; // So chunks are streamed to player at spawn if (!LoadFromDisk()) { m_Inventory.Clear(); SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX()); SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY()); SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ()); LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() ); } m_LastJumpHeight = (float)(GetPosY()); m_LastGroundHeight = (float)(GetPosY()); m_Stance = GetPosY() + 1.62; if (m_GameMode == gmNotSet) { cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName()); if (World == NULL) { World = cRoot::Get()->GetDefaultWorld(); } if (World->IsGameModeCreative()) { m_CanFly = true; } } cRoot::Get()->GetServer()->PlayerCreated(this); }
void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) { //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2)) { m_World->BroadcastEntityVelocity(*this,a_Exclude); m_bDirtySpeed = false; m_TimeLastSpeedPacket = m_World->GetWorldAge(); } //Have to process position related packets this every two ticks if (m_World->GetWorldAge() % 2 == 0) { int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0)); int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0)); int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0)); Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket; // 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds if (DiffTeleportPacket >= 400 || ((DiffX > 127) || (DiffX < -128) || (DiffY > 127) || (DiffY < -128) || (DiffZ > 127) || (DiffZ < -128))) { // m_World->BroadcastTeleportEntity(*this,a_Exclude); m_TimeLastTeleportPacket = m_World->GetWorldAge(); m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize. m_LastPosX = GetPosX(); m_LastPosY = GetPosY(); m_LastPosZ = GetPosZ(); m_bDirtyPosition = false; m_bDirtyOrientation = false; } else { Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket; //if the change is big enough. if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition) { if (m_bDirtyOrientation) { m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); m_bDirtyOrientation = false; } else { m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); } m_LastPosX = GetPosX(); m_LastPosY = GetPosY(); m_LastPosZ = GetPosZ(); m_bDirtyPosition = false; m_TimeLastMoveReltPacket = m_World->GetWorldAge(); } else { if (m_bDirtyOrientation) { m_World->BroadcastEntityLook(*this,a_Exclude); m_bDirtyOrientation = false; } } } if (m_bDirtyHead) { m_World->BroadcastEntityHeadLook(*this,a_Exclude); m_bDirtyHead = false; } } }
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) { if (IsDestroyed()) // Mainly to stop detector rails triggering again after minecart is dead { return; } int PosY = POSY_TOINT; if ((PosY <= 0) || (PosY >= cChunkDef::Height)) { // Outside the world, just process normal falling physics super::HandlePhysics(a_Dt, a_Chunk); BroadcastMovementUpdate(); return; } int RelPosX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width; int RelPosZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width; cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); if (Chunk == nullptr) { // Inside an unloaded chunk, bail out all processing return; } BLOCKTYPE InsideType; NIBBLETYPE InsideMeta; Chunk->GetBlockTypeMeta(RelPosX, PosY, RelPosZ, InsideType, InsideMeta); if (!IsBlockRail(InsideType)) { // When a descending minecart hits a flat rail, it goes through the ground; check for this Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta); if (IsBlockRail(InsideType)) { // Push cart upwards AddPosY(1); } } bool WasDetectorRail = false; if (IsBlockRail(InsideType)) { if (InsideType == E_BLOCK_RAIL) { SnapToRail(InsideMeta); } else { SnapToRail(InsideMeta & 0x07); } switch (InsideType) { case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt); break; case E_BLOCK_ACTIVATOR_RAIL: break; case E_BLOCK_POWERED_RAIL: HandlePoweredRailPhysics(InsideMeta); break; case E_BLOCK_DETECTOR_RAIL: { HandleDetectorRailPhysics(InsideMeta, a_Dt); WasDetectorRail = true; break; } default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break; } AddPosition(GetSpeed() * (a_Dt / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp } else { // Not on rail, default physics SetPosY(floor(GetPosY()) + 0.35); // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail super::HandlePhysics(a_Dt, *Chunk); } if (m_bIsOnDetectorRail && !Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT).Equals(m_DetectorRailPosition)) { m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07); m_bIsOnDetectorRail = false; } else if (WasDetectorRail) { m_bIsOnDetectorRail = true; m_DetectorRailPosition = Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT); } // Broadcast positioning changes to client BroadcastMovementUpdate(); }
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) { // TODO Add collision detection with entities. a_Dt /= 1000; // Convert from msec to sec Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ()); Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ()); int BlockX = (int) floor(NextPos.x); int BlockY = (int) floor(NextPos.y); int BlockZ = (int) floor(NextPos.z); if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) { // Outside of the world cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); // See if we can commit our changes. If not, we will discard them. if (NextChunk != NULL) { SetSpeed(NextSpeed); NextPos += (NextSpeed * a_Dt); SetPosition(NextPos); } return; } // Make sure we got the correct chunk and a valid one. No one ever knows... cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); if (NextChunk != NULL) { int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width); int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block { if (m_bOnGround) // check if it's still on the ground { if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water. { m_bOnGround = false; } } } else { // Push out entity. BLOCKTYPE GotBlock; static const struct { int x, y, z; } gCrossCoords[] = { { 1, 0, 0}, {-1, 0, 0}, { 0, 0, 1}, { 0, 0, -1}, } ; bool IsNoAirSurrounding = true; for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) { if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock)) { // The pickup is too close to an unloaded chunk, bail out of any physics handling return; } if (!g_BlockIsSolid[GotBlock]) { NextPos.x += gCrossCoords[i].x; NextPos.z += gCrossCoords[i].z; IsNoAirSurrounding = false; break; } } // for i - gCrossCoords[] if (IsNoAirSurrounding) { NextPos.y += 0.5; } m_bOnGround = true; LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}", m_UniqueID, GetClass(), BlockX, BlockY, BlockZ ); } if (!m_bOnGround) { float fallspeed; if (IsBlockWater(BlockIn)) { fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water. } else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts { fallspeed = 0; m_bOnGround = true; } else if (BlockIn == E_BLOCK_COBWEB) { NextSpeed.y *= 0.05; // Reduce overall falling speed fallspeed = 0; // No falling. } else { // Normal gravity fallspeed = m_Gravity * a_Dt; } NextSpeed.y += fallspeed; } else { if (IsMinecart()) { if (!IsBlockRail(BlockBelow)) { // Friction if minecart is off track, otherwise, Minecart.cpp handles this if (NextSpeed.SqrLength() > 0.0004f) { NextSpeed.x *= 0.7f / (1 + a_Dt); if (fabs(NextSpeed.x) < 0.05) { NextSpeed.x = 0; } NextSpeed.z *= 0.7f / (1 + a_Dt); if (fabs(NextSpeed.z) < 0.05) { NextSpeed.z = 0; } } } } else { // Friction for non-minecarts if (NextSpeed.SqrLength() > 0.0004f) { NextSpeed.x *= 0.7f / (1 + a_Dt); if (fabs(NextSpeed.x) < 0.05) { NextSpeed.x = 0; } NextSpeed.z *= 0.7f / (1 + a_Dt); if (fabs(NextSpeed.z) < 0.05) { NextSpeed.z = 0; } } } } // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we // might have different speed modifiers according to terrain. if (BlockIn == E_BLOCK_COBWEB) { NextSpeed.x *= 0.25; NextSpeed.z *= 0.25; } //Get water direction Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ); m_WaterSpeed *= 0.9f; //Reduce speed each tick switch(WaterDir) { case X_PLUS: m_WaterSpeed.x = 0.2f; m_bOnGround = false; break; case X_MINUS: m_WaterSpeed.x = -0.2f; m_bOnGround = false; break; case Z_PLUS: m_WaterSpeed.z = 0.2f; m_bOnGround = false; break; case Z_MINUS: m_WaterSpeed.z = -0.2f; m_bOnGround = false; break; default: break; } if (fabs(m_WaterSpeed.x) < 0.05) { m_WaterSpeed.x = 0; } if (fabs(m_WaterSpeed.z) < 0.05) { m_WaterSpeed.z = 0; } NextSpeed += m_WaterSpeed; if( NextSpeed.SqrLength() > 0.f ) { cTracer Tracer( GetWorld() ); int Ret = Tracer.Trace( NextPos, NextSpeed, 2 ); if( Ret ) // Oh noez! we hit something { // Set to hit position if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() ) { if( Ret == 1 ) { if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f; if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f; if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f; if( Tracer.HitNormal.y > 0 ) // means on ground { m_bOnGround = true; } } NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z); NextPos.x += Tracer.HitNormal.x * 0.3f; NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot NextPos.z += Tracer.HitNormal.z * 0.3f; } else { NextPos += (NextSpeed * a_Dt); } } else { // We didn't hit anything, so move =] NextPos += (NextSpeed * a_Dt); } } BlockX = (int) floor(NextPos.x); BlockZ = (int) floor(NextPos.z); NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ); // See if we can commit our changes. If not, we will discard them. if (NextChunk != NULL) { if (NextPos.x != GetPosX()) SetPosX(NextPos.x); if (NextPos.y != GetPosY()) SetPosY(NextPos.y); if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z); if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x); if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y); if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z); } } }
void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) { /* NOTE: Please bear in mind that taking away from negatives make them even more negative, adding to negatives make them positive, etc. */ switch (a_RailMeta) { case E_META_RAIL_ZM_ZP: // NORTHSOUTH { SetYaw(270); SetPosY(floor(GetPosY()) + 0.55); SetSpeedY(0); // Don't move vertically as on ground SetSpeedX(0); // Correct diagonal movement from curved rails // Execute both the entity and block collision checks bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); if (EntCol || BlckCol) { return; } if (GetSpeedZ() != NO_SPEED) // Don't do anything if cart is stationary { if (GetSpeedZ() > 0) { // Going SOUTH, slow down AddSpeedZ(-0.1); } else { // Going NORTH, slow down AddSpeedZ(0.1); } } break; } case E_META_RAIL_XM_XP: // EASTWEST { SetYaw(180); SetPosY(floor(GetPosY()) + 0.55); SetSpeedY(NO_SPEED); SetSpeedZ(NO_SPEED); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); if (EntCol || BlckCol) { return; } if (GetSpeedX() != NO_SPEED) { if (GetSpeedX() > 0) { AddSpeedX(-0.1); } else { AddSpeedX(0.1); } } break; } case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH { SetYaw(270); SetSpeedX(0); if (GetSpeedZ() >= 0) { // SpeedZ POSITIVE, going SOUTH if (GetSpeedZ() <= MAX_SPEED) // Speed limit { AddSpeedZ(0.5); // Speed up SetSpeedY(-GetSpeedZ()); // Downward movement is negative (0 minus positive numbers is negative) } } else { // SpeedZ NEGATIVE, going NORTH AddSpeedZ(1); // Slow down SetSpeedY(-GetSpeedZ()); // Upward movement is positive (0 minus negative number is positive number) } break; } case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH { SetYaw(270); SetSpeedX(0); if (GetSpeedZ() > 0) { // SpeedZ POSITIVE, going SOUTH AddSpeedZ(-1); // Slow down SetSpeedY(GetSpeedZ()); // Upward movement positive } else { if (GetSpeedZ() >= MAX_SPEED_NEGATIVE) // Speed limit { // SpeedZ NEGATIVE, going NORTH AddSpeedZ(-0.5); // Speed up SetSpeedY(GetSpeedZ()); // Downward movement negative } } break; } case E_META_RAIL_ASCEND_XM: // ASCEND EAST { SetYaw(180); SetSpeedZ(NO_SPEED); if (GetSpeedX() >= NO_SPEED) { if (GetSpeedX() <= MAX_SPEED) { AddSpeedX(0.5); SetSpeedY(-GetSpeedX()); } } else { AddSpeedX(1); SetSpeedY(-GetSpeedX()); } break; } case E_META_RAIL_ASCEND_XP: // ASCEND WEST { SetYaw(180); SetSpeedZ(0); if (GetSpeedX() > 0) { AddSpeedX(-1); SetSpeedY(GetSpeedX()); } else { if (GetSpeedX() >= MAX_SPEED_NEGATIVE) { AddSpeedX(-0.5); SetSpeedY(GetSpeedX()); } } break; } case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST { SetYaw(315); // Set correct rotation server side SetPosY(floor(GetPosY()) + 0.55); // Levitate dat cart SetSpeedY(0); TestBlockCollision(a_RailMeta); TestEntityCollision(a_RailMeta); // SnapToRail handles turning break; } case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST { SetYaw(225); SetPosY(floor(GetPosY()) + 0.55); SetSpeedY(0); TestBlockCollision(a_RailMeta); TestEntityCollision(a_RailMeta); break; } case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST { SetYaw(135); SetPosY(floor(GetPosY()) + 0.55); SetSpeedY(0); TestBlockCollision(a_RailMeta); TestEntityCollision(a_RailMeta); break; } case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST { SetYaw(45); SetPosY(floor(GetPosY()) + 0.55); SetSpeedY(0); TestBlockCollision(a_RailMeta); TestEntityCollision(a_RailMeta); break; } default: { ASSERT(!"Unhandled rail meta!"); // Dun dun DUN! break; } } }
void cEntity::TickBurning(cChunk & a_Chunk) { // Remember the current burning state: bool HasBeenBurning = (m_TicksLeftBurning > 0); // Do the burning damage: if (m_TicksLeftBurning > 0) { m_TicksSinceLastBurnDamage++; if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE) { TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0); m_TicksSinceLastBurnDamage = 0; } m_TicksLeftBurning--; } // Update the burning times, based on surroundings: int MinRelX = (int)floor(GetPosX() - m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY()))); int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height))); bool HasWater = false; bool HasLava = false; bool HasFire = false; for (int x = MinRelX; x <= MaxRelX; x++) { for (int z = MinRelZ; z <= MaxRelZ; z++) { int RelX = x; int RelZ = z; cChunk * CurChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ); if (CurChunk == NULL) { continue; } for (int y = MinY; y <= MaxY; y++) { switch (CurChunk->GetBlock(RelX, y, RelZ)) { case E_BLOCK_FIRE: { HasFire = true; break; } case E_BLOCK_LAVA: case E_BLOCK_STATIONARY_LAVA: { HasLava = true; break; } case E_BLOCK_STATIONARY_WATER: case E_BLOCK_WATER: { HasWater = true; break; } } // switch (BlockType) } // for y } // for z } // for x if (HasWater) { // Extinguish the fire m_TicksLeftBurning = 0; } if (HasLava) { // Burn: m_TicksLeftBurning = BURN_TICKS; // Periodically damage: m_TicksSinceLastLavaDamage++; if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE) { TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0); m_TicksSinceLastLavaDamage = 0; } } else { m_TicksSinceLastLavaDamage = 0; } if (HasFire) { // Burn: m_TicksLeftBurning = BURN_TICKS; // Periodically damage: m_TicksSinceLastFireDamage++; if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE) { TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0); m_TicksSinceLastFireDamage = 0; } } else { m_TicksSinceLastFireDamage = 0; } // If just started / finished burning, notify descendants: if ((m_TicksLeftBurning > 0) && !HasBeenBurning) { OnStartedBurning(); } else if ((m_TicksLeftBurning <= 0) && HasBeenBurning) { OnFinishedBurning(); } }
bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta) { switch (a_RailMeta) { case E_META_RAIL_ZM_ZP: { if (GetSpeedZ() > 0) { BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, (int)ceil(GetPosZ())); if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block)) { // We could try to detect a block in front based purely on coordinates, but xoft made a bounding box system - why not use? :P cBoundingBox bbBlock(Vector3d(POSX_TOINT, POSY_TOINT, (int)ceil(GetPosZ())), 0.5, 1); cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight()); if (bbBlock.DoesIntersect(bbMinecart)) { SetSpeed(0, 0, 0); SetPosZ(floor(GetPosZ()) + 0.4); return true; } } } else if (GetSpeedZ() < 0) { BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1); if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block)) { cBoundingBox bbBlock(Vector3d(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1), 0.5, 1); cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ() - 1), GetWidth() / 2, GetHeight()); if (bbBlock.DoesIntersect(bbMinecart)) { SetSpeed(0, 0, 0); SetPosZ(floor(GetPosZ()) + 0.65); return true; } } } break; } case E_META_RAIL_XM_XP: { if (GetSpeedX() > 0) { BLOCKTYPE Block = m_World->GetBlock((int)ceil(GetPosX()), POSY_TOINT, POSZ_TOINT); if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block)) { cBoundingBox bbBlock(Vector3d((int)ceil(GetPosX()), POSY_TOINT, POSZ_TOINT), 0.5, 1); cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight()); if (bbBlock.DoesIntersect(bbMinecart)) { SetSpeed(0, 0, 0); SetPosX(floor(GetPosX()) + 0.4); return true; } } } else if (GetSpeedX() < 0) { BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT); if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block)) { cBoundingBox bbBlock(Vector3d(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT), 0.5, 1); cBoundingBox bbMinecart(Vector3d(GetPosX() - 1, floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight()); if (bbBlock.DoesIntersect(bbMinecart)) { SetSpeed(0, 0, 0); SetPosX(floor(GetPosX()) + 0.65); return true; } } } break; } case E_META_RAIL_CURVED_ZM_XM: case E_META_RAIL_CURVED_ZM_XP: case E_META_RAIL_CURVED_ZP_XM: case E_META_RAIL_CURVED_ZP_XP: { BLOCKTYPE BlockXM = m_World->GetBlock(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT); BLOCKTYPE BlockXP = m_World->GetBlock(POSX_TOINT + 1, POSY_TOINT, POSZ_TOINT); BLOCKTYPE BlockZM = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1); BLOCKTYPE BlockZP = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT + 1); if ( (!IsBlockRail(BlockXM) && cBlockInfo::IsSolid(BlockXM)) || (!IsBlockRail(BlockXP) && cBlockInfo::IsSolid(BlockXP)) || (!IsBlockRail(BlockZM) && cBlockInfo::IsSolid(BlockZM)) || (!IsBlockRail(BlockZP) && cBlockInfo::IsSolid(BlockZP)) ) { SetSpeed(0, 0, 0); SetPosition(POSX_TOINT + 0.5, GetPosY(), POSZ_TOINT + 0.5); return true; } break; } default: break; } return false; }
void CBulletPaper::Render(void) { static RECT r = {615,32,624,42}; CSGD_TextureManager *pTM = CSGD_TextureManager::GetInstance(); pTM->Draw(GetImageID(),GetPosX(),GetPosY(),2.0f,2.0f,&r,GetWidth() / 2, GetHeight() /2,GetRotation(),D3DCOLOR_XRGB(255,255,255)); }
void cMonster::KilledBy(TakeDamageInfo & a_TDI) { super::KilledBy(a_TDI); if (m_SoundHurt != "") { m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); } int Reward; switch (m_MobType) { // Animals case mtChicken: case mtCow: case mtHorse: case mtPig: case mtSheep: case mtSquid: case mtMooshroom: case mtOcelot: case mtWolf: { Reward = m_World->GetTickRandomNumber(2) + 1; break; } // Monsters case mtCaveSpider: case mtCreeper: case mtEnderman: case mtGhast: case mtSilverfish: case mtSkeleton: case mtSpider: case mtWitch: case mtZombie: case mtZombiePigman: case mtSlime: case mtMagmaCube: { Reward = 6 + (m_World->GetTickRandomNumber(2)); break; } case mtBlaze: { Reward = 10; break; } // Bosses case mtEnderDragon: { Reward = 12000; break; } case mtWither: { Reward = 50; break; } default: { Reward = 0; break; } } if ((a_TDI.Attacker != NULL) && (!IsBaby())) { m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); } m_DestroyTimer = 0; }
void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) { // GetWorld()->BroadcastTeleportEntity(*this); // Test position int BlockX = POSX_TOINT; int BlockY = (int)(GetPosY() - 0.5); int BlockZ = POSZ_TOINT; if (BlockY < 0) { // Fallen out of this world, just continue falling until out of sight, then destroy: if (BlockY < VOID_BOUNDARY) { Destroy(true); } return; } if (BlockY >= cChunkDef::Height) { // Above the world, just wait for it to fall back down return; } int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width); BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx); NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx); if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta)) { // Fallen onto a block that breaks this into pickups (e. g. half-slab) // Must finish the fall with coords one below the block: cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta); Destroy(true); return; } else if (!cSandSimulator::CanContinueFallThrough(BlockBelow)) { // Fallen onto a solid block /* LOGD( "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.", BlockX, BlockY, BlockZ, BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width, ItemTypeToString(BlockBelow).c_str() ); */ cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); Destroy(true); return; } float MilliDt = a_Dt * 0.001f; AddSpeedY(MilliDt * -9.8f); AddPosition(GetSpeed() * MilliDt); // If not static (One billionth precision) broadcast movement. static const float epsilon = 0.000000001; if ((fabs(GetSpeedX()) > epsilon) || (fabs(GetSpeedZ()) > epsilon)) { BroadcastMovementUpdate(); } }