Fix #318 - added spawn set scared_strong_players [0|1]. NPCs will be afraid if gray con and in aggro range, needs faction to check aggro list

This commit is contained in:
Emagi 2023-02-12 13:09:27 -05:00
parent dbf6d0a630
commit 2ea1982355
10 changed files with 115 additions and 17 deletions

View file

@ -0,0 +1 @@
alter table spawn_npcs add column scared_by_strong_players tinyint(3) unsigned not null default 0;

View file

@ -201,6 +201,7 @@ Commands::Commands(){
spawn_set_values["race_type"] = SPAWN_SET_RACE_TYPE;
spawn_set_values["loot_tier"] = SPAWN_SET_LOOT_TIER;
spawn_set_values["loot_drop_type"] = SPAWN_SET_LOOT_DROP_TYPE;
spawn_set_values["scared_strong_players"] = SPAWN_SET_SCARED_STRONG_PLAYERS;
zone_set_values["expansion_id"] = ZONE_SET_VALUE_EXPANSION_ID;
zone_set_values["name"] = ZONE_SET_VALUE_NAME;
@ -898,6 +899,14 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
}
break;
}
case SPAWN_SET_SCARED_STRONG_PLAYERS:{
if(target->IsNPC()){
sprintf(tmp, "%u", target->IsScaredByStrongPlayers());
int32 new_value = atoul(value);
target->SetScaredByStrongPlayers(new_value);
}
break;
}
if(temp_value)
*temp_value = string(tmp);
@ -1604,6 +1613,22 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
}
break;
}
case SPAWN_SET_SCARED_STRONG_PLAYERS:{
int32 new_value = atoul(value);
target->SetScaredByStrongPlayers(new_value);
if (target->GetDatabaseID() > 0)
{
char query[256];
snprintf(query, 256, "update spawn_npcs set scared_by_strong_players=%u where id=%u", atoul(value), target->GetDatabaseID());
if (database.RunQuery(query, strnlen(query, 256)))
{
if(client)
client->Message(CHANNEL_COLOR_RED, "Ran query:%s", query);
}
}
break;
}
}
}
return true;
@ -4959,6 +4984,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
case SPAWN_SET_RACE_TYPE:
case SPAWN_SET_LOOT_TIER:
case SPAWN_SET_LOOT_DROP_TYPE:
case SPAWN_SET_SCARED_STRONG_PLAYERS:
{
// not applicable already ran db command
break;

View file

@ -571,6 +571,7 @@ private:
#define SPAWN_SET_RACE_TYPE 103
#define SPAWN_SET_LOOT_TIER 104
#define SPAWN_SET_LOOT_DROP_TYPE 105
#define SPAWN_SET_SCARED_STRONG_PLAYERS 106
#define ZONE_SET_VALUE_EXPANSION_ID 0
#define ZONE_SET_VALUE_NAME 1

View file

@ -104,6 +104,7 @@ NPC::NPC(NPC* old_npc){
SetLootTier(old_npc->GetLootTier());
SetLootDropType(old_npc->GetLootDropType());
has_spells = old_npc->HasSpells();
SetScaredByStrongPlayers(old_npc->IsScaredByStrongPlayers());
}
}

View file

@ -6940,3 +6940,39 @@ void Player::GetSpellBookSlotSort(int32 pattern, int32* i, int8* page_book_count
}
}
}
bool Player::IsSpawnInRangeList(int32 spawn_id) {
std::shared_lock lock(spawn_aggro_range_mutex);
map<int32, bool>::iterator spawn_itr = player_aggro_range_spawns.find(spawn_id);
if(spawn_itr != player_aggro_range_spawns.end()) {
return spawn_itr->second;
}
return false;
}
void Player::SetSpawnInRangeList(int32 spawn_id, bool in_range) {
std::unique_lock lock(spawn_aggro_range_mutex);
player_aggro_range_spawns[spawn_id] = in_range;
}
void Player::ProcessSpawnRangeUpdates() {
std::unique_lock lock(spawn_aggro_range_mutex);
if(GetClient()->GetCurrentZone() == nullptr) {
return;
}
map<int32, bool>::iterator spawn_itr;
for(spawn_itr = player_aggro_range_spawns.begin(); spawn_itr != player_aggro_range_spawns.end();) {
if(spawn_itr->second) {
Spawn* spawn = GetClient()->GetCurrentZone()->GetSpawnByID(spawn_itr->first);
if(spawn && spawn->IsNPC() && (GetDistance(spawn)) > ((NPC*)spawn)->GetAggroRadius()) {
GetClient()->GetCurrentZone()->SendSpawnChanges((NPC*)spawn, GetClient(), true, true);
spawn_itr->second = false;
spawn_itr = player_aggro_range_spawns.erase(spawn_itr);
continue;
}
}
spawn_itr++;
}
}

View file

@ -973,6 +973,7 @@ public:
Mutex vis_mutex;
Mutex index_mutex;
Mutex spawn_mutex;
mutable std::shared_mutex spawn_aggro_range_mutex;
void SetTempMount(int32 id) { tmp_mount_model = id; }
int32 GetTempMount() { return tmp_mount_model; }
@ -1052,6 +1053,10 @@ public:
void SetActiveReward(bool val) { active_reward = val; }
bool IsActiveReward() { return active_reward; }
bool IsSpawnInRangeList(int32 spawn_id);
void SetSpawnInRangeList(int32 spawn_id, bool in_range);
void ProcessSpawnRangeUpdates();
Mutex MPlayerQuests;
float pos_packet_speed;
private:
@ -1179,6 +1184,7 @@ private:
map<int32, Spawn*> player_spawn_id_map;
map<Spawn*, int32> player_spawn_reverse_id_map;
map<int32, bool> player_aggro_range_spawns;
bool all_spells_locked;
Timer lift_cooldown;

View file

@ -137,6 +137,7 @@ Spawn::Spawn(){
deleted_spawn = false;
is_collector = false;
trigger_widget_id = 0;
scared_by_strong_players = false;
}
Spawn::~Spawn(){
@ -2354,14 +2355,6 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
packet->setDataByName("difficulty", appearance.encounter_level); //6);
packet->setDataByName("unknown6", 1);
packet->setDataByName("heroic_flag", appearance.heroic_flag);
if (IsNPC() && !IsPet())
{
if(((Entity*)this)->GetInfoStruct()->get_interaction_flag())
packet->setDataByName("interaction_flag", ((Entity*)this)->GetInfoStruct()->get_interaction_flag()); //this makes NPCs head turn to look at you (12)
else
packet->setDataByName("interaction_flag", 12); //turn head since no other value is set
}
packet->setDataByName("class", appearance.adventure_class);
int16 model_type = appearance.model_type;
@ -2393,12 +2386,28 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
else
packet->setDataByName("action_state", appearance.action_state);
bool scaredOfPlayer = false;
if(IsCollector() && spawn->GetCollectionList()->HasCollectionsToHandIn())
packet->setDataByName("visual_state", 6674);
packet->setDataByName("visual_state", VISUAL_STATE_COLLECTION_TURN_IN);
else if(!IsRunning() && IsNPC() && IsScaredByStrongPlayers() && spawn->GetArrowColor(GetLevel()) == ARROW_COLOR_GRAY &&
(GetDistance(spawn)) <= ((NPC*)this)->GetAggroRadius() && CheckLoS(spawn)) {
packet->setDataByName("visual_state", VISUAL_STATE_IDLE_AFRAID);
scaredOfPlayer = true;
}
else if (GetTempVisualState() >= 0)
packet->setDataByName("visual_state", GetTempVisualState());
else
packet->setDataByName("visual_state", appearance.visual_state);
if (IsNPC() && !IsPet() && !scaredOfPlayer)
{
if(((Entity*)this)->GetInfoStruct()->get_interaction_flag())
packet->setDataByName("interaction_flag", ((Entity*)this)->GetInfoStruct()->get_interaction_flag()); //this makes NPCs head turn to look at you (12)
else
packet->setDataByName("interaction_flag", 12); //turn head since no other value is set
}
packet->setDataByName("emote_state", appearance.emote_state);
packet->setDataByName("mood_state", appearance.mood_state);
packet->setDataByName("gender", appearance.gender);

View file

@ -165,6 +165,9 @@
#define ENCOUNTER_STATE_OVERMATCHED 4
#define ENCOUNTER_STATE_NO_REWARD 5
#define VISUAL_STATE_COLLECTION_TURN_IN 6674
#define VISUAL_STATE_IDLE_AFRAID 17953
using namespace std;
class Spell;
class ZoneServer;
@ -1084,10 +1087,12 @@ public:
void FaceTarget(Spawn* target, bool disable_action_state = true);
void SetInvulnerable(bool val);
bool GetInvulnerable();
bool changed;
bool position_changed;
bool info_changed;
bool vis_changed;
void SetScaredByStrongPlayers(bool val) { scared_by_strong_players = val; }
bool IsScaredByStrongPlayers() { return scared_by_strong_players; }
std::atomic<bool> changed;
std::atomic<bool> position_changed;
std::atomic<bool> info_changed;
std::atomic<bool> vis_changed;
int16 size;
int32 faction_id;
int8 oversized_packet; //0xff
@ -1397,6 +1402,7 @@ private:
std::atomic<bool> deleted_spawn;
Mutex m_GridMutex;
bool is_collector;
bool scared_by_strong_players;
};
#endif

View file

@ -932,7 +932,7 @@ void WorldDatabase::LoadNPCs(ZoneServer* zone){
NPC* npc = 0;
int32 id = 0;
int32 total = 0;
MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, npc.water_type, npc.flying_type, s.loot_tier, s.loot_drop_type\n"
MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, npc.water_type, npc.flying_type, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players\n"
"FROM spawn s\n"
"INNER JOIN spawn_npcs npc\n"
"ON s.id = npc.spawn_id\n"
@ -1092,6 +1092,8 @@ void WorldDatabase::LoadNPCs(ZoneServer* zone){
npc->SetLootDropType(atoul(row[84]));
npc->SetScaredByStrongPlayers(atoul(row[85]));
zone->AddNPC(id, npc);
total++;
LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC: '%s' (%u)", npc->appearance.name, id);
@ -6695,7 +6697,7 @@ bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) {
int32 id = 0;
DatabaseResult result;
database_new.Select(&result, "SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n"
database_new.Select(&result, "SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players\n"
"FROM spawn s\n"
"INNER JOIN spawn_npcs npc\n"
"ON npc.spawn_id = s.id\n"
@ -6831,6 +6833,8 @@ bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) {
npc->SetLootDropType(result.GetInt32(80));
npc->SetScaredByStrongPlayers(result.GetInt32(81));
zone->AddNPC(id, npc);
//skipped spells/skills/equipment as it is all loaded, the following rely on a spawn to load

View file

@ -926,9 +926,17 @@ bool ZoneServer::CheckNPCAttacks(NPC* npc, Spawn* victim, Client* client){
return true;
if (client) {
int8 arrow = 0;
if (client->IsReadyForUpdates() && npc->CanSeeInvis(client->GetPlayer()) && client->GetPlayer()->GetFactions()->ShouldAttack(npc->GetFactionID()) && npc->AttackAllowed((Entity*)victim, false)) {
if (!npc->EngagedInCombat() && client->GetPlayer()->GetArrowColor(npc->GetLevel()) != ARROW_COLOR_GRAY) {
AggroVictim(npc, victim, client);
if (!npc->EngagedInCombat()) {
if(client->GetPlayer()->GetArrowColor(npc->GetLevel()) != ARROW_COLOR_GRAY) {
AggroVictim(npc, victim, client);
}
else if(npc->IsScaredByStrongPlayers() &&
!client->GetPlayer()->IsSpawnInRangeList(npc->GetID())) {
SendSpawnChanges(npc, client, true, true);
client->GetPlayer()->SetSpawnInRangeList(npc->GetID(), true);
}
}
}
}