diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index 1535364..61de4ca 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -6,6 +6,7 @@ #include "Database.h" #include "Game.h" +#include "dLogger.h" #include "User.h" #include #include "Character.h" @@ -68,16 +69,6 @@ void UserManager::Initialize() { StripCR(line); m_PreapprovedNames.push_back(line); } - - //Load custom ones from MySQL too: - /*sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT name FROM approvedNames;"); - sql::ResultSet* res = stmt->executeQuery(); - while (res->next()) { - m_PreapprovedNames.push_back(res->getString(1)); - } - - delete res; - delete stmt;*/ } UserManager::~UserManager() { @@ -566,122 +557,38 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID } } -uint32_t GetShirtColorId(uint32_t color) { - - // get the index of the color in shirtColorVector - auto colorId = std::find(shirtColorVector.begin(), shirtColorVector.end(), color); - return color = std::distance(shirtColorVector.begin(), colorId); -} - uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) { - - shirtStyle--; // to start at 0 instead of 1 - uint32_t stylesCount = 34; - uint32_t colorId = GetShirtColorId(shirtColor); - - uint32_t startID = 4049; // item ID of the shirt with color 0 (red) and style 0 (plain) - - // For some reason, if the shirt style is 34 - 39, - // The ID is different than the original... Was this because - // these shirts were added later? - if (shirtStyle >= 34) { - startID = 5730; // item ID of the shirt with color 0 (red) and style 34 (butterflies) - shirtStyle -= stylesCount; //change style from range 35-40 to range 0-5 - stylesCount = 6; + try { + std::string shirtQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"character create shirt\" AND icc.color1 == "; + shirtQuery += std::to_string(shirtColor); + shirtQuery += " AND icc.decal == "; + shirtQuery = shirtQuery + std::to_string(shirtStyle); + auto tableData = CDClientDatabase::ExecuteQuery(shirtQuery); + auto shirtLOT = tableData.getIntField(0, -1); + tableData.finalize(); + return shirtLOT; + } + catch (const std::exception&){ + Game::logger->Log("Character Create", "Failed to execute query! Using backup..."); + // in case of no shirt found in CDServer, return problematic red vest. + return 4069; } - - // Get the final ID of the shirt - uint32_t shirtID = startID + (colorId * stylesCount) + shirtStyle; - - return shirtID; } uint32_t FindCharPantsID(uint32_t pantsColor) { - uint32_t pantsID = 2508; - - switch (pantsColor) { - case 0: { - pantsID = PANTS_BRIGHT_RED; - break; + try { + std::string pantsQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"cc pants\" AND icc.color1 == "; + pantsQuery += std::to_string(pantsColor); + auto tableData = CDClientDatabase::ExecuteQuery(pantsQuery); + auto pantsLOT = tableData.getIntField(0, -1); + tableData.finalize(); + return pantsLOT; } - - case 1: { - pantsID = PANTS_BRIGHT_BLUE; - break; + catch (const std::exception&){ + Game::logger->Log("Character Create", "Failed to execute query! Using backup..."); + // in case of no pants color found in CDServer, return red pants. + return 2508; } - - case 3: { - pantsID = PANTS_DARK_GREEN; - break; - } - - case 5: { - pantsID = PANTS_BRIGHT_ORANGE; - break; - } - - case 6: { - pantsID = PANTS_BLACK; - break; - } - - case 7: { - pantsID = PANTS_DARK_STONE_GRAY; - break; - } - - case 8: { - pantsID = PANTS_MEDIUM_STONE_GRAY; - break; - } - - case 9: { - pantsID = PANTS_REDDISH_BROWN; - break; - } - - case 10: { - pantsID = PANTS_WHITE; - break; - } - - case 11: { - pantsID = PANTS_MEDIUM_BLUE; - break; - } - - case 13: { - pantsID = PANTS_DARK_RED; - break; - } - - case 14: { - pantsID = PANTS_EARTH_BLUE; - break; - } - - case 15: { - pantsID = PANTS_EARTH_GREEN; - break; - } - - case 16: { - pantsID = PANTS_BRICK_YELLOW; - break; - } - - case 84: { - pantsID = PANTS_SAND_BLUE; - break; - } - - case 96: { - pantsID = PANTS_SAND_GREEN; - break; - } - } - - return pantsID; } void UserManager::SaveAllActiveCharacters() { diff --git a/dGame/UserManager.h b/dGame/UserManager.h index b29cf50..a5db697 100644 --- a/dGame/UserManager.h +++ b/dGame/UserManager.h @@ -44,7 +44,6 @@ public: private: static UserManager* m_Address; //Singleton - //std::vector m_Users; std::map m_Users; std::vector m_UsersToDelete; @@ -54,43 +53,4 @@ private: std::vector m_PreapprovedNames; }; -enum CharCreatePantsColor : uint32_t { - PANTS_BRIGHT_RED = 2508, - PANTS_BRIGHT_ORANGE = 2509, - PANTS_BRICK_YELLOW = 2511, - PANTS_MEDIUM_BLUE = 2513, - PANTS_SAND_GREEN = 2514, - PANTS_DARK_GREEN = 2515, - PANTS_EARTH_GREEN = 2516, - PANTS_EARTH_BLUE = 2517, - PANTS_BRIGHT_BLUE = 2519, - PANTS_SAND_BLUE = 2520, - PANTS_DARK_STONE_GRAY = 2521, - PANTS_MEDIUM_STONE_GRAY = 2522, - PANTS_WHITE = 2523, - PANTS_BLACK = 2524, - PANTS_REDDISH_BROWN = 2526, - PANTS_DARK_RED = 2527 -}; - -const std::vector shirtColorVector { - 0, // BRIGHT_RED - 1, // BRIGHT_BLUE - 2, // BRIGHT_YELLOW - 3, // DARK_GREEN - 5, // BRIGHT_ORANGE - 6, // BLACK - 7, // DARK_STONE_GRAY - 8, // MEDIUM_STONE_GRAY - 9, // REDDISH_BROWN - 10, // WHITE - 11, // MEDIUM_BLUE - 13, // DARK_RED - 14, // EARTH_BLUE - 15, // EARTH_GREEN - 16, // BRICK_YELLOW - 84, // SAND_BLUE - 96 // SAND_GREEN -}; - #endif // USERMANAGER_H diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index c5b4bec..56bf26d 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -1043,7 +1043,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot() }); - if (item->GetParent() == LWOOBJID_EMPTY) ApplyBuff(item->GetLot()); + ApplyBuff(item); AddItemSkills(item->GetLot()); @@ -1071,7 +1071,7 @@ void InventoryComponent::UnEquipItem(Item* item) set->OnUnEquip(lot); } - if (item->GetParent() == LWOOBJID_EMPTY) RemoveBuff(item->GetLot()); + RemoveBuff(item); RemoveItemSkills(item->GetLot()); @@ -1089,9 +1089,9 @@ void InventoryComponent::UnEquipItem(Item* item) } } -void InventoryComponent::ApplyBuff(const LOT lot) const +void InventoryComponent::ApplyBuff(Item* item) const { - const auto buffs = FindBuffs(lot, true); + const auto buffs = FindBuffs(item, true); for (const auto buff : buffs) { @@ -1099,9 +1099,9 @@ void InventoryComponent::ApplyBuff(const LOT lot) const } } -void InventoryComponent::RemoveBuff(const LOT lot) const +void InventoryComponent::RemoveBuff(Item* item) const { - const auto buffs = FindBuffs(lot, false); + const auto buffs = FindBuffs(item, false); for (const auto buff : buffs) { @@ -1418,18 +1418,18 @@ uint32_t InventoryComponent::FindSkill(const LOT lot) return 0; } -std::vector InventoryComponent::FindBuffs(const LOT lot, bool castOnEquip) const +std::vector InventoryComponent::FindBuffs(Item* item, bool castOnEquip) const { + std::vector buffs; + if (item == nullptr) return buffs; auto* table = CDClientManager::Instance()->GetTable("ObjectSkills"); auto* behaviors = CDClientManager::Instance()->GetTable("SkillBehavior"); const auto results = table->Query([=](const CDObjectSkills& entry) { - return entry.objectTemplate == static_cast(lot); + return entry.objectTemplate == static_cast(item->GetLot()); }); - std::vector buffs; - auto* missions = static_cast(m_Parent->GetComponent(COMPONENT_TYPE_MISSION)); for (const auto& result : results) @@ -1449,8 +1449,8 @@ std::vector InventoryComponent::FindBuffs(const LOT lot, bool castOnEq { missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, result.skillID); } - - buffs.push_back(static_cast(entry.behaviorID)); + // If item is not a proxy, add its buff to the added buffs. + if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast(entry.behaviorID)); } } @@ -1531,7 +1531,7 @@ std::vector InventoryComponent::GenerateProxies(Item* parent) auto* inventory = GetInventory(ITEM_SETS); - auto* proxy = new Item(lot, inventory, inventory->FindEmptySlot(), 1, {}, parent->GetId(), false, parent->GetId()); + auto* proxy = new Item(lot, inventory, inventory->FindEmptySlot(), 1, {}, parent->GetId(), false); EquipItem(proxy); diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 2214380..0a4cfdb 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -193,15 +193,15 @@ public: /** * Adds a buff related to equipping a lot to the entity - * @param lot the lot to find buffs for + * @param item the item to find buffs for */ - void ApplyBuff(LOT lot) const; + void ApplyBuff(Item* item) const; /** * Removes buffs related to equipping a lot from the entity - * @param lot the lot to find buffs for + * @param item the item to find buffs for */ - void RemoveBuff(LOT lot) const; + void RemoveBuff(Item* item) const; /** * Saves the equipped items into a temp state @@ -240,11 +240,11 @@ public: /** * Finds all the buffs related to a lot - * @param lot the lot to get the buffs for + * @param item the item to get the buffs for * @param castOnEquip if true, the skill missions for these buffs will be progressed * @return the buffs related to the specified lot */ - std::vector FindBuffs(LOT lot, bool castOnEquip) const; + std::vector FindBuffs(Item* item, bool castOnEquip) const; /** * Initializes the equipped items with a list of items diff --git a/dGame/dMission/MissionTask.cpp b/dGame/dMission/MissionTask.cpp index 2ef36fd..1a340b2 100644 --- a/dGame/dMission/MissionTask.cpp +++ b/dGame/dMission/MissionTask.cpp @@ -416,6 +416,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& case MissionTaskType::MISSION_TASK_TYPE_RACING: { + // The meaning of associate can be found in RacingTaskParam.h if (parameters.empty()) break; if (!InAllTargets(dZoneManager::Instance()->GetZone()->GetWorldID()) && !(parameters[0] == 4 || parameters[0] == 5) && !InAllTargets(value)) break; @@ -440,6 +441,11 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& if (!InAllTargets(value)) break; AddProgress(count); } + else if (associate == 17) + { + if (!InAllTargets(value)) break; + AddProgress(count); + } else { AddProgress(count); diff --git a/dGame/dMission/RacingTaskParam.h b/dGame/dMission/RacingTaskParam.h index e85d9f9..38f8dd8 100644 --- a/dGame/dMission/RacingTaskParam.h +++ b/dGame/dMission/RacingTaskParam.h @@ -16,5 +16,5 @@ enum class RacingTaskParam : int32_t { RACING_TASK_PARAM_WIN_RACE_IN_WORLD = 14, //GetMaxGMLevel() == 0 || entity->GetGMLevel() >= 0) { - if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1) { - std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size()); - GameMessages::SendPlayAnimation(entity, anim); - } - if (chatCommand == "die") { entity->Smash(entity->GetObjectID()); } @@ -446,6 +441,11 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); // need to retoggle because it gets reenabled on creation of new character } + + if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { + std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size()); + GameMessages::SendPlayAnimation(entity, anim); + } if (chatCommand == "list-spawns" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { for (const auto& pair : EntityManager::Instance()->GetSpawnPointEntities()) { diff --git a/dScripts/AgSurvivalBuffStation.cpp b/dScripts/AgSurvivalBuffStation.cpp index 41a754d..01fe397 100644 --- a/dScripts/AgSurvivalBuffStation.cpp +++ b/dScripts/AgSurvivalBuffStation.cpp @@ -1,15 +1,66 @@ #include "AgSurvivalBuffStation.h" +#include "DestroyableComponent.h" +#include "EntityManager.h" +#include "GameMessages.h" #include "SkillComponent.h" #include "dLogger.h" +#include "TeamManager.h" void AgSurvivalBuffStation::OnRebuildComplete(Entity* self, Entity* target) { + auto destroyableComponent = self->GetComponent(); + // We set the faction to 6 so that the buff station sees players as friendly targets to buff + if (destroyableComponent != nullptr) destroyableComponent->SetFaction(6); + auto skillComponent = self->GetComponent(); - if (skillComponent == nullptr) return; + if (skillComponent != nullptr) skillComponent->CalculateBehavior(skillIdForBuffStation, behaviorIdForBuffStation, self->GetObjectID()); - skillComponent->CalculateBehavior(201, 1784, self->GetObjectID()); - - self->AddCallbackTimer(10.0f, [self]() { + self->AddCallbackTimer(smashTimer, [self]() { self->Smash(); }); + self->AddTimer("DropArmor", dropArmorTimer); + self->AddTimer("DropLife", dropLifeTimer); + self->AddTimer("Dropimagination", dropImaginationTimer); + // Since all survival players should be on the same team, we get the team. + auto team = TeamManager::Instance()->GetTeam(target->GetObjectID()); + + std::vector builderTeam; + // Not on a team + if (team == nullptr) { + builderTeam.push_back(target->GetObjectID()); + self->SetVar>(u"BuilderTeam", builderTeam); + return; + } + + for (auto memberID : team->members) { + builderTeam.push_back(memberID); + } + self->SetVar>(u"BuilderTeam", builderTeam); +} + +void AgSurvivalBuffStation::OnTimerDone(Entity* self, std::string timerName) { + uint32_t powerupToDrop = lifePowerup; + if (timerName == "DropArmor") { + powerupToDrop = armorPowerup; + self->AddTimer("DropArmor", dropArmorTimer); + } + if (timerName == "DropLife") { + powerupToDrop = lifePowerup; + self->AddTimer("DropLife", dropLifeTimer); + } + if (timerName == "Dropimagination") { + powerupToDrop = imaginationPowerup; + self->AddTimer("Dropimagination", dropImaginationTimer); + } + auto team = self->GetVar>(u"BuilderTeam"); + for (auto memberID : team) { + auto member = EntityManager::Instance()->GetEntity(memberID); + if (member != nullptr && !member->GetIsDead()) { + GameMessages::SendDropClientLoot(member, self->GetObjectID(), powerupToDrop, 0, self->GetPosition()); + } else { + // If player left the team or left early erase them from the team variable. + team.erase(std::find(team.begin(), team.end(), memberID)); + self->SetVar>(u"BuilderTeam", team); + } + } } diff --git a/dScripts/AgSurvivalBuffStation.h b/dScripts/AgSurvivalBuffStation.h index 0b4d186..b2beaba 100644 --- a/dScripts/AgSurvivalBuffStation.h +++ b/dScripts/AgSurvivalBuffStation.h @@ -11,6 +11,7 @@ public: * @param target The target of the self that called this script. */ void OnRebuildComplete(Entity* self, Entity* target) override; + void OnTimerDone(Entity* self, std::string timerName) override; private: /** * Skill ID for the buff station. @@ -20,4 +21,32 @@ private: * Behavior ID for the buff station. */ uint32_t behaviorIdForBuffStation = 1784; + /** + * Timer for dropping armor. + */ + float dropArmorTimer = 6.0f; + /** + * Timer for dropping life. + */ + float dropLifeTimer = 3.0f; + /** + * Timer for dropping imagination. + */ + float dropImaginationTimer = 4.0f; + /** + * Timer for smashing. + */ + float smashTimer = 25.0f; + /** + * LOT for armor powerup. + */ + LOT armorPowerup = 6431; + /** + * LOT for life powerup. + */ + LOT lifePowerup = 177; + /** + * LOT for imagination powerup. + */ + LOT imaginationPowerup = 935; }; \ No newline at end of file diff --git a/dScripts/AmDarklingDragon.cpp b/dScripts/AmDarklingDragon.cpp new file mode 100644 index 0000000..be87f46 --- /dev/null +++ b/dScripts/AmDarklingDragon.cpp @@ -0,0 +1,164 @@ +#include "AmDarklingDragon.h" +#include "BaseCombatAIComponent.h" +#include "DestroyableComponent.h" +#include "EntityManager.h" +#include "GameMessages.h" +#include "SkillComponent.h" +#include "BaseCombatAIComponent.h" + + +void AmDarklingDragon::OnStartup(Entity* self) { + self->SetVar(u"weakspot", 0); + + auto* baseCombatAIComponent = self->GetComponent(); + + if (baseCombatAIComponent != nullptr) { + baseCombatAIComponent->SetStunImmune(true); + } +} + +void AmDarklingDragon::OnDie(Entity* self, Entity* killer) { + if (self->GetVar(u"bDied")) { + return; + } + + self->SetVar(u"bDied", true); + + auto golemId = self->GetVar(u"Golem"); + + auto* golem = EntityManager::Instance()->GetEntity(golemId); + + if (golem != nullptr) { + golem->Smash(self->GetObjectID()); + } +} + +void AmDarklingDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) { + GameMessages::SendPlayFXEffect(self, -1, u"gothit", "", LWOOBJID_EMPTY, 1, 1, true); + + if (true) { + auto weakpoint = self->GetVar(u"weakspot"); + + if (weakpoint == 1) + { + self->Smash(attacker->GetObjectID()); + } + } + + auto* destroyableComponent = self->GetComponent(); + + if (destroyableComponent != nullptr) { + Game::logger->Log("AmDarklingDragon", "Armor is %i\n", destroyableComponent->GetArmor()); + + if (destroyableComponent->GetArmor() > 0) return; + + auto weakpoint = self->GetVar(u"weakpoint"); + + if (weakpoint == 0) { + Game::logger->Log("AmDarklingDragon", "Activating weakpoint\n"); + + self->AddTimer("ReviveTimer", 12); + + auto* baseCombatAIComponent = self->GetComponent(); + auto* skillComponent = self->GetComponent(); + + if (baseCombatAIComponent != nullptr) { + baseCombatAIComponent->SetDisabled(true); + baseCombatAIComponent->SetStunned(true); + } + + if (skillComponent != nullptr) { + skillComponent->Interrupt(); + } + + self->SetVar(u"weakpoint", 2); + + GameMessages::SendPlayAnimation(self, u"stunstart", 1.7f); + + self->AddTimer("timeToStunLoop", 1); + + auto position = self->GetPosition(); + auto forward = self->GetRotation().GetForwardVector(); + auto backwards = forward * -1; + + forward.x *= 10; + forward.z *= 10; + + auto rotation = self->GetRotation(); + + auto objectPosition = NiPoint3(); + + objectPosition.y = position.y; + objectPosition.x = position.x - (backwards.x * 8); + objectPosition.z = position.z - (backwards.z * 8); + + auto golem = self->GetVar(u"DragonSmashingGolem"); + + EntityInfo info {}; + info.lot = golem != 0 ? golem : 8340; + info.pos = objectPosition; + info.rot = rotation; + info.spawnerID = self->GetObjectID(); + info.settings = { + new LDFData(u"rebuild_activators", + std::to_string(objectPosition.x + forward.x) + "\x1f" + + std::to_string(objectPosition.y) + "\x1f" + + std::to_string(objectPosition.z + forward.z) + ), + new LDFData(u"respawn", 100000), + new LDFData(u"rebuild_reset_time", 15), + new LDFData(u"no_timed_spawn", true), + new LDFData(u"Dragon", self->GetObjectID()) + }; + + auto* golemObject = EntityManager::Instance()->CreateEntity(info); + + EntityManager::Instance()->ConstructEntity(golemObject); + } + } +} + +void AmDarklingDragon::OnTimerDone(Entity* self, std::string timerName) { + if (timerName == "ReviveHeldTimer") { + self->AddTimer("backToAttack", 2.5); + } + else if (timerName == "ExposeWeakSpotTimer") { + self->SetVar(u"weakspot", 1); + } + else if (timerName == "timeToStunLoop") { + GameMessages::SendPlayAnimation(self, u"stunloop", 1.8f); + } + else if (timerName == "ReviveTimer") { + GameMessages::SendPlayAnimation(self, u"stunend", 2.0f); + self->AddTimer("backToAttack", 1); + } + else if (timerName == "backToAttack") { + auto* baseCombatAIComponent = self->GetComponent(); + auto* skillComponent = self->GetComponent(); + if (baseCombatAIComponent != nullptr) + { + baseCombatAIComponent->SetDisabled(false); + baseCombatAIComponent->SetStunned(false); + } + if (skillComponent != nullptr) + { + skillComponent->Interrupt(); + } + self->SetVar(u"weakspot", -1); + GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS); + } +} + +void AmDarklingDragon::OnFireEventServerSide(Entity *self, Entity *sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { + if (args != "rebuildDone") return; + + self->AddTimer("ExposeWeakSpotTimer", 3.8f); + + self->CancelTimer("ReviveTimer"); + + self->AddTimer("ReviveHeldTimer", 10.5f); + + self->SetVar(u"Golem", sender->GetObjectID()); + + GameMessages::SendPlayAnimation(self, u"quickbuildhold", 1.9f); +} \ No newline at end of file diff --git a/dScripts/AmDarklingDragon.h b/dScripts/AmDarklingDragon.h new file mode 100644 index 0000000..ea4c2a1 --- /dev/null +++ b/dScripts/AmDarklingDragon.h @@ -0,0 +1,48 @@ +#pragma once +#include "CppScripts.h" + +class AmDarklingDragon : public CppScripts::Script +{ +public: + /** + * @brief When called, this function will make self immune to stuns and initialize a weakspot boolean to false. + * + * @param self The Entity that called this function. + */ + void OnStartup(Entity* self) override; + /** + * @brief When called, this function will destroy the golem if it was alive, otherwise returns immediately. + * + * @param self The Entity that called this function. + * @param killer The Entity that killed self. + */ + void OnDie(Entity* self, Entity* killer) override; + /** + * @brief When self is hit or healed, this function will check if self is at zero armor. If self is at zero armor, a golem Entity Quick Build + * is spawned that, when built, will reveal a weakpoint on the dragon that if hit will smash the dragon instantly. If at more than zero armor, + * this function returns early. + * + * @param self The Entity that was hit. + * @param attacker The Entity that attacked self. + * @param damage The amount of damage attacker did to self. + */ + void OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) override; + /** + * @brief Called when self has a timer that ended. + * + * @param self The Entity who owns a timer that finished. + * @param timerName The name of a timer attacked to self that has ended. + */ + void OnTimerDone(Entity* self, std::string timerName) override; + /** + * @brief When the Client has finished rebuilding the Golem for the dragon, this function exposes the weak spot for a set amount of time. + * + * @param self The Entity that called this script. + * @param sender The Entity that sent a fired event. + * @param args The argument that tells us what event has been fired off. + * @param param1 Unused in this script. + * @param param2 Unused in this script. + * @param param3 Unused in this script. + */ + void OnFireEventServerSide(Entity *self, Entity *sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override; +}; diff --git a/dScripts/AmNamedDarklingDragon.cpp b/dScripts/AmNamedDarklingDragon.cpp deleted file mode 100644 index c7f51cb..0000000 --- a/dScripts/AmNamedDarklingDragon.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "AmNamedDarklingDragon.h" -#include "BaseCombatAIComponent.h" - -void AmNamedDarklingDragon::OnStartup(Entity* self) -{ - auto* baseCombatAIComponent = self->GetComponent(); - - if (baseCombatAIComponent != nullptr) - { - baseCombatAIComponent->SetStunImmune(true); - } -} diff --git a/dScripts/AmNamedDarklingDragon.h b/dScripts/AmNamedDarklingDragon.h deleted file mode 100644 index cad5c2c..0000000 --- a/dScripts/AmNamedDarklingDragon.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "CppScripts.h" - -class AmNamedDarklingDragon : public CppScripts::Script -{ -public: - void OnStartup(Entity* self) override; -}; diff --git a/dScripts/BaseEnemyApe.cpp b/dScripts/BaseEnemyApe.cpp index 88c846f..3419b2c 100644 --- a/dScripts/BaseEnemyApe.cpp +++ b/dScripts/BaseEnemyApe.cpp @@ -8,11 +8,6 @@ void BaseEnemyApe::OnStartup(Entity *self) { self->SetVar(u"timesStunned", 2); self->SetVar(u"knockedOut", false); - - auto* combatAIComponent = self->GetComponent(); - if (combatAIComponent != nullptr) { - combatAIComponent->SetStunImmune(true); - } } void BaseEnemyApe::OnDie(Entity *self, Entity *killer) { @@ -23,10 +18,8 @@ void BaseEnemyApe::OnDie(Entity *self, Entity *killer) { } void BaseEnemyApe::OnSkillCast(Entity *self, uint32_t skillID) { - const auto groundPoundSkill = self->GetVar(u"GroundPoundSkill") != 0 - ? self->GetVar(u"GroundPoundSkill") : 725; - const auto spawnQuickBuildTime = self->GetVar(u"spawnQBTime") != 0.0f - ? self->GetVar(u"spawnQBTime") : 5.0f; + const auto groundPoundSkill = self->GetVar(u"GroundPoundSkill") != 0 ? self->GetVar(u"GroundPoundSkill") : 725; + const auto spawnQuickBuildTime = self->GetVar(u"spawnQBTime") != 0.0f ? self->GetVar(u"spawnQBTime") : 5.0f; if (skillID == groundPoundSkill && self->GetVar(u"QB") == LWOOBJID_EMPTY) { self->AddTimer("spawnQBTime", spawnQuickBuildTime); @@ -61,9 +54,7 @@ void BaseEnemyApe::OnTimerDone(Entity *self, std::string timerName) { StunApe(self, false); } else if (timerName == "spawnQBTime" && self->GetVar(u"QB") == LWOOBJID_EMPTY) { - - // Spawns the QB, which can insta kill the ape - // Quick mafs to spawn the QB in the correct spot + // Spawn QB in front of ape. const auto position = self->GetPosition(); const auto rotation = self->GetRotation(); @@ -107,8 +98,6 @@ void BaseEnemyApe::OnTimerDone(Entity *self, std::string timerName) { auto* skillComponent = self->GetComponent(); if (skillComponent != nullptr) { - // We use a different behavior than the script here, the original one contains a TargetCaster behavior - // but as of writing we can't pass an optional originated to give the loot to the player skillComponent->CalculateBehavior(1273, 29446, self->GetObjectID(), true, false, player->GetObjectID()); } @@ -120,8 +109,7 @@ void BaseEnemyApe::OnFireEventServerSide(Entity *self, Entity *sender, std::stri int32_t param3) { if (args == "rebuildDone" && sender != nullptr) { self->SetVar(u"smasher", sender->GetObjectID()); - const auto anchorDamageDelayTime = self->GetVar(u"AnchorDamageDelayTime") != 0.0f - ? self->GetVar(u"AnchorDamageDelayTime") : 0.5f; + const auto anchorDamageDelayTime = self->GetVar(u"AnchorDamageDelayTime") != 0.0f ? self->GetVar(u"AnchorDamageDelayTime") : 0.5f; self->AddTimer("anchorDamageTimer", anchorDamageDelayTime); } } diff --git a/dScripts/BaseSurvivalServer.cpp b/dScripts/BaseSurvivalServer.cpp index 1174f00..dd9b936 100644 --- a/dScripts/BaseSurvivalServer.cpp +++ b/dScripts/BaseSurvivalServer.cpp @@ -200,6 +200,7 @@ void BaseSurvivalServer::OnActivityTimerDone(Entity *self, const std::string &na ActivityTimerStop(self, SpawnTickTimer); ActivityTimerStart(self, CoolDownStopTimer, 1, constants.coolDownTime); + ActivateSpawnerNetwork(spawnerNetworks.rewardNetworks); SpawnerReset(spawnerNetworks.baseNetworks, false); SpawnerReset(spawnerNetworks.randomNetworks, false); } else if (name == CoolDownStopTimer) { @@ -302,7 +303,6 @@ void BaseSurvivalServer::StartWaves(Entity *self) { self->SetVar(FirstTimeDoneVariable, true); self->SetVar(MissionTypeVariable, state.players.size() == 1 ? "survival_time_solo" : "survival_time_team"); - ActivateSpawnerNetwork(spawnerNetworks.rewardNetworks); ActivateSpawnerNetwork(spawnerNetworks.smashNetworks); self->SetNetworkVar(WavesStartedVariable, true); self->SetNetworkVar(StartWaveMessageVariable, "Start!"); diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 3b9ce5f..b9fa50e 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -181,6 +181,7 @@ #include "NtVentureCannonServer.h" #include "NtCombatChallengeServer.h" #include "NtCombatChallengeDummy.h" +#include "NtCombatChallengeExplodingDummy.h" #include "BaseInteractDropLootServer.h" #include "NtAssemblyTubeServer.h" #include "NtParadoxPanelServer.h" @@ -223,7 +224,7 @@ #include "AmSkullkinDrill.h" #include "AmSkullkinDrillStand.h" #include "AmSkullkinTower.h" -#include "AmNamedDarklingDragon.h" +#include "AmDarklingDragon.h" #include "AmBlueX.h" // NJ Scripts @@ -615,6 +616,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new NtCombatChallengeServer(); else if (scriptName == "scripts\\02_server\\Map\\NT\\L_NT_COMBAT_CHALLENGE_DUMMY.lua") script = new NtCombatChallengeDummy(); + else if (scriptName == "scripts\\02_server\\Map\\NT\\\\L_NT_COMBAT_EXPLODING_TARGET.lua") + script = new NtCombatChallengeExplodingDummy(); else if (scriptName == "scripts\\02_server\\Map\\General\\L_BASE_INTERACT_DROP_LOOT_SERVER.lua") script = new BaseInteractDropLootServer(); else if (scriptName == "scripts\\02_server\\Map\\NT\\L_NT_ASSEMBLYTUBE_SERVER.lua") @@ -687,11 +690,10 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new AmSkullkinDrillStand(); else if (scriptName == "scripts\\02_server\\Map\\AM\\L_SKULLKIN_TOWER.lua") script = new AmSkullkinTower(); - // This just makes them immune to stuns. TODO: Make seperate scripts else if (scriptName == "scripts\\02_server\\Enemy\\AM\\L_AM_NAMED_DARKLING_DRAGON.lua") - script = new AmNamedDarklingDragon(); + script = new AmDarklingDragon(); else if (scriptName == "scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_DRAGON.lua") - script = new AmNamedDarklingDragon(); + script = new AmDarklingDragon(); else if (scriptName == "scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_APE.lua") script = new BaseEnemyApe(); else if (scriptName == "scripts\\02_server\\Map\\AM\\L_BLUE_X.lua") diff --git a/dScripts/FvRaceSmashEggImagineServer.cpp b/dScripts/FvRaceSmashEggImagineServer.cpp index 68faea0..8e0d089 100644 --- a/dScripts/FvRaceSmashEggImagineServer.cpp +++ b/dScripts/FvRaceSmashEggImagineServer.cpp @@ -27,7 +27,9 @@ void FvRaceSmashEggImagineServer::OnDie(Entity *self, Entity *killer) { characterComponent->UpdatePlayerStatistic(RacingSmashablesSmashed); } if (missionComponent == nullptr) return; - missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, self->GetLOT(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_SMASH_DRAGON_EGGS); + // Dragon eggs have their own smash server so we handle mission progression for them here. + missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, 0, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_SMASHABLES); + missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, self->GetLOT(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_SMASH_SPECIFIC_SMASHABLE); } } diff --git a/dScripts/NtCombatChallengeExplodingDummy.cpp b/dScripts/NtCombatChallengeExplodingDummy.cpp new file mode 100644 index 0000000..dd61817 --- /dev/null +++ b/dScripts/NtCombatChallengeExplodingDummy.cpp @@ -0,0 +1,38 @@ +#include "NtCombatChallengeExplodingDummy.h" +#include "NtCombatChallengeDummy.h" +#include "EntityManager.h" +#include "SkillComponent.h" + +void NtCombatChallengeExplodingDummy::OnDie(Entity* self, Entity* killer) +{ + const auto challengeObjectID = self->GetVar(u"challengeObjectID"); + + auto* challengeObject = EntityManager::Instance()->GetEntity(challengeObjectID); + + if (challengeObject != nullptr) + { + for (CppScripts::Script* script : CppScripts::GetEntityScripts(challengeObject)) + { + script->OnDie(challengeObject, killer); + } + } +} + +void NtCombatChallengeExplodingDummy::OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) { + const auto challengeObjectID = self->GetVar(u"challengeObjectID"); + + auto* challengeObject = EntityManager::Instance()->GetEntity(challengeObjectID); + + if (challengeObject != nullptr) + { + for (CppScripts::Script* script : CppScripts::GetEntityScripts(challengeObject)) + { + script->OnHitOrHealResult(challengeObject, attacker, damage); + } + } + auto skillComponent = self->GetComponent(); + if (skillComponent != nullptr) { + skillComponent->CalculateBehavior(1338, 30875, attacker->GetObjectID()); + } + self->Kill(attacker); +} \ No newline at end of file diff --git a/dScripts/NtCombatChallengeExplodingDummy.h b/dScripts/NtCombatChallengeExplodingDummy.h new file mode 100644 index 0000000..c1c5ef1 --- /dev/null +++ b/dScripts/NtCombatChallengeExplodingDummy.h @@ -0,0 +1,8 @@ +#pragma once +#include "CppScripts.h" + +class NtCombatChallengeExplodingDummy : public CppScripts::Script +{ + void OnDie(Entity* self, Entity* killer) override; + void OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) override; +}; \ No newline at end of file diff --git a/dScripts/RaceSmashServer.cpp b/dScripts/RaceSmashServer.cpp index 059ac43..582a8ed 100644 --- a/dScripts/RaceSmashServer.cpp +++ b/dScripts/RaceSmashServer.cpp @@ -22,6 +22,8 @@ void RaceSmashServer::OnDie(Entity *self, Entity *killer) { // Progress racing smashable missions if(missionComponent == nullptr) return; missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, 0, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_SMASHABLES); + // Progress missions that ask us to smash a specific smashable. + missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, self->GetLOT(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_SMASH_SPECIFIC_SMASHABLE); } } } diff --git a/dScripts/WaveBossApe.cpp b/dScripts/WaveBossApe.cpp index e186004..c7fdccf 100644 --- a/dScripts/WaveBossApe.cpp +++ b/dScripts/WaveBossApe.cpp @@ -32,6 +32,7 @@ void WaveBossApe::OnFireEventServerSide(Entity *self, Entity *sender, std::strin auto* combatAIComponent = self->GetComponent(); if (combatAIComponent != nullptr) { combatAIComponent->SetDisabled(false); + combatAIComponent->SetStunImmune(false); } } else { BaseEnemyApe::OnFireEventServerSide(self, sender, args, param1, param2, param3);