diff --git a/DB/updates/may2024_classictraits_personalandracial.sql b/DB/updates/may2024_classictraits_personalandracial.sql new file mode 100644 index 000000000..021e637f6 --- /dev/null +++ b/DB/updates/may2024_classictraits_personalandracial.sql @@ -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; diff --git a/EQ2/source/WorldServer/Combat.cpp b/EQ2/source/WorldServer/Combat.cpp index 2155d90a8..ba9e6e1d1 100644 --- a/EQ2/source/WorldServer/Combat.cpp +++ b/EQ2/source/WorldServer/Combat.cpp @@ -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(damage) - dmgReduction; + if(newDamage < 0.0f) + newDamage = 0.0f; + damage = static_cast(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; } \ No newline at end of file diff --git a/EQ2/source/WorldServer/Commands/Commands.cpp b/EQ2/source/WorldServer/Commands/Commands.cpp index 5c6d54a11..a36330301 100644 --- a/EQ2/source/WorldServer/Commands/Commands.cpp +++ b/EQ2/source/WorldServer/Commands/Commands.cpp @@ -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()); diff --git a/EQ2/source/WorldServer/Entity.cpp b/EQ2/source/WorldServer/Entity.cpp index 673d53015..3543430e3 100644 --- a/EQ2/source/WorldServer/Entity.cpp +++ b/EQ2/source/WorldServer/Entity.cpp @@ -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); } \ No newline at end of file diff --git a/EQ2/source/WorldServer/Entity.h b/EQ2/source/WorldServer/Entity.h index b81d56e66..9b9b36bb6 100644 --- a/EQ2/source/WorldServer/Entity.h +++ b/EQ2/source/WorldServer/Entity.h @@ -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 lk(classMutex); return name_; } @@ -706,6 +722,9 @@ struct InfoStruct{ std::string get_combat_action_state() { std::lock_guard lk(classMutex); return combat_action_state_; } + float get_max_spell_reduction() { std::lock_guard lk(classMutex); return max_spell_reduction_; } + int8 get_max_spell_reduction_override() { std::lock_guard lk(classMutex); return max_spell_reduction_override_; } + void set_name(std::string value) { std::lock_guard lk(classMutex); name_ = value; } void set_deity(std::string value) { std::lock_guard lk(classMutex); deity_ = value; } @@ -1014,6 +1033,10 @@ struct InfoStruct{ void set_combat_action_state(std::string value) { std::lock_guard lk(classMutex); combat_action_state_ = value; } + void set_max_spell_reduction(float value) { std::lock_guard lk(classMutex); max_spell_reduction_ = value; } + + void set_max_spell_reduction_override(int8 value) { std::lock_guard 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; diff --git a/EQ2/source/WorldServer/Items/Items.cpp b/EQ2/source/WorldServer/Items/Items.cpp index 97b030fbc..87cc4b923 100644 --- a/EQ2/source/WorldServer/Items/Items.cpp +++ b/EQ2/source/WorldServer/Items/Items.cpp @@ -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 diff --git a/EQ2/source/WorldServer/Items/Items.h b/EQ2/source/WorldServer/Items/Items.h index f061d6f7d..17edc2c21 100644 --- a/EQ2/source/WorldServer/Items/Items.h +++ b/EQ2/source/WorldServer/Items/Items.h @@ -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: diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index 3c0726f6e..ae3388b4f 100755 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/EQ2/source/WorldServer/LuaFunctions.cpp @@ -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; diff --git a/EQ2/source/WorldServer/Player.cpp b/EQ2/source/WorldServer/Player.cpp index a5ec33d99..b5ddcd043 100644 --- a/EQ2/source/WorldServer/Player.cpp +++ b/EQ2/source/WorldServer/Player.cpp @@ -122,6 +122,13 @@ Player::Player(){ all_spells_locked = false; current_language_id = 0; active_reward = false; + + SortedTraitList = new map > >; + ClassTraining = new map >; + RaceTraits = new map >; + InnateRaceTraits = new map >; + FocusEffects = new map >; + 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;islot >= 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;ispell_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;islot < 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) { diff --git a/EQ2/source/WorldServer/Player.h b/EQ2/source/WorldServer/Player.h index 5814c2b32..2606cab96 100644 --- a/EQ2/source/WorldServer/Player.h +++ b/EQ2/source/WorldServer/Player.h @@ -32,6 +32,7 @@ #include "Titles.h" #include "Languages.h" #include "Achievements/Achievements.h" +#include "Traits/Traits.h" #include #include @@ -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; } @@ -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 > >* SortedTraitList; + map >* ClassTraining; + map >* RaceTraits; + map >* InnateRaceTraits; + map >* FocusEffects; + mutable std::shared_mutex trait_mutex; + std::atomic need_trait_update; private: bool reset_mentorship; bool range_attack; diff --git a/EQ2/source/WorldServer/Rules/Rules.cpp b/EQ2/source/WorldServer/Rules/Rules.cpp index aba964e51..8568e2b59 100644 --- a/EQ2/source/WorldServer/Rules/Rules.cpp +++ b/EQ2/source/WorldServer/Rules/Rules.cpp @@ -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"); diff --git a/EQ2/source/WorldServer/Rules/Rules.h b/EQ2/source/WorldServer/Rules/Rules.h index b16c1fdc6..f585a7f28 100644 --- a/EQ2/source/WorldServer/Rules/Rules.h +++ b/EQ2/source/WorldServer/Rules/Rules.h @@ -87,6 +87,12 @@ enum RuleType { CoinWeightPerStone, WeightInflictsSpeed, LevelMasterySkillMultiplier, + TraitTieringSelection, + ClassicTraitLevelTable, + TraitFocusSelectLevel, + TraitTrainingSelectLevel, + TraitRaceSelectLevel, + TraitCharacterSelectLevel, /* PVP */ AllowPVP, diff --git a/EQ2/source/WorldServer/Spawn.cpp b/EQ2/source/WorldServer/Spawn.cpp index 1d48edb28..46cf4926e 100644 --- a/EQ2/source/WorldServer/Spawn.cpp +++ b/EQ2/source/WorldServer/Spawn.cpp @@ -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* 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(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; } \ No newline at end of file diff --git a/EQ2/source/WorldServer/Spawn.h b/EQ2/source/WorldServer/Spawn.h index a66ba240b..6681d1183 100644 --- a/EQ2/source/WorldServer/Spawn.h +++ b/EQ2/source/WorldServer/Spawn.h @@ -1383,6 +1383,12 @@ public: bool IsItemInLootTier(Item* item); void DistributeGroupLoot_RoundRobin(std::vector* 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 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 is_alive; + std::atomic 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 diff --git a/EQ2/source/WorldServer/SpellProcess.cpp b/EQ2/source/WorldServer/SpellProcess.cpp index cf2dc9292..807d233a3 100644 --- a/EQ2/source/WorldServer/SpellProcess.cpp +++ b/EQ2/source/WorldServer/SpellProcess.cpp @@ -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); diff --git a/EQ2/source/WorldServer/Traits/Traits.cpp b/EQ2/source/WorldServer/Traits/Traits.cpp index 81029496e..f0c340d00 100644 --- a/EQ2/source/WorldServer/Traits/Traits.cpp +++ b/EQ2/source/WorldServer/Traits/Traits.cpp @@ -23,11 +23,18 @@ along with EQ2Emulator. If not, see . #include "../../common/Log.h" #include "../Spells.h" #include "../WorldDatabase.h" +#include "../client.h" +#include "../Rules/Rules.h" +#include + #include 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 > >* sortedTraitList, map >* classTraining, + map >* raceTraits, map >* innateRaceTraits, map >* 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 > > SortedTraitList; + if (Size() == 0 || !sortedTraitList || !classTraining || !raceTraits || !innateRaceTraits || !focusEffects) + return false; + map > >::iterator itr; map >::iterator itr2; vector::iterator itr3; - map > ClassTraining; - map > RaceTraits; - map > InnateRaceTraits; - map > 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 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 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 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 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 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 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 >* traitList, vector* collectTraits, vector* tieredTraits, std::map* 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 >::iterator itr2; + vector::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::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 > >* SortedTraitList = new map > >; + map > >::iterator itr; + bool tiered_selection = rule_manager.GetGlobalRule(R_Player, TraitTieringSelection)->GetBool(); + vector::iterator itr3; + + map >* ClassTraining = new map >; + map >* RaceTraits = new map >; + map >* InnateRaceTraits = new map >; + map >* FocusEffects = new map >; + vector* collectTraits = new vector; + vector* tieredTraits = new vector; + std::map* previousMatchedSpells = new std::map; + bool match = false; + if(GenerateTraitLists(client, SortedTraitList, ClassTraining, RaceTraits, InnateRaceTraits, FocusEffects, client->GetPlayer()->GetLevel())) { + + vector* 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 >* traits, bool onlyCharTraits) { + if(!traits) + return 0; + + int16 count = 0; + map >::iterator itr2; + vector::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 PersonalTraitLevelLimits = boost::assign::list_of(0)(8)(14)(22)(28)(36)(42)(46)(48); +vector TrainingTraitLevelLimits = boost::assign::list_of(0)(10)(20)(30)(40)(50); +vector RacialTraitLevelLimits = boost::assign::list_of(0)(18)(26)(34)(44); +vector 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 don’t 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 > >* SortedTraitList = client->GetPlayer()->SortedTraitList; + map > >::iterator itr; + map >::iterator itr2; + vector::iterator itr3; + bool use_classic_table = rule_manager.GetGlobalRule(R_Player, ClassicTraitLevelTable)->GetBool(); + + map >* ClassTraining = client->GetPlayer()->ClassTraining; + map >* RaceTraits = client->GetPlayer()->RaceTraits; + map >* InnateRaceTraits = client->GetPlayer()->InnateRaceTraits; + map >* 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 > >* SortedTraitList = client->GetPlayer()->SortedTraitList; + map > >::iterator itr; + map >::iterator itr2; + vector::iterator itr3; + + map >* ClassTraining = client->GetPlayer()->ClassTraining; + map >* RaceTraits = client->GetPlayer()->RaceTraits; + map >* InnateRaceTraits = client->GetPlayer()->InnateRaceTraits; + map >* 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::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::iterator itr; diff --git a/EQ2/source/WorldServer/Traits/Traits.h b/EQ2/source/WorldServer/Traits/Traits.h index 31213f657..678a59c2b 100644 --- a/EQ2/source/WorldServer/Traits/Traits.h +++ b/EQ2/source/WorldServer/Traits/Traits.h @@ -22,13 +22,16 @@ along with EQ2Emulator. If not, see . #define __Traits__ #include +#include +#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 >* traitList, vector* collectTraits, vector* tieredTraits, std::map* previousMatchedSpells, bool omitFoundMatches = false); + bool ChooseNextTrait(Client* client); + int16 GetSpellCount(Client* client, map >* traits, bool onlyCharTraits = false); + bool IsPlayerAllowedTrait(Client* client, TraitData* trait); + bool GenerateTraitLists(Client* client, map > >* sortedTraitList, map >* classTraining, + map >* raceTraits, map >* innateRaceTraits, map >* focusEffects, int16 max_level = 0, int8 trait_group = 255); + /// Sorts the traits for the given client and creats and sends the trait packet. /// The Client calling this function /// EQ2Packet* @@ -68,6 +79,10 @@ public: /// Get the trait data for the given spell. /// Spell ID to get trait data for. TraitData* GetTrait(int32 spellID); + + /// Get the trait data for the given item. + /// Item ID to map to the trait data. + TraitData* GetTraitByItemID(int32 itemID); /// Empties the master trait list void DestroyTraits(); diff --git a/EQ2/source/WorldServer/WorldDatabase.cpp b/EQ2/source/WorldServer/WorldDatabase.cpp index f096f5fbd..09d26a07d 100644 --- a/EQ2/source/WorldServer/WorldDatabase.cpp +++ b/EQ2/source/WorldServer/WorldDatabase.cpp @@ -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); } diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index ac78d95eb..b2556513e 100755 --- a/EQ2/source/WorldServer/client.cpp +++ b/EQ2/source/WorldServer/client.cpp @@ -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::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(); } } diff --git a/server/SpawnStructs.xml b/server/SpawnStructs.xml index 7a29060d0..1afdea9a5 100644 --- a/server/SpawnStructs.xml +++ b/server/SpawnStructs.xml @@ -443,9 +443,9 @@ - - + +