- Fix #528 - Support for DoF Advancement Window!

* Restrictions based on rule R_Player, TraitTieringSelection = 1, limits to 1 trait selection per group, 0 allows 'free' selection much like newer EQ2
	* Racial Traditions and Personal Traits available, Training and Enemy Tactics need content
- Fix #472 - Spell damage now has resist added to reduce damage.  Resistability support, info struct now has float "max_spell_reduction" and int8 "max_spell_reduction_override".  Use override to set manually the resistability.  Otherwise uses table for max resistability based on level
- Fix #456 - Parital support for NPC Knockback, challenge is the updates are not using delta properly, but they do fly!
- Fixed /follow for DoF client
- Fixed multiple crashes in adding a trait
- Spell book should no longer have type 4 spells (not shown spells)
- Spell book issue addressed where innate abilities would wipe out the spell book
- Fixed situations on login where HP/Power would not regen since you were not added to the damaged spawns
- Implemented a workaround to get all items to appear in inventory for DoF client upon zoning *does not resolve evac/escape
- Fixed Spawn info struct, we were not passing spell_effects properly for DoF client
**Be sure to check the db file may2024_classictraits_personalandracial.sql and note it has a static max item id 10204351 that may need to be changed in the future if you insert it later
This commit is contained in:
Emagi 2024-05-11 20:29:26 -04:00
parent 8639e5a7ed
commit 69612e08b6
20 changed files with 1153 additions and 108 deletions

View file

@ -0,0 +1,237 @@
alter table spell_traits add column item_id int(10) unsigned not null default 0;
alter table spell_traits add column is_active tinyint(1) unsigned not null default 1;
alter table spell_traits add column insert_id int(10) not null default 0;
update spell_traits set is_active=0;
insert into spell_traits set level=8,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=0,isFocusEffect=1,spell_id=1000196;
insert into spell_traits set level=8,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=0,isFocusEffect=1,spell_id=1002091;
insert into spell_traits set level=8,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=0,isFocusEffect=1,spell_id=1000520;
insert into spell_traits set level=8,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=0,isFocusEffect=1,spell_id=1001879;
insert into spell_traits set level=8,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=0,isFocusEffect=1,spell_id=1000288;
insert into spell_traits set level=14,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=1,isFocusEffect=1,spell_id=1001768;
insert into spell_traits set level=14,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=1,isFocusEffect=1,spell_id=1000300;
insert into spell_traits set level=14,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=1,isFocusEffect=1,spell_id=1002936;
insert into spell_traits set level=14,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=1,isFocusEffect=1,spell_id=1001841;
insert into spell_traits set level=14,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=1,isFocusEffect=1,spell_id=1001890;
insert into spell_traits set level=22,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=2,isFocusEffect=1,spell_id=1002730;
insert into spell_traits set level=22,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=2,isFocusEffect=1,spell_id=1001678;
insert into spell_traits set level=22,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=2,isFocusEffect=1,spell_id=1001794;
insert into spell_traits set level=22,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=2,isFocusEffect=1,spell_id=1000240;
insert into spell_traits set level=28,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=3,isFocusEffect=1,spell_id=1002067;
insert into spell_traits set level=28,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=3,isFocusEffect=1,spell_id=1002656;
insert into spell_traits set level=28,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=3,isFocusEffect=1,spell_id=1000585;
insert into spell_traits set level=28,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=3,isFocusEffect=1,spell_id=1001268;
insert into spell_traits set level=28,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=3,isFocusEffect=1,spell_id=1002458;
insert into spell_traits set level=36,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=4,isFocusEffect=1,spell_id=1002383;
insert into spell_traits set level=36,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=4,isFocusEffect=1,spell_id=1001412;
insert into spell_traits set level=36,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=4,isFocusEffect=1,spell_id=1001706;
insert into spell_traits set level=36,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=4,isFocusEffect=1,spell_id=1002278;
insert into spell_traits set level=36,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=4,isFocusEffect=1,spell_id=1002962;
insert into spell_traits set level=42,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=5,isFocusEffect=1,spell_id=1002689;
insert into spell_traits set level=42,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=5,isFocusEffect=1,spell_id=1000254;
insert into spell_traits set level=42,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=5,isFocusEffect=1,spell_id=1002660;
insert into spell_traits set level=42,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=5,isFocusEffect=1,spell_id=1001962;
insert into spell_traits set level=46,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=6,isFocusEffect=1,spell_id=1002807;
insert into spell_traits set level=46,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=6,isFocusEffect=1,spell_id=1001422;
insert into spell_traits set level=46,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=6,isFocusEffect=1,spell_id=1002379;
insert into spell_traits set level=46,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=6,isFocusEffect=1,spell_id=1002363;
insert into spell_traits set level=46,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=6,isFocusEffect=1,spell_id=1000454;
insert into spell_traits set level=48,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=7,isFocusEffect=1,spell_id=1002221;
insert into spell_traits set level=48,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=7,isFocusEffect=1,spell_id=1000418;
insert into spell_traits set level=48,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=7,isFocusEffect=1,spell_id=1002384;
insert into spell_traits set level=48,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=7,isFocusEffect=1,spell_id=1002923;
insert into spell_traits set level=48,class_req=255,race_req=255,isTrait=1,isInate=1,tier=1,`group`=7,isFocusEffect=1,spell_id=1000203;
#barbarian racials
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550454;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550455;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550456;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550457;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550458;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550459;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550460;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1000202;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001806;
insert into spell_traits set level=0,class_req=255,race_req=0,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550461;
#dark elf racials
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550109;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550118;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550464;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550114;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550117;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550115;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550113;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550111;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550112;
insert into spell_traits set level=0,class_req=255,race_req=1,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550116;
#dwarf racials
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550281;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550475;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550280;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550287;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550283;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550282;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550286;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550284;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550476;
insert into spell_traits set level=0,class_req=255,race_req=2,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550285;
#erudite racials
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550311;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550295;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550301;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550299;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550309;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550298;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550296;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550294;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550300;
insert into spell_traits set level=0,class_req=255,race_req=3,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550293;
#froglok is race_req 4 we got nothing!
#gnome racials
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1000031;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001418;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550496;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001697;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1000268;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550497;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550498;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550499;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550500;
insert into spell_traits set level=0,class_req=255,race_req=5,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550501;
#half elf racials
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550109;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=1002991;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=1002992;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550278;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002779;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1000120;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001731;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550463;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550464;
insert into spell_traits set level=0,class_req=255,race_req=6,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550465;
#halfling racials
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550467;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550468;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002088;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550469;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550470;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550471;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002137;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550472;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550473;
insert into spell_traits set level=0,class_req=255,race_req=7,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001907;
#high elf racials
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550109;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001734;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550248;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550262;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550246;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002779;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550261;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550251;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001730;
insert into spell_traits set level=0,class_req=255,race_req=8,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550253;
#human racials
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550483;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550484;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550485;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550486;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1000293;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=1002991;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=80025;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550487;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550463;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550213;
insert into spell_traits set level=0,class_req=255,race_req=9,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550488;
#iksar racials
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550502;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550489;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=1002280;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002950;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1000378;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550503;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550504;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001942;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550505;
insert into spell_traits set level=0,class_req=255,race_req=10,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550506;
#kerra racials
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550129;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550139;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550466;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550133;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550128;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001733;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002797;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002797;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550127;
insert into spell_traits set level=0,class_req=255,race_req=11,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550143;
#ogre racials
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550474;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550079;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550086;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550084;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550082;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550078;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550088;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550081;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550087;
insert into spell_traits set level=0,class_req=255,race_req=12,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550085;
#ratonga racials
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002754;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550477;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550478;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550479;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002201;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001884;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550480;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550336;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550481;
insert into spell_traits set level=0,class_req=255,race_req=13,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550482;
#troll racials
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550489;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550490;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002835;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002010;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550491;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550492;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550493;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001985;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550494;
insert into spell_traits set level=0,class_req=255,race_req=14,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550495;
#wood elf racials
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550109;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550277;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550462;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1001731;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550273;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550507;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550508;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=2550275;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=0,tier=1,`group`=0,spell_id=2550274;
insert into spell_traits set level=0,class_req=255,race_req=15,isTrait=0,isInate=1,tier=1,`group`=0,spell_id=1002926;
update spell_traits set insert_id = id;
insert into items ( skill_id_req, id, bPvpDesc, name, item_type, icon, developer_notes, count, tier, description, show_name ) select distinct(st.spell_id), st.insert_id+10204351, 1, s.name, 'Scroll', 75, 'test trait', 0, 1, trim(replace(s.description, '\n', '')), 1 from spell_traits as st inner join spells as s on s.id = st.spell_id;
update spell_traits set item_id = insert_id + 10204351;

View file

@ -762,6 +762,7 @@ int8 Entity::DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHi
if(lua_spell->spell->GetSpellData()->resistibility > 0)
bonus -= (1.0f - lua_spell->spell->GetSpellData()->resistibility)*100.0f;
LogWrite(COMBAT__DEBUG, 9, "Combat", "SpellResist: resistibility %f, bonus %f", lua_spell->spell->GetSpellData()->resistibility, bonus);
// Here we take into account Subjugation, Disruption and Ordination (debuffs)
if(lua_spell->spell->GetSpellData()->mastery_skill) {
int32 master_skill_reduce = rule_manager.GetGlobalRule(R_Spells, MasterSkillReduceSpellResist)->GetInt32();
@ -781,6 +782,8 @@ int8 Entity::DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHi
item_stat_bonus = GetStat(item_stat);
}
bonus += (master_skill->current_val + item_stat_bonus) / master_skill_reduce;
LogWrite(COMBAT__DEBUG, 9, "Combat", "SpellResistMasterySkill: skill %u, stat bonus %f, mastery skill reduce %u, bonus %f", master_skill->current_val, item_stat_bonus, master_skill_reduce, bonus);
}
}
@ -788,13 +791,17 @@ int8 Entity::DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHi
if (victim->IsEntity())
bonus -= ((Entity*)victim)->GetDamageTypeResistPercentage(damage_type);
LogWrite(COMBAT__DEBUG, 9, "Combat", "DamageResistPercent: type %u, bonus %f", damage_type, bonus);
Entity* entity_victim = (Entity*)victim;
float chance = 80 + bonus; //80% base chance that the victim will get hit (plus bonus)
sint16 roll_chance = 100;
if(skill)
roll_chance -= skill->current_val / 25;
roll_chance -= skill->current_val / 10;
LogWrite(COMBAT__DEBUG, 9, "Combat", "Chance: fchance %f, roll_chance %i", chance, roll_chance);
if(!is_caster_spell){ // melee or range attack
skill = GetSkillByName("Offense", true); //add this skill for NPCs
@ -875,7 +882,9 @@ int8 Entity::DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHi
else{
skill = entity_victim->GetSkillByName("Spell Avoidance", true);
if(skill)
chance -= skill->current_val / 25;
chance -= skill->current_val / 10;
LogWrite(COMBAT__DEBUG, 9, "Combat", "SpellAvoidChance: fchance %f", chance);
if(rand()%roll_chance >= chance) {
return DAMAGE_PACKET_RESULT_RESIST; //successfully resisted
}
@ -1821,8 +1830,21 @@ sint32 Entity::CalculateDamageAmount(Spawn* target, sint32 damage, int8 base_typ
damage = CalculateFormulaByStat(damage, ITEM_STAT_SPELL_DAMAGE);
}
if(base_type == DAMAGE_PACKET_TYPE_SPELL_DAMAGE)
if(base_type == DAMAGE_PACKET_TYPE_SPELL_DAMAGE) {
damage = CalculateFormulaByStat(damage, ITEM_STAT_SPELL_AND_COMBAT_ART_DAMAGE);
if(target && target->IsEntity() && damage > 0) {
int16 effective_level_attacker = GetInfoStruct()->get_effective_level() != 0 ? GetInfoStruct()->get_effective_level() : GetLevel();
float resistBase = ((Entity*)target)->GetDamageTypeResistPercentage(damage_type);
if(resistBase > 1.0f) {
float dmgReduction = ((Entity*)target)->CalculateSpellDamageReduction((float)damage, resistBase - 1.0f, effective_level_attacker);
float newDamage = static_cast<float>(damage) - dmgReduction;
if(newDamage < 0.0f)
newDamage = 0.0f;
damage = static_cast<sint32>(std::floor(newDamage));
}
}
}
// combat abilities only bonus
if(damage_type <= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE)
@ -1868,4 +1890,14 @@ int32 Entity::CalculateFormulaByStat(int32 value, int16 stat) {
int32 Entity::CalculateFormulaBonus(int32 value, float percent_bonus) {
return (int32)((float)value * ((percent_bonus / 100.0f) + 1.0f));
}
float Entity::CalculateSpellDamageReduction(float spellDamage, float resistancePercentage, int16 attackerLevel) {
int16 effective_level = GetInfoStruct()->get_effective_level() != 0 ? GetInfoStruct()->get_effective_level() : GetLevel();
float levelDifference = std::abs(effective_level - attackerLevel);
float logFactor = 1.0f / (1.0f + 0.1f * levelDifference); // Adjust the factor for smoother reduction
// Calculate the actual damage reduction based on resistance percentage, logarithmic factor, and maximum reduction
float reducedDamage = spellDamage * (1.0f - resistancePercentage) * logFactor * GetInfoStruct()->get_max_spell_reduction();
return reducedDamage;
}

View file

@ -5743,27 +5743,57 @@ void Commands::Command_AcceptAdvancement(Client* client, Seperator* sep)
{
Player *player = client->GetPlayer();
if (sep && sep->IsSet(0)) {
TraitData* trait = master_trait_list.GetTrait(atoul(sep->arg[0]));
int32 trait_id = atoul(sep->arg[0]);
TraitData* trait = nullptr;
if(client->GetVersion() <= 547) {
trait = master_trait_list.GetTraitByItemID(trait_id);
}
else {
trait = master_trait_list.GetTrait(trait_id);
}
if(!trait) {
LogWrite(COMMAND__ERROR, 0, "Command", "Invalid accept advancement of trait %u, no trait found.", trait_id);
return; // not valid lets not crash!
}
if(!master_trait_list.IsPlayerAllowedTrait(client, trait)) {
client->SimpleMessage(CHANNEL_COLOR_RED, "Not enough trait points to accept trait.");
return;
}
// Check to see if this is a trait or grandmaster training (traits are always new spells, training is always upgrades)
if (!player->HasSpell(trait->spellID, 0, true))
{
Spell* spell = master_spell_list.GetSpell(trait->spellID, trait->tier);
player->AddSpellBookEntry(trait->spellID, trait->tier, player->GetFreeSpellBookSlot(spell->GetSpellData()->spell_book_type), spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true);
if(spell) {
player->AddSpellBookEntry(trait->spellID, trait->tier, player->GetFreeSpellBookSlot(spell->GetSpellData()->spell_book_type), spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true);
}
else {
client->Message(CHANNEL_COLOR_RED, "ERROR! Trait is not a valid spell id %u may be disabled.", trait->spellID);
}
}
else
{
Spell* spell = master_spell_list.GetSpell(trait->spellID, trait->tier);
int8 old_slot = player->GetSpellSlot(spell->GetSpellID());
player->RemoveSpellBookEntry(spell->GetSpellID());
player->AddSpellBookEntry(spell->GetSpellID(), spell->GetSpellTier(), old_slot, spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true);
player->UnlockSpell(spell);
client->SendSpellUpdate(spell);
if(spell) {
int8 old_slot = player->GetSpellSlot(spell->GetSpellID());
player->RemoveSpellBookEntry(spell->GetSpellID());
player->AddSpellBookEntry(spell->GetSpellID(), spell->GetSpellTier(), old_slot, spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true);
player->UnlockSpell(spell);
client->SendSpellUpdate(spell);
}
else {
client->Message(CHANNEL_COLOR_RED, "ERROR! Trait is not a valid spell id %u may be disabled.", trait->spellID);
}
}
// Spell book update
client->QueuePacket(player->GetSpellBookUpdatePacket(client->GetVersion()));
client->QueuePacket(master_trait_list.GetTraitListPacket(client));
if(client->GetVersion() <= 547) {
master_trait_list.ChooseNextTrait(client);
}
}
}
@ -10705,6 +10735,19 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
client->GetCurrentZone()->SendHealPacket(client->GetPlayer(), client->GetPlayer()->GetTarget() ? client->GetPlayer()->GetTarget() : client->GetPlayer(),
atoul(sep->arg[1]), atoul(sep->arg[2]), "TestSpell");
}
else if(atoi(sep->arg[0]) == 34 && sep->IsNumber(1) && sep->IsNumber(2)) {
PacketStruct* packet = configReader.getStruct("WS_QuestRewardPackMsg", client->GetVersion());
packet->setSubstructDataByName("reward_data", "unknown1", atoi(sep->arg[1]));
Item* item = master_item_list.GetItem(atoul(sep->arg[2]));
if(item) {
packet->setSubstructArrayLengthByName("reward_data", "num_select_rewards", 1);
packet->setArrayDataByName("select_reward_id", item->details.item_id, 0);
packet->setItemArrayDataByName("select_item", item, client->GetPlayer(), 0, 0, -1);
}
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
}
else {
PacketStruct* packet2 = configReader.getStruct("WS_ExamineSpellInfo", client->GetVersion());

View file

@ -371,6 +371,9 @@ void Entity::MapInfoStruct()
get_string_funcs["action_state"] = l::bind(&InfoStruct::get_action_state, &info_struct);
get_string_funcs["combat_action_state"] = l::bind(&InfoStruct::get_combat_action_state, &info_struct);
get_float_funcs["max_spell_reduction"] = l::bind(&InfoStruct::get_max_spell_reduction, &info_struct);
get_int8_funcs["max_spell_reduction_override"] = l::bind(&InfoStruct::get_max_spell_reduction_override, &info_struct);
/** SETS **/
set_string_funcs["name"] = l::bind(&InfoStruct::set_name, &info_struct, l::_1);
@ -575,6 +578,8 @@ void Entity::MapInfoStruct()
set_string_funcs["action_state"] = l::bind(&InfoStruct::set_action_state, &info_struct, l::_1);
set_string_funcs["combat_action_state"] = l::bind(&InfoStruct::set_combat_action_state, &info_struct, l::_1);
set_float_funcs["max_spell_reduction"] = l::bind(&InfoStruct::set_max_spell_reduction, &info_struct, l::_1);
set_int8_funcs["max_spell_reduction_override"] = l::bind(&InfoStruct::set_max_spell_reduction_override, &info_struct, l::_1);
}
bool Entity::HasMoved(bool include_heading){
@ -1404,6 +1409,8 @@ void Entity::CalculateBonuses(){
int16 effective_level = info->get_effective_level() != 0 ? info->get_effective_level() : GetLevel();
CalculateMaxReduction();
info->set_block(info->get_block_base());
info->set_cur_attack(info->get_attack_base());
@ -3947,4 +3954,28 @@ void Entity::TerminateTrade() {
tmpTradePtr->CancelTrade(this);
safe_delete(tmpTradePtr);
}
}
void Entity::CalculateMaxReduction() {
if(GetInfoStruct()->get_max_spell_reduction_override()) {
return; // override enabled, don't touch the max reduction
}
int16 effective_level = GetInfoStruct()->get_effective_level() != 0 ? GetInfoStruct()->get_effective_level() : GetLevel();
// Define thresholds and corresponding maximum reduction factors
const int thresholds[] = {1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120}; // Thresholds for level differences
const float maxReductions[] = {0.2f, 0.2f, 0.15f, 0.15f, 0.1f,0.1f,0.1f,0.1f,0.1f,0.075f,0.075f,0.075f,0.05f,0.05f,0.05f,0.05f,0.05f,0.05f,0.045f,0.045f,0.045f,0.045f,0.045f,0.045f,0.045f}; // Maximum reduction factors for each threshold
int numThresholds = sizeof(thresholds) / sizeof(thresholds[0]);
// Find the appropriate maximum reduction factor based on level difference
float maxReduction = .1f; // Default maximum reduction factor
for (int i = 0; i < numThresholds; ++i) {
if (effective_level >= thresholds[i]) {
maxReduction = maxReductions[i];
}
else {
break; // No need to check further
}
}
GetInfoStruct()->set_max_spell_reduction(maxReduction);
}

View file

@ -291,6 +291,10 @@ struct InfoStruct{
assist_auto_attack_ = 0;
action_state_ = std::string("");
combat_action_state_ = std::string("");
max_spell_reduction_ = .1f;
max_spell_reduction_override_ = 0;
}
@ -483,7 +487,19 @@ struct InfoStruct{
action_state_ = oldStruct->get_action_state();
combat_action_state_ = oldStruct->get_combat_action_state();
group_loot_method_ = oldStruct->get_group_loot_method();
group_loot_items_rarity_ = oldStruct->get_group_loot_items_rarity();
group_auto_split_ = oldStruct->get_group_auto_split();
group_default_yell_ = oldStruct->get_group_default_yell();
group_autolock_ = oldStruct->get_group_autolock();
group_lock_method_ = oldStruct->get_group_lock_method();
group_solo_autolock_ = oldStruct->get_group_solo_autolock();
group_auto_loot_method_ = oldStruct->get_group_auto_loot_method();
assist_auto_attack_ = oldStruct->get_assist_auto_attack();
max_spell_reduction_ = oldStruct->get_max_spell_reduction();
max_spell_reduction_override_ = oldStruct->get_max_spell_reduction_override();
}
//mutable std::shared_mutex mutex_;
std::string get_name() { std::lock_guard<std::mutex> lk(classMutex); return name_; }
@ -706,6 +722,9 @@ struct InfoStruct{
std::string get_combat_action_state() { std::lock_guard<std::mutex> lk(classMutex); return combat_action_state_; }
float get_max_spell_reduction() { std::lock_guard<std::mutex> lk(classMutex); return max_spell_reduction_; }
int8 get_max_spell_reduction_override() { std::lock_guard<std::mutex> lk(classMutex); return max_spell_reduction_override_; }
void set_name(std::string value) { std::lock_guard<std::mutex> lk(classMutex); name_ = value; }
void set_deity(std::string value) { std::lock_guard<std::mutex> lk(classMutex); deity_ = value; }
@ -1014,6 +1033,10 @@ struct InfoStruct{
void set_combat_action_state(std::string value) { std::lock_guard<std::mutex> lk(classMutex); combat_action_state_ = value; }
void set_max_spell_reduction(float value) { std::lock_guard<std::mutex> lk(classMutex); max_spell_reduction_ = value; }
void set_max_spell_reduction_override(int8 value) { std::lock_guard<std::mutex> lk(classMutex); max_spell_reduction_override_ = value; }
void ResetEffects(Spawn* spawn)
{
for(int i=0;i<45;i++){
@ -1237,6 +1260,9 @@ private:
std::string action_state_;
std::string combat_action_state_;
float max_spell_reduction_;
int8 max_spell_reduction_override_;
// when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock
std::mutex classMutex;
};
@ -1927,6 +1953,7 @@ public:
bool SetInfoStructSInt(std::string field, sint64 value);
bool SetInfoStructFloat(std::string field, float value);
float CalculateSpellDamageReduction(float spellDamage, int16 competitorLevel);
sint32 CalculateHateAmount(Spawn* target, sint32 amt);
sint32 CalculateHealAmount(Spawn* target, sint32 amt, int8 crit_mod, bool* crit, bool skip_crit_mod = false);
sint32 CalculateDamageAmount(Spawn* target, sint32 damage, int8 base_type, int8 damage_type, LuaSpell* spell);
@ -1934,6 +1961,7 @@ public:
sint32 CalculateFormulaByStat(sint32 value, int16 stat);
int32 CalculateFormulaByStat(int32 value, int16 stat);
int32 CalculateFormulaBonus(int32 value, float percent_bonus);
float CalculateSpellDamageReduction(float spellDamage, float resistancePercentage, int16 attackerLevel);
float GetStat(int32 item_stat) {
float item_chance_or_skill = 0.0f;
@ -2013,6 +2041,8 @@ public:
}
void TerminateTrade();
void CalculateMaxReduction();
// when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock
std::mutex MEquipment;
std::mutex MStats;

View file

@ -3528,6 +3528,10 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8
}
EQ2Packet* PlayerItemList::serialize(Player* player, int16 version){
bool firstRun = false;
if(!packet_count) {
firstRun = true;
}
EQ2Packet* app = 0;
PacketStruct* packet = configReader.getStruct("WS_UpdateInventory",version);
Item* item = 0;
@ -3535,8 +3539,12 @@ EQ2Packet* PlayerItemList::serialize(Player* player, int16 version){
if(packet && indexed_items.size() > 0){
int8 packet_size = 0;
int16 size = indexed_items.size();
if (overflowItems.size() > 0)
if (!firstRun && overflowItems.size() > 0)
size++;
if(size > 20 && firstRun) {
size = 20;
}
PacketStruct* packet2 = configReader.getStruct("Substruct_Item", version);
packet_size = packet2->GetTotalPacketSize();
safe_delete(packet2);
@ -3567,11 +3575,17 @@ EQ2Packet* PlayerItemList::serialize(Player* player, int16 version){
if(item && item->details.new_item)
new_index++;
if(firstRun && i > 19) {
item->details.new_item = true;
continue;
}
if (item && item->details.item_id > 0)
AddItemToPacket(packet, player, item, i, false, new_index);
}
if (overflowItems.size() > 0) {
if (!firstRun && overflowItems.size() > 0) {
// We have overflow items, lets get the first one
item = overflowItems.at(0);
// Lets make sure the item is valid

View file

@ -750,6 +750,25 @@ struct ItemAppearance{
int8 highlight_green;
int8 highlight_blue;
};
enum AddItemType {
NOT_SET = 0,
BUY_FROM_BROKER = 1,
GM_COMMAND = 2
};
struct QuestRewardData {
int32 quest_id;
bool is_temporary;
std::string description;
bool is_collection;
bool has_displayed;
int64 tmp_coin;
int32 tmp_status;
bool db_saved;
int32 db_index;
};
class PlayerItemList;
class Item{
public:

View file

@ -8611,21 +8611,26 @@ int EQ2Emu_lua_Knockback(lua_State* state) {
return 0;
}
if (spawn->IsPlayer() && (vertical != 0 || horizontal != 0)) {
Client* client = ((Player*)spawn)->GetClient();
PacketStruct* packet = configReader.getStruct("WS_PlayerKnockback", client->GetVersion());
if (packet) {
packet->setDataByName("target_x", target_spawn->GetX());
packet->setDataByName("target_y", target_spawn->GetY());
packet->setDataByName("target_z", target_spawn->GetZ());
packet->setDataByName("vertical_movement", vertical);
packet->setDataByName("horizontal_movement", horizontal);
if (use_heading)
packet->setDataByName("use_player_heading", 1);
if ((vertical != 0 || horizontal != 0)) {
if(spawn->IsPlayer()) {
Client* client = ((Player*)spawn)->GetClient();
PacketStruct* packet = configReader.getStruct("WS_PlayerKnockback", client->GetVersion());
if (packet) {
packet->setDataByName("target_x", target_spawn->GetX());
packet->setDataByName("target_y", target_spawn->GetY());
packet->setDataByName("target_z", target_spawn->GetZ());
packet->setDataByName("vertical_movement", vertical);
packet->setDataByName("horizontal_movement", horizontal);
if (use_heading)
packet->setDataByName("use_player_heading", 1);
client->QueuePacket(packet->serialize());
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
else {
spawn->SetKnockback(target_spawn, duration, vertical, horizontal);
}
safe_delete(packet);
}
return 0;

View file

@ -122,6 +122,13 @@ Player::Player(){
all_spells_locked = false;
current_language_id = 0;
active_reward = false;
SortedTraitList = new map <int8, map <int8, vector<TraitData*> > >;
ClassTraining = new map <int8, vector<TraitData*> >;
RaceTraits = new map <int8, vector<TraitData*> >;
InnateRaceTraits = new map <int8, vector<TraitData*> >;
FocusEffects = new map <int8, vector<TraitData*> >;
need_trait_update = true;
}
Player::~Player(){
SetSaveSpellEffects(true);
@ -204,6 +211,11 @@ Player::~Player(){
ClearPendingItemRewards();
ClearEverything();
safe_delete(SortedTraitList);
safe_delete(ClassTraining);
safe_delete(RaceTraits);
safe_delete(InnateRaceTraits);
safe_delete(FocusEffects);
// leak fix on Language* pointer from Player::AddLanguage
player_languages_list.Clear();
}
@ -2509,7 +2521,7 @@ int16 Player::GetSpellSlotMappingCount(){
MSpellsBook.lock();
for(int32 i=0;i<spells.size();i++){
SpellBookEntry* spell = (SpellBookEntry*)spells[i];
if(spell->slot >= 0 && spell->spell_id > 0)
if(spell->slot >= 0 && spell->spell_id > 0 && spell->type != SPELL_BOOK_TYPE_NOT_SHOWN)
ret++;
}
MSpellsBook.unlock();
@ -2535,7 +2547,7 @@ int16 Player::GetSpellPacketCount(){
MSpellsBook.lock();
for(int32 i=0;i<spells.size();i++){
SpellBookEntry* spell = (SpellBookEntry*)spells[i];
if(spell->spell_id > 0)
if(spell->spell_id > 0 && spell->type != SPELL_BOOK_TYPE_NOT_SHOWN)
ret++;
}
MSpellsBook.unlock();
@ -2852,7 +2864,7 @@ EQ2Packet* Player::GetSpellSlotMappingPacket(int16 version){
MSpellsBook.lock();
for(int32 i=0;i<spells.size();i++){
SpellBookEntry* spell = (SpellBookEntry*)spells[i];
if(spell->slot < 0 || spell->spell_id == 0)
if(spell->type == SPELL_BOOK_TYPE_NOT_SHOWN || spell->slot < 0 || spell->spell_id == 0)
continue;
packet->setArrayDataByName("spell_id", spell->spell_id, ptr);
packet->setArrayDataByName("slot_id", (int16)spell->slot, ptr);
@ -2903,7 +2915,7 @@ EQ2Packet* Player::GetSpellBookUpdatePacket(int16 version) {
MSpellsBook.lock();
for (int32 i = 0; i < spells.size(); i++) {
spell_entry = (SpellBookEntry*)spells[i];
if (spell_entry->spell_id == 0)
if (spell_entry->spell_id == 0 || spell_entry->type == SPELL_BOOK_TYPE_NOT_SHOWN)
continue;
spell = master_spell_list.GetSpell(spell_entry->spell_id, spell_entry->tier);
if (spell) {

View file

@ -32,6 +32,7 @@
#include "Titles.h"
#include "Languages.h"
#include "Achievements/Achievements.h"
#include "Traits/Traits.h"
#include <algorithm>
#include <set>
@ -244,18 +245,6 @@ struct SpawnQueueState {
int16 index_id;
};
struct QuestRewardData {
int32 quest_id;
bool is_temporary;
std::string description;
bool is_collection;
bool has_displayed;
int64 tmp_coin;
int32 tmp_status;
bool db_saved;
int32 db_index;
};
class PlayerLoginAppearance {
public:
PlayerLoginAppearance() { appearanceList = new map<int8, LoginAppearances>; }
@ -435,12 +424,6 @@ private:
Mutex MFlagChanges;
};
enum AddItemType {
NOT_SET = 0,
BUY_FROM_BROKER = 1,
GM_COMMAND = 2
};
class Player : public Entity{
public:
Player();
@ -1093,6 +1076,14 @@ public:
void ProcessSpawnRangeUpdates();
Mutex MPlayerQuests;
float pos_packet_speed;
map <int8, map <int8, vector<TraitData*> > >* SortedTraitList;
map <int8, vector<TraitData*> >* ClassTraining;
map <int8, vector<TraitData*> >* RaceTraits;
map <int8, vector<TraitData*> >* InnateRaceTraits;
map <int8, vector<TraitData*> >* FocusEffects;
mutable std::shared_mutex trait_mutex;
std::atomic<bool> need_trait_update;
private:
bool reset_mentorship;
bool range_attack;

View file

@ -227,6 +227,12 @@ void RuleManager::Init()
RULE_INIT(R_Player, CoinWeightPerStone, "40.0"); // coin weight per stone, 40.0 = 40 coins per 1 stone (per DoF client hover over)
RULE_INIT(R_Player, WeightInflictsSpeed, "1"); // whether weight will inflict speed, 1 = on, 0 = off
RULE_INIT(R_Player, LevelMasterySkillMultiplier, "5"); // multiplier for adventure level / recommended level when applying mastery damage to determine if they are in mastery range
RULE_INIT(R_Player, TraitTieringSelection, "1"); // when set to true limited to single trait per group, otherwise you can freely select from any group
RULE_INIT(R_Player, ClassicTraitLevelTable, "1"); // uses built in table based on Prima Guide, see Traits.cpp for more, otherwise uses the levels below
RULE_INIT(R_Player, TraitFocusSelectLevel, "9"); // x levels to receive new trait of focus, eg level/rule, level 10, rule value 5, 10/5 = 2 focus traits available at level 10
RULE_INIT(R_Player, TraitTrainingSelectLevel, "10"); // x levels to receive new trait of focus
RULE_INIT(R_Player, TraitRaceSelectLevel, "10"); // x levels to receive new trait of focus
RULE_INIT(R_Player, TraitCharacterSelectLevel, "10"); // x levels to receive new trait of focus
/* PVP */
RULE_INIT(R_PVP, AllowPVP, "0");

View file

@ -87,6 +87,12 @@ enum RuleType {
CoinWeightPerStone,
WeightInflictsSpeed,
LevelMasterySkillMultiplier,
TraitTieringSelection,
ClassicTraitLevelTable,
TraitFocusSelectLevel,
TraitTrainingSelectLevel,
TraitRaceSelectLevel,
TraitCharacterSelectLevel,
/* PVP */
AllowPVP,

View file

@ -148,6 +148,7 @@ Spawn::Spawn(){
is_loot_complete = false;
is_loot_dispensed = false;
reset_movement = false;
ResetKnockedBack();
}
Spawn::~Spawn(){
@ -522,7 +523,6 @@ EQ2Packet* Spawn::spawn_serialize(Player* player, int16 version, int16 offset, i
pos_struct->ResetData();
InitializePosPacketData(player, pos_struct);
if (version <= 283) {
if (offset == 777) {
info_struct->setDataByName("name", "This is a really long name\n");
@ -2091,7 +2091,13 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b
m_GridMutex.releasewritelock(__FUNCTION__, __LINE__);
}
packet->setDataByName("pos_grid_id", new_grid_id != 0 ? new_grid_id : GetLocation());
if(IsKnockedBack()) {
packet->setDataByName("pos_grid_id", 0);
}
else {
packet->setDataByName("pos_grid_id", new_grid_id != 0 ? new_grid_id : GetLocation());
}
bool include_heading = true;
if (IsWidget() && ((Widget*)this)->GetIncludeHeading() == false)
include_heading = false;
@ -2274,7 +2280,6 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b
}
if (IsNPC() || IsPlayer()) {
packet->setDataByName("pos_move_type", 25);
}
@ -2284,7 +2289,7 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b
else if(IsGroundSpawn()) {
packet->setDataByName("pos_move_type", 16);
}
if (!IsPlayer()) // has to be 2 or NPC's warp around when moving
packet->setDataByName("pos_movement_mode", 2);
@ -2437,7 +2442,6 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
packet->setDataByName("mood_state", appearance.mood_state);
packet->setDataByName("gender", appearance.gender);
packet->setDataByName("race", appearance.race);
packet->setDataByName("gender", appearance.gender);
if (IsEntity()) {
Entity* entity = ((Entity*)this);
packet->setDataByName("combat_voice", entity->GetCombatVoice());
@ -2740,7 +2744,10 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
*/
if (IsPlayer()) {
if (((Player*)this)->GetFollowTarget())
packet->setDataByName("follow_target", ((((Player*)this)->GetIDWithPlayerSpawn(((Player*)this)->GetFollowTarget()) * -1) - 1));
packet->setDataByName("follow_target", version <= 547 ? (((Player*)this)->GetIDWithPlayerSpawn(((Player*)this)->GetFollowTarget())) : ((((Player*)this)->GetIDWithPlayerSpawn(((Player*)this)->GetFollowTarget()) * -1) - 1));
else if(version <= 547) {
packet->setDataByName("follow_target", 0xFFFFFFFF);
}
//else
// packet->setDataByName("follow_target", 0xFFFFFFFF);
}
@ -2872,6 +2879,12 @@ void Spawn::ProcessMovement(bool isSpawnListLocked){
}*/
return;
}
if(IsKnockedBack()) {
if(CalculateSpawnProjectilePosition(GetX(), GetY(), GetZ()))
return; // being launched!
}
if(reset_movement) {
ResetMovement();
reset_movement = false;
@ -4170,7 +4183,7 @@ void Spawn::FixZ(bool forceUpdate) {
uint32 WidgetID = 0;
float new_z = GetY();
if(GetMap() != nullptr) {
float new_z = FindBestZ(current_loc, nullptr, &GridID, &WidgetID);
new_z = FindBestZ(current_loc, nullptr, &GridID, &WidgetID);
if ((IsTransportSpawn() || !IsFlyingCreature()) && GridID != 0 && GridID != GetLocation()) {
LogWrite(PLAYER__DEBUG, 0, "Player", "%s left grid %u and entered grid %u", appearance.name, GetLocation(), GridID);
@ -5056,4 +5069,125 @@ void Spawn::DistributeGroupLoot_RoundRobin(std::vector<int32>* item_list, bool r
}
}
}
}
const double g = 9.81; // acceleration due to gravity (m/s^2)
void Spawn::CalculateInitialVelocity(float heading, float distanceHorizontal, float distanceVertical, float distanceDepth, float duration) {
float vx = distanceHorizontal / duration;
float vy = (distanceVertical + 0.5 * g * duration * duration) / duration;
float vz = distanceDepth / duration;
// Convert heading angle to radians
knocked_velocity.x = vx * cos(heading);
knocked_velocity.y = vy;
knocked_velocity.z = vz * sin(heading);
}
// Function to calculate the projectile position at a given time
glm::vec3 Spawn::CalculateProjectilePosition(glm::vec3 initialVelocity, float time) {
glm::vec3 position;
position.x = knocked_back_start_x + initialVelocity.x * time;
position.y = knocked_back_start_y + initialVelocity.y * time - 0.5 * g * time * time;
position.z = knocked_back_start_z + initialVelocity.z * time;
auto loc = glm::vec3(position.x, position.z, position.y);
float new_z = FindBestZ(loc, nullptr);
if(new_z > position.y)
position.y = new_z;
return position;
}
bool Spawn::CalculateSpawnProjectilePosition(float x, float y, float z) {
float currentTimeOffset = (Timer::GetCurrentTime2() - last_movement_update) * 0.001; // * 0.001 is the same as / 1000, float muliplications is suppose to be faster though
float stepAheadOne = currentTimeOffset+currentTimeOffset;
knocked_back_time_step += currentTimeOffset;
if(Timer::GetCurrentTime2() >= knocked_back_end_time) {
ResetKnockedBack();
FixZ(true);
return false;
}
glm::vec3 position = CalculateProjectilePosition(knocked_velocity, knocked_back_time_step);
glm::vec3 position_two = position;
if(Timer::GetCurrentTime2() <= knocked_back_end_time+stepAheadOne) {
position_two = CalculateProjectilePosition(knocked_velocity, knocked_back_time_step+stepAheadOne);
}
if(GetMap()) {
glm::vec3 loc(GetX(), GetZ(), GetY() + .5f);
glm::vec3 dest_loc(position_two.x, position_two.z, position_two.y);
MIgnoredWidgets.lock_shared();
glm::vec3 outNorm;
float dist = 0.0f;
bool collide_ = GetMap()->DoCollisionCheck(loc, dest_loc, &ignored_widgets, outNorm, dist);
if(collide_) {
LogWrite(SPAWN__ERROR, 0, "Spawn", "Collision Hit: cur loc x,y,z: %f %f %f. to loc %f %f %f. TimeOffset: %f Total Time: %f Duration: %f", GetX(),GetY(),GetZ(),position_two.x,position_two.y,position_two.z, currentTimeOffset, knocked_back_time_step, knocked_back_duration);
MIgnoredWidgets.unlock_shared();
ResetKnockedBack();
FixZ(true);
return false;
}
MIgnoredWidgets.unlock_shared();
}
LogWrite(SPAWN__ERROR, 0, "Spawn", "x,y,z: %f %f %f. Final %f %f %f. TimeOffset: %f Total Time: %f Duration: %f", GetX(),GetY(),GetZ(),position.x,position.y,position.z, currentTimeOffset, knocked_back_time_step, knocked_back_duration);
SetX(position.x, false);
SetZ(position.z, false);
SetY(position.y, false, true);
SetPos(&appearance.pos.X2, position_two.x, false);
SetPos(&appearance.pos.Z2, position_two.z, false);
SetPos(&appearance.pos.Y2, position_two.y, false);
SetPos(&appearance.pos.X3, position_two.x, false);
SetPos(&appearance.pos.Z3, position_two.z, false);
SetPos(&appearance.pos.Y3, position_two.y, false);
position_changed = true;
changed = true;
GetZone()->AddChangedSpawn(this);
return true;
}
void Spawn::SetKnockback(Spawn* target, int32 duration, float vertical, float horizontal) {
if(knocked_back) {
return; // already being knocked back
}
// Calculate the direction vector from source to destination
glm::vec3 direction = {GetX() - target->GetX(), GetZ() - target->GetZ(), GetY() - target->GetY()};
// Calculate the heading angle in radians
double headingRad = atan2(direction.y, sqrt(direction.x * direction.x + direction.z * direction.z));
knocked_angle = headingRad;
knocked_back_start_x = GetX();
knocked_back_start_y = GetY();
knocked_back_start_z = GetZ();
knocked_back_h_distance = horizontal / 10.0f;
knocked_back_v_distance = vertical / 10.0f;
knocked_back_duration = static_cast<float>(duration) / 1000.0f;
knocked_back_end_time = Timer::GetCurrentTime2() + duration;
CalculateInitialVelocity(knocked_angle, knocked_back_h_distance, knocked_back_h_distance, knocked_back_h_distance, knocked_back_duration);
knocked_back = true;
}
void Spawn::ResetKnockedBack() {
knocked_back = false;
knocked_back_time_step = 0.0f;
knocked_back_h_distance = 0.0f;
knocked_back_v_distance = 0.0f;
knocked_back_duration = 0.0f;
knocked_back_end_time = 0;
knocked_back_start_x = 0.0f;
knocked_back_start_y = 0.0f;
knocked_back_start_z = 0.0f;
knocked_angle = 0.0f;
knocked_velocity.x = 0.0f;
knocked_velocity.y = 0.0f;
knocked_velocity.z = 0.0f;
}

View file

@ -1383,6 +1383,12 @@ public:
bool IsItemInLootTier(Item* item);
void DistributeGroupLoot_RoundRobin(std::vector<int32>* item_list, bool roundRobinTrashLoot = false); // trash loot is what falls under the item tier requirement by group options
void CalculateInitialVelocity(float heading, float distanceHorizontal, float distanceVertical, float distanceDepth, float duration);
glm::vec3 CalculateProjectilePosition(glm::vec3 initialVelocity, float time);
bool CalculateSpawnProjectilePosition(float x, float y, float z);
void SetKnockback(Spawn* target, int32 duration, float vertical, float horizontal);
void ResetKnockedBack();
mutable std::shared_mutex MIgnoredWidgets;
std::map<int32, bool> ignored_widgets;
@ -1429,6 +1435,7 @@ protected:
void CheckProximities();
Timer pause_timer;
bool IsKnockedBack() { return knocked_back; }
private:
int32 loot_group_id;
GroupLootMethod loot_method;
@ -1517,6 +1524,18 @@ private:
bool is_collector;
bool scared_by_strong_players;
std::atomic<bool> is_alive;
std::atomic<bool> knocked_back;
float knocked_back_time_step;
float knocked_back_h_distance;
float knocked_back_v_distance;
float knocked_back_duration;
float knocked_back_start_x;
float knocked_back_start_y;
float knocked_back_start_z;
int32 knocked_back_end_time;
float knocked_angle;
glm::vec3 knocked_velocity;
};
#endif

View file

@ -980,7 +980,7 @@ bool SpellProcess::CheckSpellQueue(Spell* spell, Entity* caster){
}
void SpellProcess::SendSpellBookUpdate(Client* client){
if(client){
if(client && client->IsReadyForSpawns()){
EQ2Packet* app = client->GetPlayer()->GetSpellBookUpdatePacket(client->GetVersion());
if(app)
client->QueuePacket(app);

View file

@ -23,11 +23,18 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
#include "../../common/Log.h"
#include "../Spells.h"
#include "../WorldDatabase.h"
#include "../client.h"
#include "../Rules/Rules.h"
#include <boost/assign.hpp>
#include <map>
extern ConfigReader configReader;
extern MasterSpellList master_spell_list;
extern WorldDatabase database;
extern RuleManager rule_manager;
using namespace boost::assign;
MasterTraitList::MasterTraitList(){
MMasterTraitList.SetName("MasterTraitList::TraitList");
@ -47,46 +54,46 @@ int MasterTraitList::Size(){
return TraitList.size();
}
EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
bool MasterTraitList::GenerateTraitLists(Client* client, map <int8, map <int8, vector<TraitData*> > >* sortedTraitList, map <int8, vector<TraitData*> >* classTraining,
map <int8, vector<TraitData*> >* raceTraits, map <int8, vector<TraitData*> >* innateRaceTraits, map <int8, vector<TraitData*> >* focusEffects, int16 max_level, int8 trait_group)
{
if (!client) {
LogWrite(SPELL__ERROR, 0, "Traits", "GetTraitListPacket called with an invalid client");
return 0;
return false;
}
// Sort the Data
if (Size() == 0)
return NULL;
map <int8, map <int8, vector<TraitData*> > > SortedTraitList;
if (Size() == 0 || !sortedTraitList || !classTraining || !raceTraits || !innateRaceTraits || !focusEffects)
return false;
map <int8, map <int8, vector<TraitData*> > >::iterator itr;
map <int8, vector<TraitData*> >::iterator itr2;
vector<TraitData*>::iterator itr3;
map <int8, vector<TraitData*> > ClassTraining;
map <int8, vector<TraitData*> > RaceTraits;
map <int8, vector<TraitData*> > InnateRaceTraits;
map <int8, vector<TraitData*> > FocusEffects;
MMasterTraitList.readlock(__FUNCTION__, __LINE__);
for (int i=0; i < Size(); i++) {
if(max_level > 0 && TraitList[i]->level > max_level) {
continue; // skip per max level requirement
}
if(trait_group != 255 && trait_group != TraitList[i]->group) {
continue;
}
// Sort Character Traits
if (TraitList[i]->classReq == 255 && TraitList[i]->raceReq == 255 && !TraitList[i]->isFocusEffect && TraitList[i]->isTrait) {
itr = SortedTraitList.lower_bound(TraitList[i]->group);
if (itr != SortedTraitList.end() && !(SortedTraitList.key_comp()(TraitList[i]->group, itr->first))) {
itr = sortedTraitList->lower_bound(TraitList[i]->group);
if (itr != sortedTraitList->end() && !(sortedTraitList->key_comp()(TraitList[i]->group, itr->first))) {
itr2 = (itr->second).lower_bound(TraitList[i]->level);
if (itr2 != (itr->second).end() && !((itr->second).key_comp()(TraitList[i]->level, itr2->first))) {
((itr->second)[itr2->first]).push_back(TraitList[i]);
//LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
}
else {
vector<TraitData*> tempVec;
tempVec.push_back(TraitList[i]);
(itr->second).insert(make_pair(TraitList[i]->level, tempVec));
//LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
}
}
else {
@ -94,51 +101,55 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
vector <TraitData*> tempVec;
tempVec.push_back(TraitList[i]);
tempMap.insert(make_pair(TraitList[i]->level, tempVec));
SortedTraitList.insert(make_pair(TraitList[i]->group, tempMap));
//LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
sortedTraitList->insert(make_pair(TraitList[i]->group, tempMap));
LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
}
}
// Sort Class Training
if (TraitList[i]->classReq == client->GetPlayer()->GetAdventureClass() && TraitList[i]->isTraining) {
itr2 = ClassTraining.lower_bound(TraitList[i]->level);
if (itr2 != ClassTraining.end() && !(ClassTraining.key_comp()(TraitList[i]->level, itr2->first))) {
itr2 = classTraining->lower_bound(TraitList[i]->level);
if (itr2 != classTraining->end() && !(classTraining->key_comp()(TraitList[i]->level, itr2->first))) {
(itr2->second).push_back(TraitList[i]);
}
else {
vector<TraitData*> tempVec;
tempVec.push_back(TraitList[i]);
ClassTraining.insert(make_pair(TraitList[i]->level, tempVec));
classTraining->insert(make_pair(TraitList[i]->level, tempVec));
}
}
// Sort Racial Abilities
if (TraitList[i]->raceReq == client->GetPlayer()->GetRace() && !TraitList[i]->isInate && !TraitList[i]->isTraining) {
itr2 = RaceTraits.lower_bound(TraitList[i]->group);
if (itr2 != RaceTraits.end() && !(RaceTraits.key_comp()(TraitList[i]->group, itr2->first))) {
itr2 = raceTraits->lower_bound(TraitList[i]->group);
if (itr2 != raceTraits->end() && !(raceTraits->key_comp()(TraitList[i]->group, itr2->first))) {
(itr2->second).push_back(TraitList[i]);
}
else {
vector<TraitData*> tempVec;
tempVec.push_back(TraitList[i]);
RaceTraits.insert(make_pair(TraitList[i]->group, tempVec));
raceTraits->insert(make_pair(TraitList[i]->group, tempVec));
}
}
// Sort Innate Racial Abilities
if (TraitList[i]->raceReq == client->GetPlayer()->GetRace() && TraitList[i]->isInate) {
itr2 = InnateRaceTraits.lower_bound(TraitList[i]->group);
if (itr2 != InnateRaceTraits.end() && !(InnateRaceTraits.key_comp()(TraitList[i]->group, itr2->first))) {
itr2 = innateRaceTraits->lower_bound(TraitList[i]->group);
if (itr2 != innateRaceTraits->end() && !(innateRaceTraits->key_comp()(TraitList[i]->group, itr2->first))) {
(itr2->second).push_back(TraitList[i]);
}
else {
vector<TraitData*> tempVec;
tempVec.push_back(TraitList[i]);
InnateRaceTraits.insert(make_pair(TraitList[i]->group, tempVec));
innateRaceTraits->insert(make_pair(TraitList[i]->group, tempVec));
}
}
// Sort Focus Effects
if ((TraitList[i]->classReq == client->GetPlayer()->GetAdventureClass() || TraitList[i]->classReq == 255) && TraitList[i]->isFocusEffect) {
int8 j = 0;
itr2 = FocusEffects.lower_bound(TraitList[i]->group);
if (itr2 != FocusEffects.end() && !(FocusEffects.key_comp()(TraitList[i]->group, itr2->first))) {
itr2 = focusEffects->lower_bound(TraitList[i]->group);
if (itr2 != focusEffects->end() && !(focusEffects->key_comp()(TraitList[i]->group, itr2->first))) {
(itr2->second).push_back(TraitList[i]);
//LogWrite(SPELL__INFO, 0, "Traits", "Added Focus Effect: %u Tier: %i", TraitList[i]->spellID, TraitList[i]->tier);
@ -147,14 +158,413 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
else {
vector<TraitData*> tempVec;
tempVec.push_back(TraitList[i]);
FocusEffects.insert(make_pair(TraitList[i]->group, tempVec));
focusEffects->insert(make_pair(TraitList[i]->group, tempVec));
//LogWrite(SPELL__INFO, 0, "Traits", "Added Focus Effect: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
}
}
}
MMasterTraitList.releasereadlock(__FUNCTION__, __LINE__);
return true;
}
bool MasterTraitList::IdentifyNextTrait(Client* client, map <int8, vector<TraitData*> >* traitList, vector<TraitData*>* collectTraits, vector<TraitData*>* tieredTraits, std::map<int32, int8>* previousMatchedSpells, bool omitFoundMatches) {
bool found_spell_match = false;
bool match = false;
bool tiered_selection = rule_manager.GetGlobalRule(R_Player, TraitTieringSelection)->GetBool();
int8 group_to_apply = 255;
int8 count = 0;
map <int8, vector<TraitData*> >::iterator itr2;
vector<TraitData*>::iterator itr3;
for (itr2 = traitList->begin(); itr2 != traitList->end(); itr2++) {
//LogWrite(SPELL__INFO, 0, "AA", "Character Traits Size...%i ", traits_size);
for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++) {
if(tiered_selection) {
if(found_spell_match && (*itr3)->group == group_to_apply) {
continue; // skip!
}
else if((*itr3)->group != group_to_apply) {
if(group_to_apply != 255 && !found_spell_match) {
// found match
LogWrite(SPELL__INFO, 0, "Traits", "Found match to group id %u", group_to_apply);
match = true;
break;
}
else {
LogWrite(SPELL__INFO, 0, "Traits", "Try match to group... spell id %u, group id %u", (*itr3)->spellID, (*itr3)->group);
found_spell_match = false;
group_to_apply = (*itr3)->group;
count = 0;
if(!omitFoundMatches)
tieredTraits->clear();
}
}
}
count++;
std::map<int32,int8>::iterator spell_itr = previousMatchedSpells->find((*itr3)->spellID);
if(spell_itr != previousMatchedSpells->end() && (*itr3)->group > spell_itr->second) {
continue;
}
if(!IsPlayerAllowedTrait(client, (*itr3))) {
LogWrite(SPELL__INFO, 0, "Traits", "We are not allowed any more spells from this type/group... spell id %u, group id %u", (*itr3)->spellID, (*itr3)->group);
found_spell_match = true;
}
else if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
LogWrite(SPELL__INFO, 0, "Traits", "Found existing spell match to group... spell id %u, group id %u", (*itr3)->spellID, (*itr3)->group);
if(!omitFoundMatches)
found_spell_match = true;
previousMatchedSpells->insert(std::make_pair((*itr3)->spellID,(*itr3)->group));
}
else {
tieredTraits->push_back((*itr3));
collectTraits->push_back((*itr3));
}
}
if(match)
break;
}
if(!match && group_to_apply != 255 && !found_spell_match) {
// found match
match = true;
}
else if(!tiered_selection && collectTraits->size() > 0) {
match = true;
}
return match;
}
bool MasterTraitList::ChooseNextTrait(Client* client) {
map <int8, map <int8, vector<TraitData*> > >* SortedTraitList = new map <int8, map <int8, vector<TraitData*> > >;
map <int8, map <int8, vector<TraitData*> > >::iterator itr;
bool tiered_selection = rule_manager.GetGlobalRule(R_Player, TraitTieringSelection)->GetBool();
vector<TraitData*>::iterator itr3;
map <int8, vector<TraitData*> >* ClassTraining = new map <int8, vector<TraitData*> >;
map <int8, vector<TraitData*> >* RaceTraits = new map <int8, vector<TraitData*> >;
map <int8, vector<TraitData*> >* InnateRaceTraits = new map <int8, vector<TraitData*> >;
map <int8, vector<TraitData*> >* FocusEffects = new map <int8, vector<TraitData*> >;
vector<TraitData*>* collectTraits = new vector<TraitData*>;
vector<TraitData*>* tieredTraits = new vector<TraitData*>;
std::map<int32, int8>* previousMatchedSpells = new std::map<int32, int8>;
bool match = false;
if(GenerateTraitLists(client, SortedTraitList, ClassTraining, RaceTraits, InnateRaceTraits, FocusEffects, client->GetPlayer()->GetLevel())) {
vector<TraitData*>* endTraits;
if(!match || !tiered_selection) {
match = IdentifyNextTrait(client, ClassTraining, collectTraits, tieredTraits, previousMatchedSpells);
}
if(!match || !tiered_selection) {
match = IdentifyNextTrait(client, RaceTraits, collectTraits, tieredTraits, previousMatchedSpells, true);
bool overrideMatch = IdentifyNextTrait(client, InnateRaceTraits, collectTraits, tieredTraits, previousMatchedSpells, true);
if(!match && overrideMatch)
match = true;
}
if(!match || !tiered_selection) {
match = IdentifyNextTrait(client, FocusEffects, collectTraits, tieredTraits, previousMatchedSpells);
}
if(!tiered_selection && collectTraits->size() > 0) {
endTraits = collectTraits;
}
else if (match) {
endTraits = tieredTraits;
}
if(match) {
PacketStruct* packet = configReader.getStruct("WS_QuestRewardPackMsg", client->GetVersion());
// 0=enemy mastery, 1=specialized training,2=character trait, 3=racial tradition
int8 packet_type = 0;
int8 item_count = 0;
packet->setSubstructArrayLengthByName("reward_data", "num_select_rewards", endTraits->size());
for (itr3 = endTraits->begin(); itr3 != endTraits->end(); itr3++) {
if((*itr3)->item_id) {
//LogWrite(SPELL__INFO, 0, "Traits", "Item %u to be sent", (*itr3)->item_id);
Item* item = master_item_list.GetItem((*itr3)->item_id);
if(item) {
//LogWrite(SPELL__INFO, 0, "Traits", "Item found %s to be sent", item->name.c_str());
packet->setArrayDataByName("select_reward_id", (*itr3)->item_id, item_count);
packet->setItemArrayDataByName("select_item", item, client->GetPlayer(), item_count, 0, -1);
item_count++;
}
}
// Sort Character Traits
if ((*itr3)->classReq == 255 && (*itr3)->raceReq == 255 && (*itr3)->isTrait) {
packet_type = 2;
}
// Sort Class Training
else if ((*itr3)->classReq == client->GetPlayer()->GetAdventureClass() && (*itr3)->isTraining) {
packet_type = 1;
}
// Sort Racial Abilities
else if ((*itr3)->raceReq == client->GetPlayer()->GetRace() && !(*itr3)->isInate && !(*itr3)->isTraining) {
packet_type = 3;
}
// Sort Innate Racial Abilities
else if ((*itr3)->raceReq == client->GetPlayer()->GetRace() && (*itr3)->isInate) {
packet_type = 3;
}
//LogWrite(SPELL__INFO, 0, "Traits", "Sending trait %u", (*itr3)->spellID);
}
packet->setSubstructDataByName("reward_data", "unknown1", packet_type);
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
}
safe_delete(SortedTraitList);
safe_delete(ClassTraining);
safe_delete(RaceTraits);
safe_delete(InnateRaceTraits);
safe_delete(FocusEffects);
safe_delete(collectTraits);
safe_delete(tieredTraits);
safe_delete(previousMatchedSpells);
return match;
}
int16 MasterTraitList::GetSpellCount(Client* client, map <int8, vector<TraitData*> >* traits, bool onlyCharTraits) {
if(!traits)
return 0;
int16 count = 0;
map <int8, vector<TraitData*> >::iterator itr2;
vector<TraitData*>::iterator itr3;
for (itr2 = traits->begin(); itr2 != traits->end(); itr2++) {
for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++) {
if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
if(!onlyCharTraits || (onlyCharTraits && (*itr3)->classReq == 255 && (*itr3)->raceReq == 255 && (*itr3)->isTrait)) {
count++;
}
}
}
}
return count;
}
vector<int16> PersonalTraitLevelLimits = boost::assign::list_of(0)(8)(14)(22)(28)(36)(42)(46)(48);
vector<int16> TrainingTraitLevelLimits = boost::assign::list_of(0)(10)(20)(30)(40)(50);
vector<int16> RacialTraitLevelLimits = boost::assign::list_of(0)(18)(26)(34)(44);
vector<int16> CharacterTraitLevelLimits = boost::assign::list_of(0)(12)(16)(24)(32)(38);
/***
Every other level, beginning at Level 8,
you gain an additional advantage a
Personal Trait, an Enemy Tactic, a Racial
Tradition or a Training ability. Each time
you reach an even-numbered level, you
can select another advantage from the
appropriate list. You dont have to select
in order you may take any of the avail-
able choices.
Level Advantage
8 Personal Trait (1st)
10 Training (1st)
12 Enemy Tactic (1st)
14 Personal Trait (2nd)
16 Enemy Tactic (2nd)
18 Racial Tradition (1st)
20 Training (2nd)
22 Personal Trait (3rd)
24 Enemy Tactic (3rd)
26 Racial Tradition (2nd)
28 Personal Trait (4th)
30 Training (3rd)
32 Enemy Tactic (4th)
34 Racial Tradition (3rd)
36 Personal Trait (5th)
38 Enemy Tactic (5th)
40 Training (4th)
42 Personal Trait (6th)
44 Racial Tradition (4th)
46 Personal Trait (7th)
48 Personal Trait (8th)
50 Training (5th)
***/
bool MasterTraitList::IsPlayerAllowedTrait(Client* client, TraitData* trait) {
std::unique_lock(client->GetPlayer()->trait_mutex);
map <int8, map <int8, vector<TraitData*> > >* SortedTraitList = client->GetPlayer()->SortedTraitList;
map <int8, map <int8, vector<TraitData*> > >::iterator itr;
map <int8, vector<TraitData*> >::iterator itr2;
vector<TraitData*>::iterator itr3;
bool use_classic_table = rule_manager.GetGlobalRule(R_Player, ClassicTraitLevelTable)->GetBool();
map <int8, vector<TraitData*> >* ClassTraining = client->GetPlayer()->ClassTraining;
map <int8, vector<TraitData*> >* RaceTraits = client->GetPlayer()->RaceTraits;
map <int8, vector<TraitData*> >* InnateRaceTraits = client->GetPlayer()->InnateRaceTraits;
map <int8, vector<TraitData*> >* FocusEffects = client->GetPlayer()->FocusEffects;
if(client->GetPlayer()->need_trait_update) {
SortedTraitList->clear();
ClassTraining->clear();
RaceTraits->clear();
InnateRaceTraits->clear();
FocusEffects->clear();
}
bool allowed_trait = false;
if(!client->GetPlayer()->need_trait_update || GenerateTraitLists(client, SortedTraitList, ClassTraining, RaceTraits, InnateRaceTraits, FocusEffects, 0)) {
client->GetPlayer()->need_trait_update = false;
if(trait->isFocusEffect) {
int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitFocusSelectLevel)->GetInt32();
int16 num_available_selections = 0;
if(trait_level > 0) {
num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
}
int16 total_used = GetSpellCount(client, FocusEffects);
int16 classic_avail = 0;
if(use_classic_table && PersonalTraitLevelLimits.size() > total_used+1) {
int16 classic_level_req = PersonalTraitLevelLimits.at(total_used+1);
if(client->GetPlayer()->GetLevel() >= classic_level_req)
classic_avail = num_available_selections = total_used+1;
else
num_available_selections = 0;
}
else if(use_classic_table)
num_available_selections = 0;
LogWrite(SPELL__INFO, 9, "Traits", "%s FocusEffects used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used, num_available_selections, classic_avail);
if(total_used < num_available_selections) {
allowed_trait = true;
}
}
else if(trait->isTraining) {
int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitTrainingSelectLevel)->GetInt32();
int16 num_available_selections = 0;
if(trait_level > 0) {
num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
}
int16 total_used = GetSpellCount(client, ClassTraining);
int16 classic_avail = 0;
if(use_classic_table && TrainingTraitLevelLimits.size() > total_used+1) {
int16 classic_level_req = TrainingTraitLevelLimits.at(total_used+1);
if(client->GetPlayer()->GetLevel() >= classic_level_req)
classic_avail = num_available_selections = total_used+1;
else
num_available_selections = 0;
}
else if(use_classic_table)
num_available_selections = 0;
LogWrite(SPELL__INFO, 9, "Traits", "%s ClassTraining used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used, num_available_selections, classic_avail);
if(total_used < num_available_selections) {
allowed_trait = true;
}
}
else {
if(trait->raceReq == client->GetPlayer()->GetRace()) {
int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitRaceSelectLevel)->GetInt32();
int16 num_available_selections = 0;
if(trait_level > 0) {
num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
}
int16 total_used = GetSpellCount(client, RaceTraits);
int16 total_used2 = GetSpellCount(client, InnateRaceTraits);
int16 classic_avail = 0;
if(use_classic_table && RacialTraitLevelLimits.size() > total_used+total_used2+1) {
int16 classic_level_req = RacialTraitLevelLimits.at(total_used+total_used2+1);
if(client->GetPlayer()->GetLevel() >= classic_level_req)
classic_avail = num_available_selections = total_used+total_used2+1;
else
num_available_selections = 0;
}
else if(use_classic_table)
num_available_selections = 0;
LogWrite(SPELL__INFO, 9, "Traits", "%s RaceTraits used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used+total_used2, num_available_selections, classic_avail);
if(total_used+total_used2 < num_available_selections) {
allowed_trait = true;
}
}
else { // character trait?
int16 num_available_selections = 0;
int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitCharacterSelectLevel)->GetInt32();
if(trait_level > 0) {
num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
}
int16 total_used = 0;
for (itr = SortedTraitList->begin(); itr != SortedTraitList->end(); itr++) {
total_used += GetSpellCount(client, &itr->second, true);
}
int16 classic_avail = 0;
if(use_classic_table && CharacterTraitLevelLimits.size() > total_used+1) {
int16 classic_level_req = CharacterTraitLevelLimits.at(total_used+1);
if(client->GetPlayer()->GetLevel() >= classic_level_req)
classic_avail = num_available_selections = total_used+1;
else
num_available_selections = 0;
}
else if(use_classic_table)
num_available_selections = 0;
LogWrite(SPELL__INFO, 9, "Traits", "%s CharacterTraits used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used, num_available_selections, classic_avail);
if(total_used < num_available_selections) {
allowed_trait = true;
}
}
}
}
return allowed_trait;
}
EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
{
std::unique_lock(client->GetPlayer()->trait_mutex);
if (!client) {
LogWrite(SPELL__ERROR, 0, "Traits", "GetTraitListPacket called with an invalid client");
return 0;
}
// Sort the Data
if (Size() == 0)
return NULL;
map <int8, map <int8, vector<TraitData*> > >* SortedTraitList = client->GetPlayer()->SortedTraitList;
map <int8, map <int8, vector<TraitData*> > >::iterator itr;
map <int8, vector<TraitData*> >::iterator itr2;
vector<TraitData*>::iterator itr3;
map <int8, vector<TraitData*> >* ClassTraining = client->GetPlayer()->ClassTraining;
map <int8, vector<TraitData*> >* RaceTraits = client->GetPlayer()->RaceTraits;
map <int8, vector<TraitData*> >* InnateRaceTraits = client->GetPlayer()->InnateRaceTraits;
map <int8, vector<TraitData*> >* FocusEffects = client->GetPlayer()->FocusEffects;
if(client->GetPlayer()->need_trait_update) {
SortedTraitList->clear();
ClassTraining->clear();
RaceTraits->clear();
InnateRaceTraits->clear();
FocusEffects->clear();
}
if(client->GetPlayer()->need_trait_update && !GenerateTraitLists(client, SortedTraitList, ClassTraining, RaceTraits, InnateRaceTraits, FocusEffects)) {
return NULL;
}
client->GetPlayer()->need_trait_update = false;
int16 version = 1;
int8 count = 0;
int8 index = 0;
@ -167,18 +577,19 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
version = client->GetVersion();
// Jabantiz: Get the value for num_traits in the struct (num_traits refers to the number of rows not the total number of traits)
for (itr = SortedTraitList.begin(); itr != SortedTraitList.end(); itr++) {
for (itr = SortedTraitList->begin(); itr != SortedTraitList->end(); itr++) {
num_traits += (itr->second).size();
}
PacketStruct* packet = configReader.getStruct("WS_TraitsList", version);
if (packet == NULL)
if (packet == NULL) {
return NULL;
}
packet->setArrayLengthByName("num_traits", num_traits);
for (itr = SortedTraitList.begin(); itr != SortedTraitList.end(); itr++) {
for (itr = SortedTraitList->begin(); itr != SortedTraitList->end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++, index++) {
traits_size += (itr2->second).size();
@ -260,9 +671,9 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
}
// Class Training portion of the packet
packet->setArrayLengthByName("num_trainings", ClassTraining.size());
packet->setArrayLengthByName("num_trainings", ClassTraining->size());
index = 0;
for (itr2 = ClassTraining.begin(); itr2 != ClassTraining.end(); itr2++, index++) {
for (itr2 = ClassTraining->begin(); itr2 != ClassTraining->end(); itr2++, index++) {
count = 0;
Spell* tmp_spell = 0;
packet->setArrayDataByName("training_level", itr2->first, index);
@ -345,11 +756,11 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
}
}
// Racial Traits
packet->setArrayLengthByName("num_sections", RaceTraits.size());
packet->setArrayLengthByName("num_sections", RaceTraits->size());
index = 0;
string tempStr;
int8 num_selections = 0;
for (itr2 = RaceTraits.begin(); itr2 != RaceTraits.end(); itr2++, index++) {
for (itr2 = RaceTraits->begin(); itr2 != RaceTraits->end(); itr2++, index++) {
count = 0;
Spell* tmp_spell = 0;
switch (itr2->first)
@ -410,11 +821,11 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
// total number of Innate traits
num_traits = 0;
for (itr2 = InnateRaceTraits.begin(); itr2 != InnateRaceTraits.end(); itr2++) {
for (itr2 = InnateRaceTraits->begin(); itr2 != InnateRaceTraits->end(); itr2++) {
num_traits += (itr2->second).size();
}
packet->setArrayLengthByName("num_abilities", num_traits);
for (itr2 = InnateRaceTraits.begin(); itr2 != InnateRaceTraits.end(); itr2++) {
for (itr2 = InnateRaceTraits->begin(); itr2 != InnateRaceTraits->end(); itr2++) {
for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++, index++) {
Spell* innate_spell = master_spell_list.GetSpell((*itr3)->spellID, (*itr3)->tier);
if (innate_spell) {
@ -433,11 +844,11 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
num_selections = 0;
num_focuseffects = 0;
index = 0;
for (itr2 = FocusEffects.begin(); itr2 != FocusEffects.end(); itr2++) {
for (itr2 = FocusEffects->begin(); itr2 != FocusEffects->end(); itr2++) {
num_focuseffects += (itr2->second).size();
}
packet->setArrayLengthByName("num_focuseffects", num_focuseffects);
for (itr2 = FocusEffects.begin(); itr2 != FocusEffects.end(); itr2++) {
for (itr2 = FocusEffects->begin(); itr2 != FocusEffects->end(); itr2++) {
for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++, index++) {
Spell* spell = master_spell_list.GetSpell((*itr3)->spellID, (*itr3)->tier);
if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
@ -474,6 +885,7 @@ EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
//DumpPacket(outapp);
safe_delete(packet);
safe_delete(data);
return outapp;
}
@ -494,6 +906,22 @@ TraitData* MasterTraitList::GetTrait(int32 spellID) {
return data;
}
TraitData* MasterTraitList::GetTraitByItemID(int32 itemID) {
vector<TraitData*>::iterator itr;
TraitData* data = NULL;
MMasterTraitList.readlock(__FUNCTION__, __LINE__);
for (itr = TraitList.begin(); itr != TraitList.end(); itr++) {
if ((*itr)->item_id == itemID) {
data = (*itr);
break;
}
}
MMasterTraitList.releasereadlock(__FUNCTION__, __LINE__);
return data;
}
void MasterTraitList::DestroyTraits(){
MMasterTraitList.writelock(__FUNCTION__, __LINE__);
vector<TraitData*>::iterator itr;

View file

@ -22,13 +22,16 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
#define __Traits__
#include <vector>
#include <map>
#include "../../common/Mutex.h"
#include "../../common/types.h"
#include "../../common/EQPacket.h"
#include "../client.h"
class Client;
struct TraitData
{
int32 spellID ;
int32 spellID;
int8 level;
int8 classReq;
int8 raceReq;
@ -38,6 +41,7 @@ struct TraitData
bool isTraining;
int8 tier;
int8 group;
int32 item_id;
};
#define TRAITS_ATTRIBUTES 0
@ -52,7 +56,14 @@ class MasterTraitList
public:
MasterTraitList();
~MasterTraitList();
bool IdentifyNextTrait(Client* client, map <int8, vector<TraitData*> >* traitList, vector<TraitData*>* collectTraits, vector<TraitData*>* tieredTraits, std::map<int32, int8>* previousMatchedSpells, bool omitFoundMatches = false);
bool ChooseNextTrait(Client* client);
int16 GetSpellCount(Client* client, map <int8, vector<TraitData*> >* traits, bool onlyCharTraits = false);
bool IsPlayerAllowedTrait(Client* client, TraitData* trait);
bool GenerateTraitLists(Client* client, map <int8, map <int8, vector<TraitData*> > >* sortedTraitList, map <int8, vector<TraitData*> >* classTraining,
map <int8, vector<TraitData*> >* raceTraits, map <int8, vector<TraitData*> >* innateRaceTraits, map <int8, vector<TraitData*> >* focusEffects, int16 max_level = 0, int8 trait_group = 255);
/// <summary>Sorts the traits for the given client and creats and sends the trait packet.</summary>
/// <param name='client'>The Client calling this function</param>
/// <returns>EQ2Packet*</returns>
@ -68,6 +79,10 @@ public:
/// <summary>Get the trait data for the given spell.</summary>
/// <param name='spellID'>Spell ID to get trait data for.</param>
TraitData* GetTrait(int32 spellID);
/// <summary>Get the trait data for the given item.</summary>
/// <param name='itemID'>Item ID to map to the trait data.</param>
TraitData* GetTraitByItemID(int32 itemID);
/// <summary>Empties the master trait list</summary>
void DestroyTraits();

View file

@ -4735,7 +4735,7 @@ void WorldDatabase::LoadTraits(){
MYSQL_ROW row;
TraitData* trait;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`, `level`, `class_req`, `race_req`, `isTrait`,`isInate`, `isFocusEffect`, `isTraining`,`tier`, `group` FROM spell_traits");
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`, `level`, `class_req`, `race_req`, `isTrait`,`isInate`, `isFocusEffect`, `isTraining`,`tier`, `group`, `item_id` FROM spell_traits where is_active = 1");
while (result && (row = mysql_fetch_row(result))){
trait = new TraitData;
int8 i = 0;
@ -4749,6 +4749,7 @@ void WorldDatabase::LoadTraits(){
trait->isTraining = (atoi(row[(++i)]) == 0) ? false : true;
trait->tier = atoi(row[(++i)]);
trait->group = atoi(row[(++i)]);
trait->item_id = atoul(row[(++i)]);
master_trait_list.AddTrait(trait);
}

View file

@ -1526,9 +1526,17 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
break;
}
case OP_DoneLoadingUIResourcesMsg: {
if(GetVersion() < 546) {
if(GetVersion() <= 547) {
ClientPacketFunctions::SendUpdateSpellBook(this);
}
// need to quickly flash the DoF client the rest of their inventory
if(GetVersion() <= 547) {
EQ2Packet* item_app = player->GetPlayerItemList()->serialize(GetPlayer(), GetVersion());
if (item_app) {
QueuePacket(item_app);
}
}
EQ2Packet* app = new EQ2Packet(OP_DoneLoadingUIResourcesMsg, 0, 0);
QueuePacket(app);
if(!player_loading_complete)
@ -1556,6 +1564,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
SetReadyForSpawns(true);
player->CalculateApplyWeight();
SendCharInfo();
GetPlayer()->GetZone()->GetSpellProcess()->SendSpellBookUpdate(this);
pos_update.Start();
quest_pos_timer.Start();
break;
@ -1869,6 +1878,12 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
player_pos_changed = true;
GetPlayer()->AddChangedZoneSpawn();
ProcessZoneIgnoreWidgets();
if (version <= 547) {
master_trait_list.ChooseNextTrait(this);
}
if (GetPlayer()->GetHP() < GetPlayer()->GetTotalHP() || GetPlayer()->GetPower() < GetPlayer()->GetTotalPower())
GetCurrentZone()->AddDamagedSpawn(GetPlayer());
}
else {
LogWrite(CCLIENT__WARNING, 0, "Client", "Player %s reported SysClient/SignalMsg state %s.", GetPlayer()->GetName(), str.data.c_str());
@ -5157,10 +5172,14 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) {
// Also need to force the char sheet update or else there can be a large delay from when you level
// to when you are actually able to select traits.
QueuePacket(GetPlayer()->GetPlayerInfo()->serialize(GetVersion()));
QueuePacket(master_trait_list.GetTraitListPacket(this));
if (version > 546)
GetPlayer()->need_trait_update = true;
if (version > 547) {
QueuePacket(master_trait_list.GetTraitListPacket(this));
master_aa_list.DisplayAA(this, 0, 0);
}
else
master_trait_list.ChooseNextTrait(this);
if (GetPlayer()->SpawnedBots.size() > 0) {
map<int32, int32>::iterator itr;
@ -5170,6 +5189,9 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) {
((Bot*)bot)->ChangeLevel(old_level, new_level);
}
}
if (GetPlayer()->GetHP() < GetPlayer()->GetTotalHP() || GetPlayer()->GetPower() < GetPlayer()->GetTotalPower())
GetPlayer()->GetZone()->AddDamagedSpawn(GetPlayer());
}
void Client::ChangeTSLevel(int16 old_level, int16 new_level) {
@ -9763,7 +9785,7 @@ void Client::SetReadyForUpdates() {
ready_for_updates = true;
if(GetVersion() <= 546) {
if(GetVersion() <= 547) {
SendRecipeList();
}
}

View file

@ -443,9 +443,9 @@
<Struct Name="Substruct_SpawnInfoStruct" ClientVersion="546" >
<Data ElementName="hp_remaining" Type="int32" Size="1" /> <!-- 0 -->
<Data ElementName="power_percent" Type="int32" Size="1" /> <!-- 4 -->
<Data ElementName="spells" Substruct="Substruct_TargetSpellEffects" Size="30" /> <!-- 8 -->
<Data ElementName="follow_target" Type="int32" Size="1" /> <!-- 278 -->
<Data ElementName="spell_effects" Substruct="Substruct_TargetSpellEffects" Size="30" /> <!-- 8 -->
<Data ElementName="target_id" Type="int32" Size="1" /> <!-- 282 -->
<Data ElementName="follow_target" Type="int32" Size="1" /> <!-- 278 -->
<Data ElementName="unknown5" Type="int8" Size="8" /> <!-- 286 -->
<Data ElementName="corpse" Type="int8" Size="1" /> <!-- 295 -->
<Data ElementName="class" Type="int8" Size="1" /> <!-- 296 -->