Replaced npcspecialatk with special_attacks, needs more testing also gotta export new api for it as I can't remove the legacy one. Too many quests rely on the legacy functionality.

This commit is contained in:
KimLS 2013-07-06 03:45:06 -07:00
parent 0c675c33e2
commit 63d678ce29
32 changed files with 355 additions and 663 deletions

View File

@ -3,7 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50)
== 07/05/2013 ==
KLS: Added some lua functions
KLS: Fixed some lua functions
KLS: Perl now will (like lua) keep track of the values you return from EVENT_*. This allows perl to use the extra behavior in things like EVENT_DEATH. Now g,enerally the perl system is now considered deprecated (in favor of lua in the long term) I felt this was too big a change to pass up adding when I got it working.
KLS: Perl now will (like lua) keep track of the values you return from EVENT_*. This allows perl to use the extra behavior in things like EVENT_DEATH. Now generally the perl system is now considered deprecated (in favor of lua in the long term) I felt this was too big a change to pass up adding when I got it working.
== 07/02/2013 ==
KLS: Exported eq.follow(entity_id, [distance]) and eq.stop_follow() to lua.

View File

@ -16,8 +16,6 @@
#include "StringUtil.h"
#include <string>
#include <cstdarg>
#include <cstring> // for strncpy
#include <stdexcept>
@ -328,3 +326,15 @@ const char *ConvertArrayF(float input, char *returnchar)
sprintf(returnchar, "%0.2f", input);
return returnchar;
}
std::vector<std::string> SplitString(const std::string &str, char delim) {
std::vector<std::string> ret;
std::stringstream ss(str);
std::string item;
while(std::getline(ss, item, delim)) {
ret.push_back(item);
}
return ret;
}

View File

@ -16,7 +16,8 @@
#ifndef _STRINGUTIL_H_
#define _STRINGUTIL_H_
#include <string>
#include <sstream>
#include <vector>
#include <cstdarg>
#include "types.h"
@ -48,4 +49,6 @@ const char *ConvertArrayF(float input, char *returnchar);
void RemoveApostrophes(std::string &s);
char *RemoveApostrophes(const char *s);
std::vector<std::string> SplitString(const std::string &s, char delim);
#endif

View File

@ -837,50 +837,51 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
make_npc->npc_spells_id = 7;
break;
case PALADIN:
make_npc->npc_attacks[0] = 'T';
//SPECATK_TRIPLE
make_npc->special_abilities = std::to_string((long)SPECATK_TRIPLE) + std::string(",1");
make_npc->cur_hp = make_npc->cur_hp * 150 / 100;
make_npc->max_hp = make_npc->max_hp * 150 / 100;
make_npc->npc_spells_id = 8;
break;
case SHADOWKNIGHT:
make_npc->npc_attacks[0] = 'T';
make_npc->special_abilities = std::to_string((long)SPECATK_TRIPLE) + std::string(",1");
make_npc->cur_hp = make_npc->cur_hp * 150 / 100;
make_npc->max_hp = make_npc->max_hp * 150 / 100;
make_npc->npc_spells_id = 9;
break;
case RANGER:
make_npc->npc_attacks[0] = 'Q';
make_npc->special_abilities = std::to_string((long)SPECATK_QUAD) + std::string(",1");
make_npc->cur_hp = make_npc->cur_hp * 135 / 100;
make_npc->max_hp = make_npc->max_hp * 135 / 100;
make_npc->npc_spells_id = 10;
break;
case BARD:
make_npc->npc_attacks[0] = 'T';
make_npc->special_abilities = std::to_string((long)SPECATK_TRIPLE) + std::string(",1");
make_npc->cur_hp = make_npc->cur_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
make_npc->npc_spells_id = 11;
break;
case BEASTLORD:
make_npc->npc_attacks[0] = 'Q';
make_npc->special_abilities = std::to_string((long)SPECATK_QUAD) + std::string(",1");
make_npc->cur_hp = make_npc->cur_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
make_npc->npc_spells_id = 12;
break;
case ROGUE:
make_npc->npc_attacks[0] = 'Q';
make_npc->special_abilities = std::to_string((long)SPECATK_QUAD) + std::string(",1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->cur_hp = make_npc->cur_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
break;
case MONK:
make_npc->npc_attacks[0] = 'Q';
make_npc->special_abilities = std::to_string((long)SPECATK_QUAD) + std::string(",1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->cur_hp = make_npc->cur_hp * 135 / 100;
make_npc->max_hp = make_npc->max_hp * 135 / 100;
break;
case WARRIOR:
case BERSERKER:
make_npc->npc_attacks[0] = 'Q';
make_npc->special_abilities = std::to_string((long)SPECATK_QUAD) + std::string(",1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->cur_hp = make_npc->cur_hp * 175 / 100;
make_npc->max_hp = make_npc->max_hp * 175 / 100;

View File

@ -358,7 +358,7 @@ bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float
if(caster->AI_HasSpells() == false)
return false;
if(caster->SpecAttacks[NPC_NO_BUFFHEAL_FRIENDS])
if(caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS))
return false;
if (iChance < 100) {
@ -522,7 +522,7 @@ void NPC::AI_Start(uint32 iMoveDelay) {
if (NPCTypedata) {
AI_AddNPCSpells(NPCTypedata->npc_spells_id);
NPCSpecialAttacks(NPCTypedata->npc_attacks,0);
ProcessSpecialAbilities(NPCTypedata->special_abilities);
}
SendTo(GetX(), GetY(), GetZ());
@ -823,7 +823,7 @@ void Client::AI_Process()
if(GetTarget()) {
bool triple_attack_success = false;
if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER)
&& GetLevel() >= 60) || SpecAttacks[SPECATK_TRIPLE])
&& GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE))
&& CheckDoubleAttack(true))
{
Attack(GetTarget(), 13, true);
@ -1095,10 +1095,10 @@ void Mob::AI_Process() {
if(DivineAura())
return;
if(SpecAttacks[TETHER] || SpecAttacks[LEASH]) {
if(GetSpecialAbility(TETHER) || GetSpecialAbility(LEASH)) {
if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > pAggroRange*pAggroRange) {
GMMove(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY(), CastToNPC()->GetSpawnPointZ(), CastToNPC()->GetSpawnPointH());
if(SpecAttacks[LEASH]) {
if(GetSpecialAbility(LEASH)) {
SetHP(GetMaxHP());
BuffFadeAll();
WipeHateList();
@ -1164,27 +1164,25 @@ void Mob::AI_Process() {
//check double attack, this is NOT the same rules that clients use...
&& RandRoll < (GetLevel() + NPCDualAttackModifier))
{
if (Attack(target, 13))
Attack(target, 13);
// lets see if we can do a triple attack with the main hand
//pets are excluded from triple and quads...
if (GetSpecialAbility(SPECATK_TRIPLE)
&& !IsPet() && RandRoll < (GetLevel()+NPCTripleAttackModifier))
{
// lets see if we can do a triple attack with the main hand
//pets are excluded from triple and quads...
if (SpecAttacks[SPECATK_TRIPLE]
&& !IsPet() && RandRoll < (GetLevel()+NPCTripleAttackModifier))
Attack(target, 13);
// now lets check the quad attack
if (GetSpecialAbility(SPECATK_QUAD)
&& RandRoll < (GetLevel() + NPCQuadAttackModifier))
{
if (Attack(target, 13))
{ // now lets check the quad attack
if (SpecAttacks[SPECATK_QUAD]
&& RandRoll < (GetLevel() + NPCQuadAttackModifier))
{
Attack(target, 13);
}
}
Attack(target, 13);
}
}
}
}
if (SpecAttacks[SPECATK_FLURRY]) {
if (GetSpecialAbility(SPECATK_FLURRY)) {
uint8 npc_flurry = RuleI(Combat, NPCFlurryChance);
if (GetFlurryChance())
@ -1206,14 +1204,14 @@ void Mob::AI_Process() {
}
}
if (SpecAttacks[SPECATK_RAMPAGE])
if (GetSpecialAbility(SPECATK_RAMPAGE))
{
//simply based off dex for now, probably a better calc
if(MakeRandomInt(0, 100) < ((int)(GetDEX() / ((GetLevel() * 0.760) + 10.0)) + 5))
Rampage();
}
if (SpecAttacks[SPECATK_AREA_RAMPAGE])
if (GetSpecialAbility(SPECATK_AREA_RAMPAGE))
{
//simply based off dex for now, probably a better calc
@ -1227,7 +1225,7 @@ void Mob::AI_Process() {
{
int myclass = GetClass();
//can only dual wield without a weapon if your a monk
if(SpecAttacks[SPECATK_INNATE_DW] || (GetEquipment(MATERIAL_SECONDARY) != 0 && GetLevel() > 29) || myclass == MONK || myclass == MONKGM) {
if(GetSpecialAbility(SPECATK_INNATE_DW) || (GetEquipment(MATERIAL_SECONDARY) != 0 && GetLevel() > 29) || myclass == MONK || myclass == MONKGM) {
float DualWieldProbability = (GetSkill(DUAL_WIELD) + GetLevel()) / 400.0f;
if(MakeRandomFloat(0.0, 1.0) < DualWieldProbability)
{
@ -1274,7 +1272,7 @@ void Mob::AI_Process() {
if (!HateSummon())
{
//could not summon them, check ranged...
if(SpecAttacks[SPECATK_RANGED_ATK])
if(GetSpecialAbility(SPECATK_RANGED_ATK))
doranged = true;
// Now pursue
@ -1863,33 +1861,27 @@ void Mob::StartEnrage()
// dont continue if already enraged
if (bEnraged)
return;
if (SpecAttackTimers[SPECATK_ENRAGE] && !SpecAttackTimers[SPECATK_ENRAGE]->Check())
return;
// see if NPC has possibility to enrage
if (!SpecAttacks[SPECATK_ENRAGE])
return;
// check if timer exists (should be true at all times)
if (SpecAttackTimers[SPECATK_ENRAGE])
{
safe_delete(SpecAttackTimers[SPECATK_ENRAGE]);
SpecAttackTimers[SPECATK_ENRAGE] = nullptr;
}
if (!SpecAttackTimers[SPECATK_ENRAGE])
{
SpecAttackTimers[SPECATK_ENRAGE] = new Timer(EnragedDurationTimer);
}
if(!GetSpecialAbility(SPECATK_ENRAGE))
return;
Timer *timer = GetSpecialAbilityTimer(SPECATK_ENRAGE);
if (timer && !timer->Check())
return;
StartSpecialAbilityTimer(SPECATK_ENRAGE, EnragedDurationTimer);
// start the timer. need to call IsEnraged frequently since we dont have callback timers :-/
SpecAttackTimers[SPECATK_ENRAGE]->Start();
bEnraged = true;
entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_START, GetCleanName());
}
void Mob::ProcessEnrage(){
if(IsEnraged()){
if(SpecAttackTimers[SPECATK_ENRAGE] && SpecAttackTimers[SPECATK_ENRAGE]->Check()){
Timer *timer = GetSpecialAbilityTimer(SPECATK_ENRAGE);
if(timer && timer->Check()){
entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_END, GetCleanName());
SpecAttackTimers[SPECATK_ENRAGE]->Start(EnragedTimer);
StartSpecialAbilityTimer(SPECATK_ENRAGE, EnragedTimer);
bEnraged = false;
}
}
@ -1921,7 +1913,7 @@ bool Mob::AddRampage(Mob *mob)
if(!mob)
return false;
if (!SpecAttacks[SPECATK_RAMPAGE])
if (!GetSpecialAbility(SPECATK_RAMPAGE))
return false;
for (int i = 0; i < RampageArray.size(); i++)

View File

@ -517,7 +517,7 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
if(this == target) // you can attack yourself
return true;
if(target->SpecAttacks[NO_HARM_FROM_CLIENT]){
if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){
return false;
}

View File

@ -771,12 +771,12 @@ int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) {
int banedmg = 0;
//can't hit invulnerable stuff with weapons.
if(against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){
if(against->GetInvul() || against->GetSpecialAbility(IMMUNE_MELEE)){
return 0;
}
//check to see if our weapons or fists are magical.
if(against->SpecAttacks[IMMUNE_MELEE_NONMAGICAL]){
if(against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)){
if(weapon_item){
if(weapon_item->Magic){
dmg = weapon_item->Damage;
@ -797,7 +797,7 @@ int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) {
//it gives us an idea if we can hit due to the dual nature of this function
dmg = 1;
}
else if(SpecAttacks[SPECATK_MAGICAL])
else if(GetSpecialAbility(SPECATK_MAGICAL))
{
dmg = 1;
}
@ -822,7 +822,7 @@ int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) {
}
int eledmg = 0;
if(!against->SpecAttacks[IMMUNE_MAGIC]){
if(!against->GetSpecialAbility(IMMUNE_MAGIC)){
if(weapon_item && weapon_item->ElemDmgAmt){
//we don't check resist for npcs here
eledmg = weapon_item->ElemDmgAmt;
@ -830,7 +830,7 @@ int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) {
}
}
if(against->SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE]){
if(against->GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)){
if(weapon_item){
if(weapon_item->BaneDmgBody == against->GetBodyType()){
banedmg += weapon_item->BaneDmgAmt;
@ -842,7 +842,7 @@ int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) {
}
if(!eledmg && !banedmg){
if(!SpecAttacks[SPECATK_BANE])
if(!GetSpecialAbility(SPECATK_BANE))
return 0;
else
return 1;
@ -877,7 +877,7 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
int dmg = 0;
int banedmg = 0;
if(!against || against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){
if(!against || against->GetInvul() || against->GetSpecialAbility(IMMUNE_MELEE)){
return 0;
}
@ -898,7 +898,7 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
}
}
if(against->SpecAttacks[IMMUNE_MELEE_NONMAGICAL]){
if(against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)){
if(weapon_item){
// check to see if the weapon is magic
bool MagicWeapon = false;
@ -937,7 +937,7 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ //pets wouldn't actually use this but...
dmg = 1; //it gives us an idea if we can hit
}
else if(SpecAttacks[SPECATK_MAGICAL]){
else if(GetSpecialAbility(SPECATK_MAGICAL)){
dmg = 1;
}
else
@ -976,7 +976,7 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
}
int eledmg = 0;
if(!against->SpecAttacks[IMMUNE_MAGIC]){
if(!against->GetSpecialAbility(IMMUNE_MAGIC)){
if(weapon_item && weapon_item->GetItem() && weapon_item->GetItem()->ElemDmgAmt){
if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){
eledmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->ElemDmgAmt);
@ -1001,7 +1001,7 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
}
}
if(against->SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE]){
if(against->GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)){
if(weapon_item && weapon_item->GetItem()){
if(weapon_item->GetItem()->BaneDmgBody == against->GetBodyType()){
if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){
@ -1036,7 +1036,7 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
if(!eledmg && !banedmg)
{
if(!SpecAttacks[SPECATK_BANE])
if(!GetSpecialAbility(SPECATK_BANE))
return 0;
else
return 1;
@ -2423,16 +2423,16 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp,
if(IsClient() && !IsAIControlled())
return;
if(IsFamiliar() || SpecAttacks[IMMUNE_AGGRO])
if(IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO))
return;
if (other == myowner)
return;
if(other->SpecAttacks[IMMUNE_AGGRO_ON])
if(other->GetSpecialAbility(IMMUNE_AGGRO_ON))
return;
if(SpecAttacks[NPC_TUNNELVISION]) {
if(GetSpecialAbility(NPC_TUNNELVISION)) {
Mob *top = GetTarget();
if(top && top != other) {
hate *= RuleR(Aggro, TunnelVisionAggroMod);
@ -2493,7 +2493,7 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp,
} else {
// cb:2007-08-17
// owner must get on list, but he's not actually gained any hate yet
if(!owner->SpecAttacks[IMMUNE_AGGRO])
if(!owner->GetSpecialAbility(IMMUNE_AGGRO))
{
hate_list.Add(owner, 0, 0, false, !iBuffTic);
if(owner->IsClient())
@ -2503,10 +2503,10 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp,
}
if (mypet && (!(GetAA(aaPetDiscipline) && mypet->IsHeld()))) { // I have a pet, add other to it
if(!mypet->IsFamiliar() && !mypet->SpecAttacks[IMMUNE_AGGRO])
if(!mypet->IsFamiliar() && !mypet->GetSpecialAbility(IMMUNE_AGGRO))
mypet->hate_list.Add(other, 0, 0, bFrenzy);
} else if (myowner) { // I am a pet, add other to owner if it's NPC/LD
if (myowner->IsAIControlled() && !myowner->SpecAttacks[IMMUNE_AGGRO])
if (myowner->IsAIControlled() && !myowner->GetSpecialAbility(IMMUNE_AGGRO))
myowner->hate_list.Add(other, 0, 0, bFrenzy);
}
if (!wasengaged) {
@ -3366,7 +3366,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
} //end `if there is some damage being done and theres anattacker person involved`
Mob *pet = GetPet();
if (pet && !pet->IsFamiliar() && !pet->SpecAttacks[IMMUNE_AGGRO] && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse())
if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse())
{
if (!pet->IsHeld()) {
mlog(PETS__AGGRO, "Sending pet %s into battle due to attack.", pet->GetName());
@ -4143,7 +4143,7 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage)
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRIPPLING_BLOW, GetCleanName(), itoa(damage));
// Crippling blows also have a chance to stun
//Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message.
if (defender->GetLevel() <= 55 && !defender->SpecAttacks[IMMUNE_STUN]){
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)){
defender->Emote("staggers.");
defender->Stun(0);
}

View File

@ -3438,7 +3438,7 @@ bool Bot::CanDoSpecialAttack(Mob *other)
return false;
}
if(other->GetInvul() || other->SpecAttacks[IMMUNE_MELEE])
if(other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE))
return false;
return true;
@ -3776,13 +3776,13 @@ void Bot::AI_Process() {
Attack(GetTarget(), SLOT_PRIMARY, true);
}
if(BotOwner && GetTarget() && SpecAttacks[SPECATK_TRIPLE] && CheckBotDoubleAttack(true)) {
if(BotOwner && GetTarget() && GetSpecialAbility(SPECATK_TRIPLE) && CheckBotDoubleAttack(true)) {
tripleSuccess = true;
Attack(GetTarget(), SLOT_PRIMARY, true);
}
//quad attack, does this belong here??
if(BotOwner && GetTarget() && SpecAttacks[SPECATK_QUAD] && CheckBotDoubleAttack(true)) {
if(BotOwner && GetTarget() && GetSpecialAbility(SPECATK_QUAD) && CheckBotDoubleAttack(true)) {
Attack(GetTarget(), SLOT_PRIMARY, true);
}
}
@ -8098,7 +8098,7 @@ void Bot::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage)
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRIPPLING_BLOW, GetCleanName(), itoa(damage));
// Crippling blows also have a chance to stun
//Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message.
if (defender->GetLevel() <= 55 && !defender->SpecAttacks[IMMUNE_STUN]){
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)){
defender->Emote("staggers.");
defender->Stun(0);
}
@ -10258,7 +10258,7 @@ void Bot::GenerateSpecialAttacks()
{
// Special Attacks
if(((GetClass() == MONK) || (GetClass() == WARRIOR) || (GetClass() == RANGER) || (GetClass() == BERSERKER)) && (GetLevel() >= 60)) {
SpecAttacks[SPECATK_TRIPLE] = true;
SetSpecialAbility(SPECATK_TRIPLE, 1);
}
}

View File

@ -484,7 +484,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
if(botClass == PALADIN)
stunChance = 50;
if(!tar->SpecAttacks[UNSTUNABLE] && !tar->IsStunned() && (MakeRandomInt(1, 100) <= stunChance)) {
if(!tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned() && (MakeRandomInt(1, 100) <= stunChance)) {
botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target);
}
}

View File

@ -2316,7 +2316,7 @@ bool Client::CheckIncreaseSkill(SkillType skillid, Mob *against_who, int chancem
if(against_who)
{
if(against_who->SpecAttacks[IMMUNE_AGGRO] || against_who->IsClient() ||
if(against_who->GetSpecialAbility(IMMUNE_AGGRO) || against_who->IsClient() ||
GetLevelCon(against_who->GetLevel()) == CON_GREEN)
{
//false by default

View File

@ -419,7 +419,7 @@ bool Client::Process() {
//triple attack: rangers, monks, warriors, berserkers over level 60
if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER)
&& GetLevel() >= 60) || SpecAttacks[SPECATK_TRIPLE])
&& GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE))
&& CheckDoubleAttack(true))
{
tripleAttackSuccess = true;
@ -427,7 +427,7 @@ bool Client::Process() {
}
//quad attack, does this belong here??
if(SpecAttacks[SPECATK_QUAD] && CheckDoubleAttack(true))
if(GetSpecialAbility(SPECATK_QUAD) && CheckDoubleAttack(true))
{
Attack(auto_attack_target, 13, false);
}

View File

@ -3373,7 +3373,7 @@ void command_viewnpctype(Client *c, const Seperator *sep)
c->Message(0, " Class: %i", npct->class_);
c->Message(0, " MinDmg: %i", npct->min_dmg);
c->Message(0, " MaxDmg: %i", npct->max_dmg);
c->Message(0, " Attacks: %s", npct->npc_attacks);
c->Message(0, " Special Abilities: %s", npct->special_abilities.c_str());
c->Message(0, " Spells: %i", npct->npc_spells_id);
c->Message(0, " Loot Table: %i", npct->loottable_id);
c->Message(0, " NPCFactionID: %i", npct->npc_faction_id);

View File

@ -128,8 +128,7 @@ enum {
LEASH, // J - Dispell, wipe agro && return to spawn
TETHER, // j - Return to spawn
DESTRUCTIBLE_OBJECT, // o - This is only for destructible objects
NO_HARM_FROM_CLIENT, // Z - This is to prevent attacking NPC's period for clients
SPECATK_MAXNUM
NO_HARM_FROM_CLIENT // Z - This is to prevent attacking NPC's period for clients
};
typedef enum { //fear states

View File

@ -2646,57 +2646,6 @@ void EntityList::CountNPC(uint32* NPCCount, uint32* NPCLootCount, uint32* gmspaw
}
}
void EntityList::DoZoneDump(ZSDump_Spawn2* spawn2_dump, ZSDump_NPC* npc_dump, ZSDump_NPC_Loot* npcloot_dump, NPCType* gmspawntype_dump) {
uint32 spawn2index = 0;
uint32 NPCindex = 0;
uint32 NPCLootindex = 0;
uint32 gmspawntype_index = 0;
if (npc_dump != 0) {
LinkedListIterator<NPC*> iterator(npc_list);
NPC* npc = 0;
iterator.Reset();
while(iterator.MoreElements())
{
npc = iterator.GetData()->CastToNPC();
if (spawn2_dump != 0)
npc_dump[NPCindex].spawn2_dump_index = zone->DumpSpawn2(spawn2_dump, &spawn2index, npc->respawn2);
npc_dump[NPCindex].npctype_id = npc->GetNPCTypeID();
npc_dump[NPCindex].cur_hp = npc->GetHP();
if (npc->IsCorpse()) {
if (npc->CastToCorpse()->IsLocked())
npc_dump[NPCindex].corpse = 2;
else
npc_dump[NPCindex].corpse = 1;
}
else
npc_dump[NPCindex].corpse = 0;
npc_dump[NPCindex].decay_time_left = 0xFFFFFFFF;
npc_dump[NPCindex].x = npc->GetX();
npc_dump[NPCindex].y = npc->GetY();
npc_dump[NPCindex].z = npc->GetZ();
npc_dump[NPCindex].heading = npc->GetHeading();
npc_dump[NPCindex].copper = npc->copper;
npc_dump[NPCindex].silver = npc->silver;
npc_dump[NPCindex].gold = npc->gold;
npc_dump[NPCindex].platinum = npc->platinum;
if (npcloot_dump != 0)
npc->DumpLoot(NPCindex, npcloot_dump, &NPCLootindex);
if (gmspawntype_dump != 0) {
if (npc->GetNPCTypeID() == 0) {
memcpy(&gmspawntype_dump[gmspawntype_index], npc->NPCTypedata, sizeof(NPCType));
npc_dump[NPCindex].gmspawntype_index = gmspawntype_index;
gmspawntype_index++;
}
}
NPCindex++;
iterator.Advance();
}
}
if (spawn2_dump != 0)
zone->DumpAllSpawn2(spawn2_dump, &spawn2index);
}
void EntityList::Depop(bool StartSpawnTimer) {
LinkedListIterator<NPC*> iterator(npc_list);
@ -3212,7 +3161,7 @@ void EntityList::ClearFeignAggro(Mob* targ)
{
if (iterator.GetData()->CheckAggro(targ))
{
if(iterator.GetData()->SpecAttacks[IMMUNE_FEIGN_DEATH])
if(iterator.GetData()->GetSpecialAbility(IMMUNE_FEIGN_DEATH))
{
iterator.Advance();
continue;

View File

@ -316,7 +316,6 @@ public:
static char* RemoveNumbers(char* name);
void SignalMobsByNPCID(uint32 npc_type, int signal_id);
void CountNPC(uint32* NPCCount, uint32* NPCLootCount, uint32* gmspawntype_count);
void DoZoneDump(ZSDump_Spawn2* spawn2dump, ZSDump_NPC* npcdump, ZSDump_NPC_Loot* npclootdump, NPCType* gmspawntype_dump);
void RemoveEntity(uint16 id);
void SendPetitionToAdmins(Petition* pet);
void SendPetitionToAdmins();

View File

@ -45,7 +45,7 @@ void Mob::CheckFlee() {
return;
//dont bother if we are immune to fleeing
if(SpecAttacks[IMMUNE_FLEEING] || spellbonuses.ImmuneToFlee)
if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee)
return;
if(!flee_timer.Check())
@ -105,7 +105,7 @@ void Mob::ProcessFlee() {
//Stop fleeing if effect is applied after they start to run.
//When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee.
if(flee_mode && (SpecAttacks[IMMUNE_FLEEING] || spellbonuses.ImmuneToFlee) && !spellbonuses.IsFeared){
if(flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && !spellbonuses.IsFeared){
curfp = false;
return;
}

View File

@ -86,7 +86,13 @@ const NPCType *Horse::BuildHorseType(uint16 spell_id) {
NPCType* npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
strcpy(npc_type->name,"Unclaimed_Mount"); //this should never get used
strcpy(npc_type->npc_attacks,"ABH");
npc_type->special_abilities = itoa(IMMUNE_MELEE);
npc_type->special_abilities += std::string(",1^");
npc_type->special_abilities += itoa(IMMUNE_MAGIC);
npc_type->special_abilities += std::string(",1^");
npc_type->special_abilities += itoa(IMMUNE_AGGRO);
npc_type->special_abilities += std::string(",1");
npc_type->cur_hp = 1;
npc_type->max_hp = 1;
npc_type->race = atoi(row[0]);

View File

@ -1671,13 +1671,13 @@ void Merc::AI_Process() {
Attack(GetTarget(), SLOT_PRIMARY, true);
}
if(GetOwner() && GetTarget() && SpecAttacks[SPECATK_TRIPLE]) {
if(GetOwner() && GetTarget() && GetSpecialAbility(SPECATK_TRIPLE)) {
tripleSuccess = true;
Attack(GetTarget(), SLOT_PRIMARY, true);
}
//quad attack, does this belong here??
if(GetOwner() && GetTarget() && SpecAttacks[SPECATK_QUAD]) {
if(GetOwner() && GetTarget() && GetSpecialAbility(SPECATK_QUAD)) {
Attack(GetTarget(), SLOT_PRIMARY, true);
}
}
@ -1851,7 +1851,7 @@ void Merc::AI_Start(int32 iMoveDelay) {
if (NPCTypedata_ours) {
//AI_AddNPCSpells(ourNPCData->npc_spells_id);
NPCSpecialAttacks(NPCTypedata_ours->npc_attacks,0);
ProcessSpecialAbilities(NPCTypedata_ours->special_abilities);
}
SendTo(GetX(), GetY(), GetZ());
@ -2373,7 +2373,7 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
selectedMercSpell = GetBestMercSpellForAENuke(this, tar);
if(selectedMercSpell.spellid == 0 && !tar->SpecAttacks[UNSTUNABLE] && !tar->IsStunned()) {
if(selectedMercSpell.spellid == 0 && !tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned()) {
uint8 stunChance = 15;
if(MakeRandomInt(1, 100) <= stunChance) {
selectedMercSpell = GetBestMercSpellForStun(this);

View File

@ -299,10 +299,7 @@ Mob::Mob(const char* in_name,
shielder[m].shielder_id = 0;
shielder[m].shielder_bonus = 0;
}
for (i=0; i<SPECATK_MAXNUM ; i++) {
SpecAttacks[i] = false;
SpecAttackTimers[i] = 0;
}
destructibleobject = false;
wandertype=0;
pausetype=0;
@ -402,9 +399,9 @@ Mob::~Mob()
else
SetPet(0);
}
for (int i=0; i<SPECATK_MAXNUM ; i++) {
safe_delete(SpecAttackTimers[i]);
}
ClearSpecialAbilities();
EQApplicationPacket app;
CreateDespawnPacket(&app, !IsCorpse());
Corpse* corpse = entity_list.GetCorpseByID(GetID());
@ -2339,19 +2336,19 @@ bool Mob::HateSummon() {
if(GetOwnerID())
mob_owner = entity_list.GetMob(GetOwnerID());
if (GetHPRatio() >= 98 || SpecAttacks[SPECATK_SUMMON] == false || !GetTarget() ||
if (GetHPRatio() >= 98 || GetSpecialAbility(SPECATK_SUMMON) == 0 || !GetTarget() ||
(mob_owner && mob_owner->IsClient() && !CheckLosFN(GetTarget())))
return false;
// now validate the timer
if (!SpecAttackTimers[SPECATK_SUMMON])
Timer *timer = GetSpecialAbilityTimer(SPECATK_SUMMON);
if (!timer)
{
SpecAttackTimers[SPECATK_SUMMON] = new Timer(6000);
SpecAttackTimers[SPECATK_SUMMON]->Start();
StartSpecialAbilityTimer(SPECATK_SUMMON, 6000);
}
// now check the timer
if (!SpecAttackTimers[SPECATK_SUMMON]->Check())
if (!timer->Check())
return false;
// get summon target
@ -2813,7 +2810,7 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) {
void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on) {
// Changed proc targets to look up based on the spells goodEffect flag.
// This should work for the majority of weapons.
if(spell_id == SPELL_UNKNOWN || on->SpecAttacks[NO_HARM_FROM_CLIENT]) {
if(spell_id == SPELL_UNKNOWN || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
//This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging.
return;
}
@ -4778,3 +4775,117 @@ bool Mob::HasSpellEffect(int effectid)
return(0);
}
int Mob::GetSpecialAbility(int ability) {
auto iter = SpecialAbilities.find(ability);
if(iter != SpecialAbilities.end()) {
return iter->second.level;
}
return 0;
}
void Mob::SetSpecialAbility(int ability, int level) {
auto iter = SpecialAbilities.find(ability);
if(iter != SpecialAbilities.end()) {
SpecialAbility spec = iter->second;
spec.level = level;
SpecialAbilities[ability] = spec;
} else {
SpecialAbility spec;
spec.level = level;
spec.timer = nullptr;
SpecialAbilities[ability] = spec;
}
}
void Mob::StartSpecialAbilityTimer(int ability, uint32 time) {
auto iter = SpecialAbilities.find(ability);
if(iter != SpecialAbilities.end()) {
SpecialAbility spec = iter->second;
spec.level = level;
if(spec.timer) {
spec.timer->Start(time);
} else {
spec.timer = new Timer(time);
spec.timer->Start();
}
SpecialAbilities[ability] = spec;
} else {
SpecialAbility spec;
spec.level = level;
spec.timer = new Timer(time);
spec.timer->Start();
SpecialAbilities[ability] = spec;
}
}
void Mob::StopSpecialAbilityTimer(int ability) {
auto iter = SpecialAbilities.find(ability);
if(iter != SpecialAbilities.end()) {
SpecialAbility spec = iter->second;
if(spec.timer) {
delete spec.timer;
spec.timer = nullptr;
}
SpecialAbilities[ability] = spec;
}
}
Timer *Mob::GetSpecialAbilityTimer(int ability) {
auto iter = SpecialAbilities.find(ability);
if(iter != SpecialAbilities.end()) {
return iter->second.timer;
}
return nullptr;
}
void Mob::ClearSpecialAbilities() {
auto iter = SpecialAbilities.begin();
while(iter != SpecialAbilities.end()) {
if(iter->second.timer) {
delete iter->second.timer;
}
++iter;
}
SpecialAbilities.clear();
}
void Mob::ProcessSpecialAbilities(const std::string str) {
ClearSpecialAbilities();
std::vector<std::string> sp = SplitString(str, '^');
for(auto iter = sp.begin(); iter != sp.end(); ++iter) {
std::vector<std::string> sub_sp = SplitString((*iter), ',');
if(sub_sp.size() == 2) {
int ability = std::stoi(sub_sp[0]);
int value = std::stoi(sub_sp[1]);
SetSpecialAbility(ability, value);
switch(ability) {
case SPECATK_SUMMON:
if(value > 0) {
StartSpecialAbilityTimer(SPECATK_SUMMON, 6000);
}
break;
case SPECATK_QUAD:
if(value > 0) {
SetSpecialAbility(SPECATK_TRIPLE, 1);
}
break;
case DESTRUCTIBLE_OBJECT:
if(value == 0) {
SetDestructibleObject(false);
} else {
SetDestructibleObject(true);
}
break;
default:
break;
}
}
}
}

View File

@ -37,6 +37,11 @@ public:
CLIENT_KICKED, DISCONNECTED, CLIENT_ERROR, CLIENT_CONNECTINGALL };
enum eStandingPetOrder { SPO_Follow, SPO_Sit, SPO_Guard };
struct SpecialAbility {
int level;
Timer *timer;
};
Mob(const char* in_name,
const char* in_lastname,
int32 in_cur_hp,
@ -758,12 +763,19 @@ public:
void SetNextIncHPEvent( int inchpevent );
bool DivineAura() const;
bool SpecAttacks[SPECATK_MAXNUM];
bool HasNPCSpecialAtk(const char* parse);
int GetSpecialAbility(int ability);
void SetSpecialAbility(int ability, int level);
void StartSpecialAbilityTimer(int ability, uint32 time);
void StopSpecialAbilityTimer(int ability);
Timer *GetSpecialAbilityTimer(int ability);
void ClearSpecialAbilities();
void ProcessSpecialAbilities(const std::string str);
Shielders_Struct shielder[MAX_SHIELDERS];
Trade* trade;
inline float GetCWPX() const { return(cur_wp_x); }
inline float GetCWPY() const { return(cur_wp_y); }
inline float GetCWPZ() const { return(cur_wp_z); }
@ -947,10 +959,6 @@ protected:
char clean_name[64];
char lastname[64];
bool bEnraged;
Timer *SpecAttackTimers[SPECATK_MAXNUM];
bool destructibleobject;
int32 delta_heading;
float delta_x;
float delta_y;
@ -1145,6 +1153,10 @@ protected:
void InsertQuestGlobal(int charid, int npcid, int zoneid, const char *name, const char *value, int expdate);
uint16 emoteid;
std::map<int, SpecialAbility> SpecialAbilities;
bool bEnraged;
bool destructibleobject;
private:
void _StopSong(); //this is not what you think it is
Mob* target;

View File

@ -716,23 +716,6 @@ uint32 NPC::CountLoot() {
return(itemlist.size());
}
void NPC::DumpLoot(uint32 npcdump_index, ZSDump_NPC_Loot* npclootdump, uint32* NPCLootindex) {
ItemList::iterator cur,end;
cur = itemlist.begin();
end = itemlist.end();
for(; cur != end; cur++) {
ServerLootItem_Struct* item = *cur;
npclootdump[*NPCLootindex].npc_dump_index = npcdump_index;
npclootdump[*NPCLootindex].itemid = item->item_id;
npclootdump[*NPCLootindex].charges = item->charges;
npclootdump[*NPCLootindex].equipSlot = item->equipSlot;
npclootdump[*NPCLootindex].minlevel = item->minlevel;
npclootdump[*NPCLootindex].maxlevel = item->maxlevel;
(*NPCLootindex)++;
}
ClearItemList();
}
void NPC::Depop(bool StartSpawnTimer) {
uint16 emoteid = this->GetEmoteID();
if(emoteid != 0)
@ -1431,11 +1414,7 @@ void NPC::PickPocket(Client* thief) {
void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool remove) {
if(reset)
{
for(int i = 0; i < SPECATK_MAXNUM; i++)
{
SpecAttacks[i] = false;
safe_delete(SpecAttackTimers[i]);
}
ClearSpecialAbilities();
}
const char* orig_parse = parse;
@ -1444,124 +1423,122 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem
switch(*parse)
{
case 'E':
SpecAttacks[SPECATK_ENRAGE] = (remove ? false : true);
SetSpecialAbility(SPECATK_ENRAGE, remove ? 0 : 1);
break;
case 'F':
SpecAttacks[SPECATK_FLURRY] = (remove ? false : true);
SetSpecialAbility(SPECATK_FLURRY, remove ? 0 : 1);
break;
case 'R':
SpecAttacks[SPECATK_RAMPAGE] = (remove ? false : true);
SetSpecialAbility(SPECATK_RAMPAGE, remove ? 0 : 1);
break;
case 'r':
SpecAttacks[SPECATK_AREA_RAMPAGE] = (remove ? false : true);
SetSpecialAbility(SPECATK_AREA_RAMPAGE, remove ? 0 : 1);
break;
case 'S':
if(remove) {
SpecAttacks[SPECATK_SUMMON] = false;
safe_delete(SpecAttackTimers[SPECATK_SUMMON]);
SetSpecialAbility(SPECATK_SUMMON, 0);
StopSpecialAbilityTimer(SPECATK_SUMMON);
} else {
SpecAttacks[SPECATK_SUMMON] = true;
safe_delete(SpecAttackTimers[SPECATK_SUMMON]);
SpecAttackTimers[SPECATK_SUMMON] = new Timer(6000);
SpecAttackTimers[SPECATK_SUMMON]->Start();
SetSpecialAbility(SPECATK_SUMMON, 1);
StartSpecialAbilityTimer(SPECATK_SUMMON, 6000);
}
break;
case 'T':
SpecAttacks[SPECATK_TRIPLE] = (remove ? false : true);
SetSpecialAbility(SPECATK_TRIPLE, remove ? 0 : 1);
break;
case 'Q':
//quad requires triple to work properly
if(remove) {
SpecAttacks[SPECATK_QUAD] = false;
SetSpecialAbility(SPECATK_QUAD, 0);
} else {
SpecAttacks[SPECATK_TRIPLE] = true;
SpecAttacks[SPECATK_QUAD] = true;
}
SetSpecialAbility(SPECATK_TRIPLE, 1);
SetSpecialAbility(SPECATK_QUAD, 1);
}
break;
case 'b':
SpecAttacks[SPECATK_BANE] = (remove ? false : true);
SetSpecialAbility(SPECATK_BANE, remove ? 0 : 1);
break;
case 'm':
SpecAttacks[SPECATK_MAGICAL] = (remove ? false : true);
SetSpecialAbility(SPECATK_MAGICAL, remove ? 0 : 1);
break;
case 'U':
SpecAttacks[UNSLOWABLE] = (remove ? false : true);
SetSpecialAbility(UNSLOWABLE, remove ? 0 : 1);
break;
case 'M':
SpecAttacks[UNMEZABLE] = (remove ? false : true);
SetSpecialAbility(UNMEZABLE, remove ? 0 : 1);
break;
case 'C':
SpecAttacks[UNCHARMABLE] = (remove ? false : true);
SetSpecialAbility(UNCHARMABLE, remove ? 0 : 1);
break;
case 'N':
SpecAttacks[UNSTUNABLE] = (remove ? false : true);
SetSpecialAbility(UNSTUNABLE, remove ? 0 : 1);
break;
case 'I':
SpecAttacks[UNSNAREABLE] = (remove ? false : true);
SetSpecialAbility(UNSNAREABLE, remove ? 0 : 1);
break;
case 'D':
SpecAttacks[UNFEARABLE] = (remove ? false : true);
SetSpecialAbility(UNFEARABLE, remove ? 0 : 1);
break;
case 'K':
SpecAttacks[UNDISPELLABLE] = (remove ? false : true);
SetSpecialAbility(UNDISPELLABLE, remove ? 0 : 1);
break;
case 'A':
SpecAttacks[IMMUNE_MELEE] = (remove ? false : true);
SetSpecialAbility(IMMUNE_MELEE, remove ? 0 : 1);
break;
case 'B':
SpecAttacks[IMMUNE_MAGIC] = (remove ? false : true);
SetSpecialAbility(IMMUNE_MAGIC, remove ? 0 : 1);
break;
case 'f':
SpecAttacks[IMMUNE_FLEEING] = (remove ? false : true);
SetSpecialAbility(IMMUNE_FLEEING, remove ? 0 : 1);
break;
case 'O':
SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE] = (remove ? false : true);
SetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE, remove ? 0 : 1);
break;
case 'W':
SpecAttacks[IMMUNE_MELEE_NONMAGICAL] = (remove ? false : true);
SetSpecialAbility(IMMUNE_MELEE_NONMAGICAL, remove ? 0 : 1);
break;
case 'H':
SpecAttacks[IMMUNE_AGGRO] = (remove ? false : true);
SetSpecialAbility(IMMUNE_AGGRO, remove ? 0 : 1);
break;
case 'G':
SpecAttacks[IMMUNE_AGGRO_ON] = (remove ? false : true);
SetSpecialAbility(IMMUNE_AGGRO_ON, remove ? 0 : 1);
break;
case 'g':
SpecAttacks[IMMUNE_CASTING_FROM_RANGE] = (remove ? false : true);
SetSpecialAbility(IMMUNE_CASTING_FROM_RANGE, remove ? 0 : 1);
break;
case 'd':
SpecAttacks[IMMUNE_FEIGN_DEATH] = (remove ? false : true);
SetSpecialAbility(IMMUNE_FEIGN_DEATH, remove ? 0 : 1);
break;
case 'Y':
SpecAttacks[SPECATK_RANGED_ATK] = (remove ? false : true);
SetSpecialAbility(SPECATK_RANGED_ATK, remove ? 0 : 1);
break;
case 'L':
SpecAttacks[SPECATK_INNATE_DW] = (remove ? false : true);
SetSpecialAbility(SPECATK_INNATE_DW, remove ? 0 : 1);
break;
case 't':
SpecAttacks[NPC_TUNNELVISION] = (remove ? false : true);
SetSpecialAbility(NPC_TUNNELVISION, remove ? 0 : 1);
break;
case 'n':
SpecAttacks[NPC_NO_BUFFHEAL_FRIENDS] = (remove ? false : true);
SetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS, remove ? 0 : 1);
break;
case 'p':
SpecAttacks[IMMUNE_PACIFY] = (remove ? false : true);
SetSpecialAbility(IMMUNE_PACIFY, remove ? 0 : 1);
break;
case 'J':
SpecAttacks[LEASH] = (remove ? false : true);
SetSpecialAbility(LEASH, remove ? 0 : 1);
break;
case 'j':
SpecAttacks[TETHER] = (remove ? false : true);
SetSpecialAbility(TETHER, remove ? 0 : 1);
break;
case 'o':
SpecAttacks[DESTRUCTIBLE_OBJECT] = (remove ? false : true);
SetDestructibleObject(true);
SetSpecialAbility(DESTRUCTIBLE_OBJECT, remove ? 0 : 1);
SetDestructibleObject(remove ? true : false);
break;
case 'Z':
SpecAttacks[NO_HARM_FROM_CLIENT] = (remove ? false : true);
SetSpecialAbility(NO_HARM_FROM_CLIENT, remove ? 0 : 1);
break;
case 'i':
SpecAttacks[IMMUNE_TAUNT] = (remove ? false : true);
SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1);
break;
default:
@ -1588,138 +1565,138 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
switch(*parse)
{
case 'E':
if (!SpecAttacks[SPECATK_ENRAGE])
if (!GetSpecialAbility(SPECATK_ENRAGE))
HasAllAttacks = false;
break;
case 'F':
if (!SpecAttacks[SPECATK_FLURRY])
if (!GetSpecialAbility(SPECATK_FLURRY))
HasAllAttacks = false;
break;
case 'R':
if (!SpecAttacks[SPECATK_RAMPAGE])
if (!GetSpecialAbility(SPECATK_RAMPAGE))
HasAllAttacks = false;
break;
case 'r':
if (!SpecAttacks[SPECATK_AREA_RAMPAGE])
if (!GetSpecialAbility(SPECATK_AREA_RAMPAGE))
HasAllAttacks = false;
break;
case 'S':
if (!SpecAttacks[SPECATK_SUMMON])
if (!GetSpecialAbility(SPECATK_SUMMON))
HasAllAttacks = false;
break;
case 'T':
if (!SpecAttacks[SPECATK_TRIPLE])
if (!GetSpecialAbility(SPECATK_TRIPLE))
HasAllAttacks = false;
break;
case 'Q':
if (!SpecAttacks[SPECATK_QUAD])
if (!GetSpecialAbility(SPECATK_QUAD))
HasAllAttacks = false;
break;
case 'b':
if (!SpecAttacks[SPECATK_BANE])
if (!GetSpecialAbility(SPECATK_BANE))
HasAllAttacks = false;
break;
case 'm':
if (!SpecAttacks[SPECATK_MAGICAL])
if (!GetSpecialAbility(SPECATK_MAGICAL))
HasAllAttacks = false;
break;
case 'U':
if (!SpecAttacks[UNSLOWABLE])
if (!GetSpecialAbility(UNSLOWABLE))
HasAllAttacks = false;
break;
case 'M':
if (!SpecAttacks[UNMEZABLE])
if (!GetSpecialAbility(UNMEZABLE))
HasAllAttacks = false;
break;
case 'C':
if (!SpecAttacks[UNCHARMABLE])
if (!GetSpecialAbility(UNCHARMABLE))
HasAllAttacks = false;
break;
case 'N':
if (!SpecAttacks[UNSTUNABLE])
if (!GetSpecialAbility(UNSTUNABLE))
HasAllAttacks = false;
break;
case 'I':
if (!SpecAttacks[UNSNAREABLE])
if (!GetSpecialAbility(UNSNAREABLE))
HasAllAttacks = false;
break;
case 'D':
if (!SpecAttacks[UNFEARABLE])
if (!GetSpecialAbility(UNFEARABLE))
HasAllAttacks = false;
break;
case 'A':
if (!SpecAttacks[IMMUNE_MELEE])
if (!GetSpecialAbility(IMMUNE_MELEE))
HasAllAttacks = false;
break;
case 'B':
if (!SpecAttacks[IMMUNE_MAGIC])
if (!GetSpecialAbility(IMMUNE_MAGIC))
HasAllAttacks = false;
break;
case 'f':
if (!SpecAttacks[IMMUNE_FLEEING])
if (!GetSpecialAbility(IMMUNE_FLEEING))
HasAllAttacks = false;
break;
case 'O':
if (!SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE])
if (!GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE))
HasAllAttacks = false;
break;
case 'W':
if (!SpecAttacks[IMMUNE_MELEE_NONMAGICAL])
if (!GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL))
HasAllAttacks = false;
break;
case 'H':
if (!SpecAttacks[IMMUNE_AGGRO])
if (!GetSpecialAbility(IMMUNE_AGGRO))
HasAllAttacks = false;
break;
case 'G':
if (!SpecAttacks[IMMUNE_AGGRO_ON])
if (!GetSpecialAbility(IMMUNE_AGGRO_ON))
HasAllAttacks = false;
break;
case 'g':
if (!SpecAttacks[IMMUNE_CASTING_FROM_RANGE])
if (!GetSpecialAbility(IMMUNE_CASTING_FROM_RANGE))
HasAllAttacks = false;
break;
case 'd':
if (!SpecAttacks[IMMUNE_FEIGN_DEATH])
if (!GetSpecialAbility(IMMUNE_FEIGN_DEATH))
HasAllAttacks = false;
break;
case 'Y':
if (!SpecAttacks[SPECATK_RANGED_ATK])
if (!GetSpecialAbility(SPECATK_RANGED_ATK))
HasAllAttacks = false;
break;
case 'L':
if (!SpecAttacks[SPECATK_INNATE_DW])
if (!GetSpecialAbility(SPECATK_INNATE_DW))
HasAllAttacks = false;
break;
case 't':
if (!SpecAttacks[NPC_TUNNELVISION])
if (!GetSpecialAbility(NPC_TUNNELVISION))
HasAllAttacks = false;
break;
case 'n':
if (!SpecAttacks[NPC_NO_BUFFHEAL_FRIENDS])
if (!GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS))
HasAllAttacks = false;
break;
case 'p':
if(!SpecAttacks[IMMUNE_PACIFY])
if(!GetSpecialAbility(IMMUNE_PACIFY))
HasAllAttacks = false;
break;
case 'J':
if(!SpecAttacks[LEASH])
if(!GetSpecialAbility(LEASH))
HasAllAttacks = false;
break;
case 'j':
if(!SpecAttacks[TETHER])
if(!GetSpecialAbility(TETHER))
HasAllAttacks = false;
break;
case 'o':
if(!SpecAttacks[DESTRUCTIBLE_OBJECT])
if(!GetSpecialAbility(DESTRUCTIBLE_OBJECT))
{
HasAllAttacks = false;
SetDestructibleObject(false);
}
break;
case 'Z':
if(!SpecAttacks[NO_HARM_FROM_CLIENT]){
if(!GetSpecialAbility(NO_HARM_FROM_CLIENT)){
HasAllAttacks = false;
}
break;

View File

@ -164,7 +164,6 @@ public:
void RemoveCash();
void QueryLoot(Client* to);
uint32 CountLoot();
void DumpLoot(uint32 npcdump_index, ZSDump_NPC_Loot* npclootdump, uint32* NPCLootindex);
inline uint32 GetLoottableID() const { return loottable_id; }
inline uint32 GetCopper() const { return copper; }

View File

@ -459,41 +459,6 @@ uint32 Zone::CountSpawn2() {
return count;
}
uint32 Zone::DumpSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index, Spawn2* spawn2) {
if (spawn2 == 0)
return 0;
LinkedListIterator<Spawn2*> iterator(spawn2_list);
// uint32 index = 0;
iterator.Reset();
while(iterator.MoreElements())
{
if (iterator.GetData() == spawn2) {
spawn2dump[*spawn2index].spawn2_id = iterator.GetData()->spawn2_id;
spawn2dump[*spawn2index].time_left = iterator.GetData()->timer.GetRemainingTime();
iterator.RemoveCurrent();
return (*spawn2index)++;
}
iterator.Advance();
}
return 0xFFFFFFFF;
}
void Zone::DumpAllSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index) {
LinkedListIterator<Spawn2*> iterator(spawn2_list);
// uint32 index = 0;
iterator.Reset();
while(iterator.MoreElements())
{
spawn2dump[*spawn2index].spawn2_id = iterator.GetData()->spawn2_id;
spawn2dump[*spawn2index].time_left = iterator.GetData()->timer.GetRemainingTime();
(*spawn2index)++;
iterator.RemoveCurrent();
}
}
void Zone::Despawn(uint32 spawn2ID) {
LinkedListIterator<Spawn2*> iterator(spawn2_list);

View File

@ -979,7 +979,7 @@ void NPC::RangedAttack(Mob* other)
//if we have SPECATK_RANGED_ATK set then we range attack without weapon or ammo
const Item_Struct* weapon = nullptr;
const Item_Struct* ammo = nullptr;
if(!SpecAttacks[SPECATK_RANGED_ATK])
if(!GetSpecialAbility(SPECATK_RANGED_ATK))
{
//find our bow and ammo return if we can't find them...
return;
@ -1940,7 +1940,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) {
float level_difference = GetLevel() - who->GetLevel();
//Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level.
if ((RuleB(Combat,TauntOverLevel) == false) && (level_difference < 0) || who->SpecAttacks[IMMUNE_TAUNT]){
if ((RuleB(Combat,TauntOverLevel) == false) && (level_difference < 0) || who->GetSpecialAbility(IMMUNE_TAUNT)){
Message_StringID(MT_SpellFailure,FAILED_TAUNT);
return;
}
@ -2207,7 +2207,7 @@ bool Mob::CanDoSpecialAttack(Mob *other)
return false;
}
if(other->GetInvul() || other->SpecAttacks[IMMUNE_MELEE])
if(other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE))
return false;
return true;

View File

@ -646,7 +646,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if (max_level == 0)
max_level = RuleI(Spells, BaseImmunityLevel);
// NPCs get to ignore max_level for their spells.
if(SpecAttacks[UNSTUNABLE] ||
if(GetSpecialAbility(UNSTUNABLE) ||
((GetLevel() > max_level)
&& caster && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))))
{
@ -989,7 +989,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Cancel Magic: %d", effect_value);
#endif
if(SpecAttacks[UNDISPELLABLE]){
if(GetSpecialAbility(UNDISPELLABLE)){
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
break;
}
@ -1013,7 +1013,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Dispel Detrimental: %d", effect_value);
#endif
if(SpecAttacks[UNDISPELLABLE]){
if(GetSpecialAbility(UNDISPELLABLE)){
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
break;
}
@ -1037,7 +1037,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Dispel Beneficial: %d", effect_value);
#endif
if(SpecAttacks[UNDISPELLABLE]){
if(GetSpecialAbility(UNDISPELLABLE)){
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
break;
}
@ -1377,7 +1377,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
max_level = RuleI(Spells, BaseImmunityLevel); // Default max is 55 level limit
// NPCs ignore level limits in their spells
if(SpecAttacks[UNSTUNABLE] ||
if(GetSpecialAbility(UNSTUNABLE) ||
((GetLevel() > max_level)
&& caster && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))))
{

View File

@ -222,7 +222,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
for(int i = 0; i < EFFECT_COUNT; i++) {
// not important to check limit on SE_Lull as it doesnt have one and if the other components won't land, then SE_Lull wont either
if (spells[spell_id].effectid[i] == SE_ChangeFrenzyRad || spells[spell_id].effectid[i] == SE_Harmony) {
if((spells[spell_id].max[i] != 0 && GetTarget()->GetLevel() > spells[spell_id].max[i]) || GetTarget()->SpecAttacks[IMMUNE_PACIFY]) {
if((spells[spell_id].max[i] != 0 && GetTarget()->GetLevel() > spells[spell_id].max[i]) || GetTarget()->GetSpecialAbility(IMMUNE_PACIFY)) {
InterruptSpell(CANNOT_AFFECT_NPC, 0x121, spell_id);
return(false);
}
@ -3761,7 +3761,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(IsMezSpell(spell_id))
{
if(SpecAttacks[UNMEZABLE]) {
if(GetSpecialAbility(UNMEZABLE)) {
mlog(SPELLS__RESISTS, "We are immune to Mez spells.");
caster->Message_StringID(MT_Shout, CANNOT_MEZ);
int32 aggro = CheckAggroAmount(spell_id);
@ -3787,7 +3787,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
}
// slow and haste spells
if(SpecAttacks[UNSLOWABLE] && IsEffectInSpell(spell_id, SE_AttackSpeed))
if(GetSpecialAbility(UNSLOWABLE) && IsEffectInSpell(spell_id, SE_AttackSpeed))
{
mlog(SPELLS__RESISTS, "We are immune to Slow spells.");
caster->Message_StringID(MT_Shout, IMMUNE_ATKSPEED);
@ -3804,7 +3804,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(IsEffectInSpell(spell_id, SE_Fear))
{
effect_index = GetSpellEffectIndex(spell_id, SE_Fear);
if(SpecAttacks[UNFEARABLE]) {
if(GetSpecialAbility(UNFEARABLE)) {
mlog(SPELLS__RESISTS, "We are immune to Fear spells.");
caster->Message_StringID(MT_Shout, IMMUNE_FEAR);
int32 aggro = CheckAggroAmount(spell_id);
@ -3838,7 +3838,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(IsCharmSpell(spell_id))
{
if(SpecAttacks[UNCHARMABLE])
if(GetSpecialAbility(UNCHARMABLE))
{
mlog(SPELLS__RESISTS, "We are immune to Charm spells.");
caster->Message_StringID(MT_Shout, CANNOT_CHARM);
@ -3879,7 +3879,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
IsEffectInSpell(spell_id, SE_MovementSpeed)
)
{
if(SpecAttacks[UNSNAREABLE]) {
if(GetSpecialAbility(UNSNAREABLE)) {
mlog(SPELLS__RESISTS, "We are immune to Snare spells.");
caster->Message_StringID(MT_Shout, IMMUNE_MOVEMENT);
int32 aggro = CheckAggroAmount(spell_id);
@ -3939,7 +3939,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
return 0;
}
if(SpecAttacks[IMMUNE_CASTING_FROM_RANGE])
if(GetSpecialAbility(IMMUNE_CASTING_FROM_RANGE))
{
if(!caster->CombatRange(this))
{
@ -3947,7 +3947,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
}
}
if(SpecAttacks[IMMUNE_MAGIC])
if(GetSpecialAbility(IMMUNE_MAGIC))
{
mlog(SPELLS__RESISTS, "We are immune to magic, so we fully resist the spell %d", spell_id);
return(0);

View File

@ -330,7 +330,14 @@ void Trap::CreateHiddenTrigger()
make_npc->d_meele_texture2 = 0;
make_npc->trackable = 0;
make_npc->level = level;
strcpy(make_npc->npc_attacks, "ABHG");
make_npc->special_abilities = itoa(IMMUNE_MELEE);
make_npc->special_abilities += std::string(",1^");
make_npc->special_abilities += itoa(IMMUNE_MAGIC);
make_npc->special_abilities += std::string(",1^");
make_npc->special_abilities += itoa(IMMUNE_AGGRO);
make_npc->special_abilities += std::string(",1^");
make_npc->special_abilities += itoa(IMMUNE_AGGRO_ON);
make_npc->special_abilities += std::string(",1");
NPC* npca = new NPC(make_npc, 0, x, y, z, 0, FlyMode3);
npca->GiveNPCTypeData(make_npc);
entity_list.AddNPC(npca);

View File

@ -79,9 +79,6 @@ volatile bool ZoneLoaded = false;
extern QuestParserCollection* parse;
extern DBAsyncFinishedQueue MTdbafq;
extern DBAsync *dbasync;
void CleanupLoadZoneState(uint32 spawn2_count, ZSDump_Spawn2** spawn2_dump, ZSDump_NPC** npc_dump, ZSDump_NPC_Loot** npcloot_dump, NPCType** gmspawntype_dump, Spawn2*** spawn2_loaded, NPC*** npc_loaded, MYSQL_RES** result);
bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) {
_ZP(Zone_Bootup);
@ -805,14 +802,7 @@ void Zone::Shutdown(bool quite)
if (!quite)
LogFile->write(EQEMuLog::Normal, "Zone shutdown: going to sleep");
ZoneLoaded = false;
char pzs[3] = "";
if (database.GetVariable("PersistentZoneState", pzs, 2)) {
if (pzs[0] == '1') {
Sleep(100);
LogFile->write(EQEMuLog::Normal, "Saving zone state");
database.DumpZoneState();
}
}
zone->ResetAuth();
safe_delete(zone);
dbasync->CommitWrites();
@ -1662,301 +1652,9 @@ bool ZoneDatabase::LoadStaticZonePoints(LinkedList<ZonePoint*>* zone_point_list,
safe_delete_array(query);
return false;
}
return true;
}
bool ZoneDatabase::DumpZoneState() {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM zone_state_dump WHERE zonename='%s'", zone->GetShortName()), errbuf)) {
std::cerr << "Error in DumpZoneState query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return false;
}
safe_delete_array(query);
uint32 spawn2_count = zone->CountSpawn2();
uint32 npc_count = 0;
uint32 npcloot_count = 0;
uint32 gmspawntype_count = 0;
entity_list.CountNPC(&npc_count, &npcloot_count, &gmspawntype_count);
std::cout << "DEBUG: spawn2count=" << spawn2_count << ", npc_count=" << npc_count << ", npcloot_count=" << npcloot_count << ", gmspawntype_count=" << gmspawntype_count << std::endl;
ZSDump_Spawn2* spawn2_dump = 0;
ZSDump_NPC* npc_dump = 0;
ZSDump_NPC_Loot* npcloot_dump = 0;
NPCType* gmspawntype_dump = 0;
if (spawn2_count > 0) {
spawn2_dump = (ZSDump_Spawn2*) new uchar[spawn2_count * sizeof(ZSDump_Spawn2)];
memset(spawn2_dump, 0, sizeof(ZSDump_Spawn2) * spawn2_count);
}
if (npc_count > 0) {
npc_dump = (ZSDump_NPC*) new uchar[npc_count * sizeof(ZSDump_NPC)];
memset(npc_dump, 0, sizeof(ZSDump_NPC) * npc_count);
for (unsigned int i=0; i < npc_count; i++) {
npc_dump[i].spawn2_dump_index = 0xFFFFFFFF;
npc_dump[i].gmspawntype_index = 0xFFFFFFFF;
}
}
if (npcloot_count > 0) {
npcloot_dump = (ZSDump_NPC_Loot*) new uchar[npcloot_count * sizeof(ZSDump_NPC_Loot)];
memset(npcloot_dump, 0, sizeof(ZSDump_NPC_Loot) * npcloot_count);
for (unsigned int k=0; k < npcloot_count; k++)
npcloot_dump[k].npc_dump_index = 0xFFFFFFFF;
}
if (gmspawntype_count > 0) {
gmspawntype_dump = (NPCType*) new uchar[gmspawntype_count * sizeof(NPCType)];
memset(gmspawntype_dump, 0, sizeof(NPCType) * gmspawntype_count);
}
entity_list.DoZoneDump(spawn2_dump, npc_dump, npcloot_dump, gmspawntype_dump);
query = new char[512 + ((sizeof(ZSDump_Spawn2) * spawn2_count + sizeof(ZSDump_NPC) * npc_count + sizeof(ZSDump_NPC_Loot) * npcloot_count + sizeof(NPCType) * gmspawntype_count) * 2)];
char* end = query;
end += sprintf(end, "Insert Into zone_state_dump (zonename, spawn2_count, npc_count, npcloot_count, gmspawntype_count, spawn2, npcs, npc_loot, gmspawntype) values ('%s', %i, %i, %i, %i, ", zone->GetShortName(), spawn2_count, npc_count, npcloot_count, gmspawntype_count);
*end++ = '\'';
if (spawn2_dump != 0) {
end += DoEscapeString(end, (char*)spawn2_dump, sizeof(ZSDump_Spawn2) * spawn2_count);
safe_delete_array(spawn2_dump);
}
*end++ = '\'';
end += sprintf(end, ", ");
*end++ = '\'';
if (npc_dump != 0) {
end += DoEscapeString(end, (char*)npc_dump, sizeof(ZSDump_NPC) * npc_count);
safe_delete_array(npc_dump);
}
*end++ = '\'';
end += sprintf(end, ", ");
*end++ = '\'';
if (npcloot_dump != 0) {
end += DoEscapeString(end, (char*)npcloot_dump, sizeof(ZSDump_NPC_Loot) * npcloot_count);
safe_delete_array(npcloot_dump);
}
*end++ = '\'';
end += sprintf(end, ", ");
*end++ = '\'';
if (gmspawntype_dump != 0) {
end += DoEscapeString(end, (char*)gmspawntype_dump, sizeof(NPCType) * gmspawntype_count);
safe_delete_array(gmspawntype_dump);
}
*end++ = '\'';
end += sprintf(end, ")");
uint32 affected_rows = 0;
if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) {
// if (DoEscapeString(query, (unsigned int) (end - query))) {
safe_delete_array(query);
std::cerr << "Error in ZoneDump query " << errbuf << std::endl;
return false;
}
safe_delete_array(query);
if (affected_rows == 0) {
std::cerr << "Zone dump failed. (affected rows = 0)" << std::endl;
return false;
}
return true;
}
int8 ZoneDatabase::LoadZoneState(const char* zonename, LinkedList<Spawn2*>& spawn2_list) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
uint32 i;
unsigned long* lengths;
uint32 elapsedtime = 0;
uint32 spawn2_count = 0;
uint32 npc_count = 0;
uint32 npcloot_count = 0;
uint32 gmspawntype_count = 0;
ZSDump_Spawn2* spawn2_dump = 0;
ZSDump_NPC* npc_dump = 0;
ZSDump_NPC_Loot* npcloot_dump = 0;
NPCType* gmspawntype_dump = 0;
Spawn2** spawn2_loaded = 0;
NPC** npc_loaded = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spawn2_count, npc_count, npcloot_count, gmspawntype_count, spawn2, npcs, npc_loot, gmspawntype, (UNIX_TIMESTAMP()-UNIX_TIMESTAMP(time)) as elapsedtime FROM zone_state_dump WHERE zonename='%s'", zonename), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
std::cout << "Elapsed time: " << row[8] << std::endl;
elapsedtime = atoi(row[8]) * 1000;
lengths = mysql_fetch_lengths(result);
spawn2_count = atoi(row[0]);
std::cout << "Spawn2count: " << spawn2_count << std::endl;
if (lengths[4] != (sizeof(ZSDump_Spawn2) * spawn2_count)) {
std::cerr << "Error in LoadZoneState: spawn2_dump length mismatch l=" << lengths[4] << ", e=" << (sizeof(ZSDump_Spawn2) * spawn2_count) << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
else if (spawn2_count > 0) {
spawn2_dump = new ZSDump_Spawn2[spawn2_count];
spawn2_loaded = new Spawn2*[spawn2_count];
memcpy(spawn2_dump, row[4], lengths[4]);
for (i=0; i < spawn2_count; i++) {
if (spawn2_dump[i].time_left == 0xFFFFFFFF) // npc spawned, timer should be disabled
spawn2_loaded[i] = LoadSpawn2(spawn2_list, spawn2_dump[i].spawn2_id, 0xFFFFFFFF);
else if (spawn2_dump[i].time_left <= elapsedtime)
spawn2_loaded[i] = LoadSpawn2(spawn2_list, spawn2_dump[i].spawn2_id, 0);
else
spawn2_loaded[i] = LoadSpawn2(spawn2_list, spawn2_dump[i].spawn2_id, spawn2_dump[i].time_left - elapsedtime);
if (spawn2_loaded[i] == 0) {
std::cerr << "Error in LoadZoneState: spawn2_loaded[" << i << "] == 0" << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
}
}
gmspawntype_count = atoi(row[3]);
std::cout << "gmspawntype_count: " << gmspawntype_count << std::endl;
if (lengths[7] != (sizeof(NPCType) * gmspawntype_count)) {
std::cerr << "Error in LoadZoneState: gmspawntype_dump length mismatch l=" << lengths[7] << ", e=" << (sizeof(NPCType) * gmspawntype_count) << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
else if (gmspawntype_count > 0) {
gmspawntype_dump = new NPCType[gmspawntype_count];
memcpy(gmspawntype_dump, row[7], lengths[7]);
}
npc_count = atoi(row[1]);
std::cout << "npc_count: " << npc_count << std::endl;
if (lengths[5] != (sizeof(ZSDump_NPC) * npc_count)) {
std::cerr << "Error in LoadZoneState: npc_dump length mismatch l=" << lengths[5] << ", e=" << (sizeof(ZSDump_NPC) * npc_count) << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
else if (npc_count > 0) {
npc_dump = new ZSDump_NPC[npc_count];
npc_loaded = new NPC*[npc_count];
for (i=0; i < npc_count; i++) {
npc_loaded[i] = 0;
}
memcpy(npc_dump, row[5], lengths[5]);
for (i=0; i < npc_count; i++) {
if (npc_loaded[i] != 0) {
std::cerr << "Error in LoadZoneState: npc_loaded[" << i << "] != 0" << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
Spawn2* tmp = 0;
if (!npc_dump[i].corpse && npc_dump[i].spawn2_dump_index != 0xFFFFFFFF) {
if (spawn2_loaded == 0 || npc_dump[i].spawn2_dump_index >= spawn2_count) {
std::cerr << "Error in LoadZoneState: (spawn2_loaded == 0 || index >= count) && npc_dump[" << i << "].spawn2_dump_index != 0xFFFFFFFF" << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
tmp = spawn2_loaded[npc_dump[i].spawn2_dump_index];
spawn2_loaded[npc_dump[i].spawn2_dump_index] = 0;
}
if (npc_dump[i].npctype_id == 0) {
if (npc_dump[i].gmspawntype_index == 0xFFFFFFFF) {
std::cerr << "Error in LoadZoneState: gmspawntype index invalid" << std::endl;
safe_delete(tmp);
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
else {
if (gmspawntype_dump == 0 || npc_dump[i].gmspawntype_index >= gmspawntype_count) {
std::cerr << "Error in LoadZoneState: (gmspawntype_dump == 0 || index >= count) && npc_dump[" << i << "].npctype_id == 0" << std::endl;
safe_delete(tmp);
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
npc_loaded[i] = new NPC(&gmspawntype_dump[npc_dump[i].gmspawntype_index], tmp, npc_dump[i].x, npc_dump[i].y, npc_dump[i].z, npc_dump[i].heading, FlyMode3, npc_dump[i].corpse);
}
}
else {
const NPCType* crap = database.GetNPCType(npc_dump[i].npctype_id);
if (crap != 0)
npc_loaded[i] = new NPC(crap, tmp, npc_dump[i].x, npc_dump[i].y, npc_dump[i].z, npc_dump[i].heading, FlyMode3, npc_dump[i].corpse);
else {
std::cerr << "Error in LoadZoneState: Unknown npctype_id: " << npc_dump[i].npctype_id << std::endl;
safe_delete(tmp);
}
}
if (npc_loaded[i] != 0) {
npc_loaded[i]->AddCash(npc_dump[i].copper, npc_dump[i].silver, npc_dump[i].gold, npc_dump[i].platinum);
// if (npc_dump[i].corpse) {
// if (npc_dump[i].decay_time_left <= elapsedtime)
// npc_loaded[i]->SetDecayTimer(0);
// else
// npc_loaded[i]->SetDecayTimer(npc_dump[i].decay_time_left - elapsedtime);
// }
entity_list.AddNPC(npc_loaded[i]);
}
}
}
npcloot_count = atoi(row[2]);
std::cout << "npcloot_count: " << npcloot_count << std::endl;
if (lengths[6] != (sizeof(ZSDump_NPC_Loot) * npcloot_count)) {
std::cerr << "Error in LoadZoneState: npcloot_dump length mismatch l=" << lengths[6] << ", e=" << (sizeof(ZSDump_NPC_Loot) * npcloot_count) << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
else if (npcloot_count > 0) {
if (npc_loaded == 0) {
std::cerr << "Error in LoadZoneState: npcloot_count > 0 && npc_loaded == 0" << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
npcloot_dump = new ZSDump_NPC_Loot[npcloot_count];
memcpy(npcloot_dump, row[6], lengths[6]);
for (i=0; i < npcloot_count; i++) {
if (npcloot_dump[i].npc_dump_index >= npc_count) {
std::cerr << "Error in LoadZoneState: npcloot_dump[" << i << "].npc_dump_index >= npc_count" << std::endl;
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return -1;
}
if (npc_loaded[npcloot_dump[i].npc_dump_index] != 0) {
npc_loaded[npcloot_dump[i].npc_dump_index]->AddItem(npcloot_dump[i].itemid, npcloot_dump[i].charges, npcloot_dump[i].equipSlot);
}
}
}
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
}
else {
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
return 0;
}
CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result);
}
else {
std::cerr << "Error in LoadZoneState query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return -1;
}
return 1;
}
void CleanupLoadZoneState(uint32 spawn2_count, ZSDump_Spawn2** spawn2_dump, ZSDump_NPC** npc_dump, ZSDump_NPC_Loot** npcloot_dump, NPCType** gmspawntype_dump, Spawn2*** spawn2_loaded, NPC*** npc_loaded, MYSQL_RES** result) {
safe_delete(*spawn2_dump);
safe_delete(*spawn2_loaded);
safe_delete(*gmspawntype_dump);
safe_delete(*npc_dump);
safe_delete(*npc_loaded);
safe_delete(*npcloot_dump);
if (*result) {
mysql_free_result(*result);
*result = 0;
}
}
void Zone::SpawnStatus(Mob* client) {
LinkedListIterator<Spawn2*> iterator(spawn2_list);

View File

@ -139,8 +139,6 @@ public:
bool RemoveSpawnGroup(uint32 in_id);
bool Process();
void DumpAllSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index);
uint32 DumpSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index, Spawn2* spawn2);
void Despawn(uint32 spawngroupID);
bool Depop(bool StartSpawnTimer = false);

View File

@ -1035,7 +1035,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
"npc_types.mindmg,"
"npc_types.maxdmg,"
"npc_types.attack_count,"
"npc_types.npcspecialattks,"
"npc_types.special_abilities,"
"npc_types.npc_spells_id,"
"npc_types.d_meele_texture1,"
"npc_types.d_meele_texture2,"
@ -1130,7 +1130,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->min_dmg = atoi(row[r++]);
tmpNPCType->max_dmg = atoi(row[r++]);
tmpNPCType->attack_count = atoi(row[r++]);
strcpy(tmpNPCType->npc_attacks,row[r++]);
tmpNPCType->special_abilities = row[r++];
tmpNPCType->npc_spells_id = atoi(row[r++]);
tmpNPCType->d_meele_texture1 = atoi(row[r++]);
tmpNPCType->d_meele_texture2 = atoi(row[r++]);
@ -1345,7 +1345,7 @@ const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 client
"vwMercNpcTypes.mindmg,"
"vwMercNpcTypes.maxdmg,"
"vwMercNpcTypes.attack_count,"
"vwMercNpcTypes.npcspecialattks,"
"vwMercNpcTypes.special_abilities,"
// "vwMercNpcTypes.npc_spells_id,"
"vwMercNpcTypes.d_meele_texture1,"
"vwMercNpcTypes.d_meele_texture2,"
@ -1440,7 +1440,7 @@ const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 client
tmpNPCType->min_dmg = atoi(row[r++]);
tmpNPCType->max_dmg = atoi(row[r++]);
tmpNPCType->attack_count = atoi(row[r++]);
strcpy(tmpNPCType->npc_attacks,row[r++]);
tmpNPCType->special_abilities = row[r++];
//tmpNPCType->npc_spells_id = atoi(row[r++]);
tmpNPCType->d_meele_texture1 = atoi(row[r++]);
tmpNPCType->d_meele_texture2 = atoi(row[r++]);

View File

@ -297,8 +297,6 @@ public:
*/
bool GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, int &ruleset, char **map_filename);
bool SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd);
bool DumpZoneState();
int8 LoadZoneState(const char* zonename, LinkedList<Spawn2*>& spawn2_list);
bool LoadStaticZonePoints(LinkedList<ZonePoint*>* zone_point_list,const char* zonename, uint32 version);
bool UpdateZoneSafeCoords(const char* zonename, float x, float y, float z);
uint8 GetUseCFGSafeCoords();

View File

@ -89,7 +89,7 @@ struct NPCType
uint32 min_dmg;
uint32 max_dmg;
int16 attack_count;
char npc_attacks[30];
std::string special_abilities;
uint16 d_meele_texture1;
uint16 d_meele_texture2;
uint8 prim_melee_type;
@ -120,38 +120,6 @@ struct NPCType
float healscale;
};
struct ZSDump_Spawn2 {
uint32 spawn2_id;
uint32 time_left;
};
struct ZSDump_NPC {
uint32 spawn2_dump_index;
uint32 gmspawntype_index;
uint32 npctype_id;
int32 cur_hp;
uint8 corpse; // 0=no, 1=yes, 2=yes and locked
uint32 decay_time_left;
// needatype buffs; // decided not to save these, would be hard because if expired them on bootup, wouldnt take into account the npcai refreshing them, etc
float x;
float y;
float z;
float heading;
uint32 copper;
uint32 silver;
uint32 gold;
uint32 platinum;
};
struct ZSDump_NPC_Loot {
uint32 npc_dump_index;
uint16 itemid;
int8 charges;
int16 equipSlot;
uint8 minlevel;
uint8 maxlevel;
};
/*
Below are the blob structures for saving player corpses to the database
-Quagmire