From b0d4377f9f639694da115a37e6f61fa26e9aa719 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 1 Mar 2020 01:20:40 -0300 Subject: [PATCH 001/235] Developer console seems broken. Added command line options to generate and validate the definesvalidator.h --- Doc/HowTo/DefinesValidator.txt | 4 + Main/Include/definesvalidator.h | 466 ++++++++++++++++++++++++++++++-- Main/Source/main.cpp | 16 ++ 3 files changed, 457 insertions(+), 29 deletions(-) diff --git a/Doc/HowTo/DefinesValidator.txt b/Doc/HowTo/DefinesValidator.txt index 0b2e9b97e..cae7eb51b 100644 --- a/Doc/HowTo/DefinesValidator.txt +++ b/Doc/HowTo/DefinesValidator.txt @@ -16,3 +16,7 @@ or when someone changes a value only at `define.dat` or at some c++ .h file, so in case c++ has no such define, it will be ignored ex.: the check will be performed only `#ifdef EMISSARY` (currently not used in c++ .h files) 7) run any game, go to the console ctrl+` and run the validator, it will abort complaining about TORSO value with this message: "Defined TORSO with value 2 from .dat file mismatches hardcoded c++ define value of 1!" + +Ps.: alternatively (instead of using developer console), you can run this command line commands: +./ivan --defgen # that creates definesvalidator.h +./ivan --defval # to validate it diff --git a/Main/Include/definesvalidator.h b/Main/Include/definesvalidator.h index 38848c095..78a091617 100644 --- a/Main/Include/definesvalidator.h +++ b/Main/Include/definesvalidator.h @@ -56,6 +56,14 @@ class definesvalidator{ #endif +#ifdef ACID_GAS // DO NOT MODIFY! + bsA = 12303; + bsB = ACID_GAS; + if(bsA!=bsB) + ssErrors << "Defined ACID_GAS with value 12303 from .dat file mismatches hardcoded c++ define value of " << ACID_GAS << "!" << std::endl; +#endif + + #ifdef ACM // DO NOT MODIFY! bsA = 9; bsB = ACM; @@ -352,6 +360,14 @@ class definesvalidator{ #endif +#ifdef ANGEL_TEAM // DO NOT MODIFY! + bsA = 4; + bsB = ANGEL_TEAM; + if(bsA!=bsB) + ssErrors << "Defined ANGEL_TEAM with value 4 from .dat file mismatches hardcoded c++ define value of " << ANGEL_TEAM << "!" << std::endl; +#endif + + #ifdef ANM // DO NOT MODIFY! bsA = 6; bsB = ANM; @@ -417,10 +433,10 @@ class definesvalidator{ #ifdef ARANEA // DO NOT MODIFY! - bsA = 3; + bsA = 5; bsB = ARANEA; if(bsA!=bsB) - ssErrors << "Defined ARANEA with value 3 from .dat file mismatches hardcoded c++ define value of " << ARANEA << "!" << std::endl; + ssErrors << "Defined ARANEA with value 5 from .dat file mismatches hardcoded c++ define value of " << ARANEA << "!" << std::endl; #endif @@ -512,6 +528,22 @@ class definesvalidator{ #endif +#ifdef ASLONA_CASTLE // DO NOT MODIFY! + bsA = 8; + bsB = ASLONA_CASTLE; + if(bsA!=bsB) + ssErrors << "Defined ASLONA_CASTLE with value 8 from .dat file mismatches hardcoded c++ define value of " << ASLONA_CASTLE << "!" << std::endl; +#endif + + +#ifdef ASLONA_TEAM // DO NOT MODIFY! + bsA = 17; + bsB = ASLONA_TEAM; + if(bsA!=bsB) + ssErrors << "Defined ASLONA_TEAM with value 17 from .dat file mismatches hardcoded c++ define value of " << ASLONA_TEAM << "!" << std::endl; +#endif + + #ifdef ASPHALT // DO NOT MODIFY! bsA = 16415; bsB = ASPHALT; @@ -1056,6 +1088,14 @@ class definesvalidator{ #endif +#ifdef BLACK_BLOOD // DO NOT MODIFY! + bsA = 16441; + bsB = BLACK_BLOOD; + if(bsA!=bsB) + ssErrors << "Defined BLACK_BLOOD with value 16441 from .dat file mismatches hardcoded c++ define value of " << BLACK_BLOOD << "!" << std::endl; +#endif + + #ifdef BLACK_DIAMOND // DO NOT MODIFY! bsA = 4299; bsB = BLACK_DIAMOND; @@ -1096,6 +1136,14 @@ class definesvalidator{ #endif +#ifdef BLACK_MARKET // DO NOT MODIFY! + bsA = 7; + bsB = BLACK_MARKET; + if(bsA!=bsB) + ssErrors << "Defined BLACK_MARKET with value 7 from .dat file mismatches hardcoded c++ define value of " << BLACK_MARKET << "!" << std::endl; +#endif + + #ifdef BLACK_SAND // DO NOT MODIFY! bsA = 24582; bsB = BLACK_SAND; @@ -1120,6 +1168,14 @@ class definesvalidator{ #endif +#ifdef BLOAT // DO NOT MODIFY! + bsA = 4; + bsB = BLOAT; + if(bsA!=bsB) + ssErrors << "Defined BLOAT with value 4 from .dat file mismatches hardcoded c++ define value of " << BLOAT << "!" << std::endl; +#endif + + #ifdef BLOOD // DO NOT MODIFY! bsA = 16419; bsB = BLOOD; @@ -1192,6 +1248,14 @@ class definesvalidator{ #endif +#ifdef BLUE_SNAKE // DO NOT MODIFY! + bsA = 3; + bsB = BLUE_SNAKE; + if(bsA!=bsB) + ssErrors << "Defined BLUE_SNAKE with value 3 from .dat file mismatches hardcoded c++ define value of " << BLUE_SNAKE << "!" << std::endl; +#endif + + #ifdef BLUNT // DO NOT MODIFY! bsA = 1; bsB = BLUNT; @@ -1304,6 +1368,14 @@ class definesvalidator{ #endif +#ifdef BOOT_OF_DISPLACEMENT // DO NOT MODIFY! + bsA = 4; + bsB = BOOT_OF_DISPLACEMENT; + if(bsA!=bsB) + ssErrors << "Defined BOOT_OF_DISPLACEMENT with value 4 from .dat file mismatches hardcoded c++ define value of " << BOOT_OF_DISPLACEMENT << "!" << std::endl; +#endif + + #ifdef BOOT_OF_KICKING // DO NOT MODIFY! bsA = 3; bsB = BOOT_OF_KICKING; @@ -1472,14 +1544,6 @@ class definesvalidator{ #endif -#ifdef Base // DO NOT MODIFY! - bsA = 0; - bsB = Base; - if(bsA!=bsB) - ssErrors << "Defined Base with value 0 from .dat file mismatches hardcoded c++ define value of " << Base << "!" << std::endl; -#endif - - #ifdef CACTUS // DO NOT MODIFY! bsA = 20; bsB = CACTUS; @@ -1608,6 +1672,14 @@ class definesvalidator{ #endif +#ifdef CASTLE // DO NOT MODIFY! + bsA = 17; + bsB = CASTLE; + if(bsA!=bsB) + ssErrors << "Defined CASTLE with value 17 from .dat file mismatches hardcoded c++ define value of " << CASTLE << "!" << std::endl; +#endif + + #ifdef CAT_FLESH // DO NOT MODIFY! bsA = 20504; bsB = CAT_FLESH; @@ -2177,10 +2249,10 @@ class definesvalidator{ #ifdef CRAZED_FARMER // DO NOT MODIFY! - bsA = 3; + bsA = 4; bsB = CRAZED_FARMER; if(bsA!=bsB) - ssErrors << "Defined CRAZED_FARMER with value 3 from .dat file mismatches hardcoded c++ define value of " << CRAZED_FARMER << "!" << std::endl; + ssErrors << "Defined CRAZED_FARMER with value 4 from .dat file mismatches hardcoded c++ define value of " << CRAZED_FARMER << "!" << std::endl; #endif @@ -2216,6 +2288,14 @@ class definesvalidator{ #endif +#ifdef CRYSTEEL // DO NOT MODIFY! + bsA = 4328; + bsB = CRYSTEEL; + if(bsA!=bsB) + ssErrors << "Defined CRYSTEEL with value 4328 from .dat file mismatches hardcoded c++ define value of " << CRYSTEEL << "!" << std::endl; +#endif + + #ifdef CT_BONE // DO NOT MODIFY! bsA = 32; bsB = CT_BONE; @@ -2233,10 +2313,10 @@ class definesvalidator{ #ifdef CT_GAS // DO NOT MODIFY! - bsA = 512; + bsA = 1024; bsB = CT_GAS; if(bsA!=bsB) - ssErrors << "Defined CT_GAS with value 512 from .dat file mismatches hardcoded c++ define value of " << CT_GAS << "!" << std::endl; + ssErrors << "Defined CT_GAS with value 1024 from .dat file mismatches hardcoded c++ define value of " << CT_GAS << "!" << std::endl; #endif @@ -2248,6 +2328,14 @@ class definesvalidator{ #endif +#ifdef CT_MAGIC // DO NOT MODIFY! + bsA = 2048; + bsB = CT_MAGIC; + if(bsA!=bsB) + ssErrors << "Defined CT_MAGIC with value 2048 from .dat file mismatches hardcoded c++ define value of " << CT_MAGIC << "!" << std::endl; +#endif + + #ifdef CT_MEAT // DO NOT MODIFY! bsA = 2; bsB = CT_MEAT; @@ -2272,19 +2360,27 @@ class definesvalidator{ #endif -#ifdef CT_MISC_ORGANIC // DO NOT MODIFY! +#ifdef CT_MISC_ANIMAL // DO NOT MODIFY! + bsA = 256; + bsB = CT_MISC_ANIMAL; + if(bsA!=bsB) + ssErrors << "Defined CT_MISC_ANIMAL with value 256 from .dat file mismatches hardcoded c++ define value of " << CT_MISC_ANIMAL << "!" << std::endl; +#endif + + +#ifdef CT_MISC_PLANT // DO NOT MODIFY! bsA = 128; - bsB = CT_MISC_ORGANIC; + bsB = CT_MISC_PLANT; if(bsA!=bsB) - ssErrors << "Defined CT_MISC_ORGANIC with value 128 from .dat file mismatches hardcoded c++ define value of " << CT_MISC_ORGANIC << "!" << std::endl; + ssErrors << "Defined CT_MISC_PLANT with value 128 from .dat file mismatches hardcoded c++ define value of " << CT_MISC_PLANT << "!" << std::endl; #endif #ifdef CT_PLASTIC // DO NOT MODIFY! - bsA = 256; + bsA = 512; bsB = CT_PLASTIC; if(bsA!=bsB) - ssErrors << "Defined CT_PLASTIC with value 256 from .dat file mismatches hardcoded c++ define value of " << CT_PLASTIC << "!" << std::endl; + ssErrors << "Defined CT_PLASTIC with value 512 from .dat file mismatches hardcoded c++ define value of " << CT_PLASTIC << "!" << std::endl; #endif @@ -2297,10 +2393,10 @@ class definesvalidator{ #ifdef CULTIST // DO NOT MODIFY! - bsA = 2; + bsA = 3; bsB = CULTIST; if(bsA!=bsB) - ssErrors << "Defined CULTIST with value 2 from .dat file mismatches hardcoded c++ define value of " << CULTIST << "!" << std::endl; + ssErrors << "Defined CULTIST with value 3 from .dat file mismatches hardcoded c++ define value of " << CULTIST << "!" << std::endl; #endif @@ -2312,6 +2408,14 @@ class definesvalidator{ #endif +#ifdef CURTAIN // DO NOT MODIFY! + bsA = 3; + bsB = CURTAIN; + if(bsA!=bsB) + ssErrors << "Defined CURTAIN with value 3 from .dat file mismatches hardcoded c++ define value of " << CURTAIN << "!" << std::endl; +#endif + + #ifdef CUSTARD // DO NOT MODIFY! bsA = 16392; bsB = CUSTARD; @@ -2368,6 +2472,14 @@ class definesvalidator{ #endif +#ifdef DARK_FOREST // DO NOT MODIFY! + bsA = 15; + bsB = DARK_FOREST; + if(bsA!=bsB) + ssErrors << "Defined DARK_FOREST with value 15 from .dat file mismatches hardcoded c++ define value of " << DARK_FOREST << "!" << std::endl; +#endif + + #ifdef DARK_FROG_BLOOD // DO NOT MODIFY! bsA = 16425; bsB = DARK_FROG_BLOOD; @@ -2536,6 +2648,14 @@ class definesvalidator{ #endif +#ifdef DIRE // DO NOT MODIFY! + bsA = 1; + bsB = DIRE; + if(bsA!=bsB) + ssErrors << "Defined DIRE with value 1 from .dat file mismatches hardcoded c++ define value of " << DIRE << "!" << std::endl; +#endif + + #ifdef DIRT // DO NOT MODIFY! bsA = 24583; bsB = DIRT; @@ -2720,6 +2840,14 @@ class definesvalidator{ #endif +#ifdef EFFECT_ACID_GAS // DO NOT MODIFY! + bsA = 44; + bsB = EFFECT_ACID_GAS; + if(bsA!=bsB) + ssErrors << "Defined EFFECT_ACID_GAS with value 44 from .dat file mismatches hardcoded c++ define value of " << EFFECT_ACID_GAS << "!" << std::endl; +#endif + + #ifdef EFFECT_ANTIDOTE // DO NOT MODIFY! bsA = 9; bsB = EFFECT_ANTIDOTE; @@ -2768,6 +2896,14 @@ class definesvalidator{ #endif +#ifdef EFFECT_FIRE_GAS // DO NOT MODIFY! + bsA = 45; + bsB = EFFECT_FIRE_GAS; + if(bsA!=bsB) + ssErrors << "Defined EFFECT_FIRE_GAS with value 45 from .dat file mismatches hardcoded c++ define value of " << EFFECT_FIRE_GAS << "!" << std::endl; +#endif + + #ifdef EFFECT_GOOD_WONDER_STAFF_VAPOUR // DO NOT MODIFY! bsA = 18; bsB = EFFECT_GOOD_WONDER_STAFF_VAPOUR; @@ -2816,6 +2952,14 @@ class definesvalidator{ #endif +#ifdef EFFECT_LAUGH // DO NOT MODIFY! + bsA = 39; + bsB = EFFECT_LAUGH; + if(bsA!=bsB) + ssErrors << "Defined EFFECT_LAUGH with value 39 from .dat file mismatches hardcoded c++ define value of " << EFFECT_LAUGH << "!" << std::endl; +#endif + + #ifdef EFFECT_LYCANTHROPY // DO NOT MODIFY! bsA = 7; bsB = EFFECT_LYCANTHROPY; @@ -2952,6 +3096,14 @@ class definesvalidator{ #endif +#ifdef EFFECT_PHASE // DO NOT MODIFY! + bsA = 43; + bsB = EFFECT_PHASE; + if(bsA!=bsB) + ssErrors << "Defined EFFECT_PHASE with value 43 from .dat file mismatches hardcoded c++ define value of " << EFFECT_PHASE << "!" << std::endl; +#endif + + #ifdef EFFECT_POISON // DO NOT MODIFY! bsA = 1; bsB = EFFECT_POISON; @@ -2960,6 +3112,14 @@ class definesvalidator{ #endif +#ifdef EFFECT_POLYJUICE // DO NOT MODIFY! + bsA = 40; + bsB = EFFECT_POLYJUICE; + if(bsA!=bsB) + ssErrors << "Defined EFFECT_POLYJUICE with value 40 from .dat file mismatches hardcoded c++ define value of " << EFFECT_POLYJUICE << "!" << std::endl; +#endif + + #ifdef EFFECT_POLYMORPH // DO NOT MODIFY! bsA = 11; bsB = EFFECT_POLYMORPH; @@ -2968,6 +3128,14 @@ class definesvalidator{ #endif +#ifdef EFFECT_PUKE // DO NOT MODIFY! + bsA = 41; + bsB = EFFECT_PUKE; + if(bsA!=bsB) + ssErrors << "Defined EFFECT_PUKE with value 41 from .dat file mismatches hardcoded c++ define value of " << EFFECT_PUKE << "!" << std::endl; +#endif + + #ifdef EFFECT_REGENERATION // DO NOT MODIFY! bsA = 37; bsB = EFFECT_REGENERATION; @@ -2984,6 +3152,14 @@ class definesvalidator{ #endif +#ifdef EFFECT_SICKNESS // DO NOT MODIFY! + bsA = 42; + bsB = EFFECT_SICKNESS; + if(bsA!=bsB) + ssErrors << "Defined EFFECT_SICKNESS with value 42 from .dat file mismatches hardcoded c++ define value of " << EFFECT_SICKNESS << "!" << std::endl; +#endif + + #ifdef EFFECT_SKUNK_SMELL // DO NOT MODIFY! bsA = 13; bsB = EFFECT_SKUNK_SMELL; @@ -3368,6 +3544,14 @@ class definesvalidator{ #endif +#ifdef FIRE_GAS // DO NOT MODIFY! + bsA = 12304; + bsB = FIRE_GAS; + if(bsA!=bsB) + ssErrors << "Defined FIRE_GAS with value 12304 from .dat file mismatches hardcoded c++ define value of " << FIRE_GAS << "!" << std::endl; +#endif + + #ifdef FIR_WOOD // DO NOT MODIFY! bsA = 4239; bsB = FIR_WOOD; @@ -3400,6 +3584,14 @@ class definesvalidator{ #endif +#ifdef FLAWLESS_CRYSTEEL // DO NOT MODIFY! + bsA = 4329; + bsB = FLAWLESS_CRYSTEEL; + if(bsA!=bsB) + ssErrors << "Defined FLAWLESS_CRYSTEEL with value 4329 from .dat file mismatches hardcoded c++ define value of " << FLAWLESS_CRYSTEEL << "!" << std::endl; +#endif + + #ifdef FLEE_FROM_ENEMIES // DO NOT MODIFY! bsA = 2; bsB = FLEE_FROM_ENEMIES; @@ -3552,6 +3744,14 @@ class definesvalidator{ #endif +#ifdef FUNGAL_CAVE // DO NOT MODIFY! + bsA = 11; + bsB = FUNGAL_CAVE; + if(bsA!=bsB) + ssErrors << "Defined FUNGAL_CAVE with value 11 from .dat file mismatches hardcoded c++ define value of " << FUNGAL_CAVE << "!" << std::endl; +#endif + + #ifdef FUNGI_WOOD // DO NOT MODIFY! bsA = 4226; bsB = FUNGI_WOOD; @@ -3568,6 +3768,14 @@ class definesvalidator{ #endif +#ifdef FUSANGA_LEVEL // DO NOT MODIFY! + bsA = 3; + bsB = FUSANGA_LEVEL; + if(bsA!=bsB) + ssErrors << "Defined FUSANGA_LEVEL with value 3 from .dat file mismatches hardcoded c++ define value of " << FUSANGA_LEVEL << "!" << std::endl; +#endif + + #ifdef GALVORN // DO NOT MODIFY! bsA = 4111; bsB = GALVORN; @@ -3648,6 +3856,14 @@ class definesvalidator{ #endif +#ifdef GIANT_GOLD // DO NOT MODIFY! + bsA = 4; + bsB = GIANT_GOLD; + if(bsA!=bsB) + ssErrors << "Defined GIANT_GOLD with value 4 from .dat file mismatches hardcoded c++ define value of " << GIANT_GOLD << "!" << std::endl; +#endif + + #ifdef GIANT_LIGHT // DO NOT MODIFY! bsA = 6; bsB = GIANT_LIGHT; @@ -3712,6 +3928,14 @@ class definesvalidator{ #endif +#ifdef GOBLIN_FORT // DO NOT MODIFY! + bsA = 10; + bsB = GOBLIN_FORT; + if(bsA!=bsB) + ssErrors << "Defined GOBLIN_FORT with value 10 from .dat file mismatches hardcoded c++ define value of " << GOBLIN_FORT << "!" << std::endl; +#endif + + #ifdef GOLD // DO NOT MODIFY! bsA = 4115; bsB = GOLD; @@ -3936,6 +4160,14 @@ class definesvalidator{ #endif +#ifdef GREEN_SNAKE // DO NOT MODIFY! + bsA = 2; + bsB = GREEN_SNAKE; + if(bsA!=bsB) + ssErrors << "Defined GREEN_SNAKE with value 2 from .dat file mismatches hardcoded c++ define value of " << GREEN_SNAKE << "!" << std::endl; +#endif + + #ifdef GRIFFON_FEATHER // DO NOT MODIFY! bsA = 4162; bsB = GRIFFON_FEATHER; @@ -4656,6 +4888,14 @@ class definesvalidator{ #endif +#ifdef IRINOX // DO NOT MODIFY! + bsA = 14; + bsB = IRINOX; + if(bsA!=bsB) + ssErrors << "Defined IRINOX with value 14 from .dat file mismatches hardcoded c++ define value of " << IRINOX << "!" << std::endl; +#endif + + #ifdef IRON // DO NOT MODIFY! bsA = 28674; bsB = IRON; @@ -4864,6 +5104,14 @@ class definesvalidator{ #endif +#ifdef KATANA // DO NOT MODIFY! + bsA = 24; + bsB = KATANA; + if(bsA!=bsB) + ssErrors << "Defined KATANA with value 24 from .dat file mismatches hardcoded c++ define value of " << KATANA << "!" << std::endl; +#endif + + #ifdef KAURI_WOOD // DO NOT MODIFY! bsA = 4244; bsB = KAURI_WOOD; @@ -4912,6 +5160,14 @@ class definesvalidator{ #endif +#ifdef KING_LEVEL // DO NOT MODIFY! + bsA = 5; + bsB = KING_LEVEL; + if(bsA!=bsB) + ssErrors << "Defined KING_LEVEL with value 5 from .dat file mismatches hardcoded c++ define value of " << KING_LEVEL << "!" << std::endl; +#endif + + #ifdef KIWI_FLESH // DO NOT MODIFY! bsA = 8198; bsB = KIWI_FLESH; @@ -4992,6 +5248,22 @@ class definesvalidator{ #endif +#ifdef LAUGHING_GAS // DO NOT MODIFY! + bsA = 12302; + bsB = LAUGHING_GAS; + if(bsA!=bsB) + ssErrors << "Defined LAUGHING_GAS with value 12302 from .dat file mismatches hardcoded c++ define value of " << LAUGHING_GAS << "!" << std::endl; +#endif + + +#ifdef LAVA // DO NOT MODIFY! + bsA = 16443; + bsB = LAVA; + if(bsA!=bsB) + ssErrors << "Defined LAVA with value 16443 from .dat file mismatches hardcoded c++ define value of " << LAVA << "!" << std::endl; +#endif + + #ifdef LAW_STUDENT // DO NOT MODIFY! bsA = 1; bsB = LAW_STUDENT; @@ -5328,14 +5600,6 @@ class definesvalidator{ #endif -#ifdef MAGE_STAFF // DO NOT MODIFY! - bsA = 18; - bsB = MAGE_STAFF; - if(bsA!=bsB) - ssErrors << "Defined MAGE_STAFF with value 18 from .dat file mismatches hardcoded c++ define value of " << MAGE_STAFF << "!" << std::endl; -#endif - - #ifdef MAGIC_CRYSTAL // DO NOT MODIFY! bsA = 4201; bsB = MAGIC_CRYSTAL; @@ -5368,6 +5632,14 @@ class definesvalidator{ #endif +#ifdef MAGMA // DO NOT MODIFY! + bsA = 3; + bsB = MAGMA; + if(bsA!=bsB) + ssErrors << "Defined MAGMA with value 3 from .dat file mismatches hardcoded c++ define value of " << MAGMA << "!" << std::endl; +#endif + + #ifdef MAGPIE_FLESH // DO NOT MODIFY! bsA = 20522; bsB = MAGPIE_FLESH; @@ -5608,6 +5880,14 @@ class definesvalidator{ #endif +#ifdef MONDEDR // DO NOT MODIFY! + bsA = 13; + bsB = MONDEDR; + if(bsA!=bsB) + ssErrors << "Defined MONDEDR with value 13 from .dat file mismatches hardcoded c++ define value of " << MONDEDR << "!" << std::endl; +#endif + + #ifdef MONK // DO NOT MODIFY! bsA = 5; bsB = MONK; @@ -5776,6 +6056,14 @@ class definesvalidator{ #endif +#ifdef NAPALM // DO NOT MODIFY! + bsA = 16444; + bsB = NAPALM; + if(bsA!=bsB) + ssErrors << "Defined NAPALM with value 16444 from .dat file mismatches hardcoded c++ define value of " << NAPALM << "!" << std::endl; +#endif + + #ifdef NECRO_CHAMBER_LEVEL // DO NOT MODIFY! bsA = 6; bsB = NECRO_CHAMBER_LEVEL; @@ -6384,6 +6672,14 @@ class definesvalidator{ #endif +#ifdef PHASE // DO NOT MODIFY! + bsA = 3; + bsB = PHASE; + if(bsA!=bsB) + ssErrors << "Defined PHASE with value 3 from .dat file mismatches hardcoded c++ define value of " << PHASE << "!" << std::endl; +#endif + + #ifdef PHOENIX_FEATHER // DO NOT MODIFY! bsA = 4163; bsB = PHOENIX_FEATHER; @@ -6608,6 +6904,14 @@ class definesvalidator{ #endif +#ifdef POLYMORPHINE // DO NOT MODIFY! + bsA = 16442; + bsB = POLYMORPHINE; + if(bsA!=bsB) + ssErrors << "Defined POLYMORPHINE with value 16442 from .dat file mismatches hardcoded c++ define value of " << POLYMORPHINE << "!" << std::endl; +#endif + + #ifdef POLYMORPH_CONTROL // DO NOT MODIFY! bsA = 8; bsB = POLYMORPH_CONTROL; @@ -6752,6 +7056,14 @@ class definesvalidator{ #endif +#ifdef PYRAMID // DO NOT MODIFY! + bsA = 12; + bsB = PYRAMID; + if(bsA!=bsB) + ssErrors << "Defined PYRAMID with value 12 from .dat file mismatches hardcoded c++ define value of " << PYRAMID << "!" << std::endl; +#endif + + #ifdef PYRITE // DO NOT MODIFY! bsA = 4325; bsB = PYRITE; @@ -6848,6 +7160,46 @@ class definesvalidator{ #endif +#ifdef REBEL // DO NOT MODIFY! + bsA = 19; + bsB = REBEL; + if(bsA!=bsB) + ssErrors << "Defined REBEL with value 19 from .dat file mismatches hardcoded c++ define value of " << REBEL << "!" << std::endl; +#endif + + +#ifdef REBEL_CAMP // DO NOT MODIFY! + bsA = 9; + bsB = REBEL_CAMP; + if(bsA!=bsB) + ssErrors << "Defined REBEL_CAMP with value 9 from .dat file mismatches hardcoded c++ define value of " << REBEL_CAMP << "!" << std::endl; +#endif + + +#ifdef REBEL_LIEUTENANT // DO NOT MODIFY! + bsA = 2; + bsB = REBEL_LIEUTENANT; + if(bsA!=bsB) + ssErrors << "Defined REBEL_LIEUTENANT with value 2 from .dat file mismatches hardcoded c++ define value of " << REBEL_LIEUTENANT << "!" << std::endl; +#endif + + +#ifdef REBEL_SOLDIER // DO NOT MODIFY! + bsA = 1; + bsB = REBEL_SOLDIER; + if(bsA!=bsB) + ssErrors << "Defined REBEL_SOLDIER with value 1 from .dat file mismatches hardcoded c++ define value of " << REBEL_SOLDIER << "!" << std::endl; +#endif + + +#ifdef REBEL_TEAM // DO NOT MODIFY! + bsA = 18; + bsB = REBEL_TEAM; + if(bsA!=bsB) + ssErrors << "Defined REBEL_TEAM with value 18 from .dat file mismatches hardcoded c++ define value of " << REBEL_TEAM << "!" << std::endl; +#endif + + #ifdef RECTANGLE // DO NOT MODIFY! bsA = 1; bsB = RECTANGLE; @@ -6912,6 +7264,14 @@ class definesvalidator{ #endif +#ifdef RED_SNAKE // DO NOT MODIFY! + bsA = 1; + bsB = RED_SNAKE; + if(bsA!=bsB) + ssErrors << "Defined RED_SNAKE with value 1 from .dat file mismatches hardcoded c++ define value of " << RED_SNAKE << "!" << std::endl; +#endif + + #ifdef RED_WINE // DO NOT MODIFY! bsA = 16403; bsB = RED_WINE; @@ -7136,6 +7496,14 @@ class definesvalidator{ #endif +#ifdef RING_OF_SPEED // DO NOT MODIFY! + bsA = 18; + bsB = RING_OF_SPEED; + if(bsA!=bsB) + ssErrors << "Defined RING_OF_SPEED with value 18 from .dat file mismatches hardcoded c++ define value of " << RING_OF_SPEED << "!" << std::endl; +#endif + + #ifdef RING_OF_TELEPORTATION // DO NOT MODIFY! bsA = 4; bsB = RING_OF_TELEPORTATION; @@ -7240,6 +7608,14 @@ class definesvalidator{ #endif +#ifdef ROOM_OWNED_AREA // DO NOT MODIFY! + bsA = 7; + bsB = ROOM_OWNED_AREA; + if(bsA!=bsB) + ssErrors << "Defined ROOM_OWNED_AREA with value 7 from .dat file mismatches hardcoded c++ define value of " << ROOM_OWNED_AREA << "!" << std::endl; +#endif + + #ifdef ROOM_SHOP // DO NOT MODIFY! bsA = 2; bsB = ROOM_SHOP; @@ -7288,6 +7664,22 @@ class definesvalidator{ #endif +#ifdef ROYAL // DO NOT MODIFY! + bsA = 18; + bsB = ROYAL; + if(bsA!=bsB) + ssErrors << "Defined ROYAL with value 18 from .dat file mismatches hardcoded c++ define value of " << ROYAL << "!" << std::endl; +#endif + + +#ifdef ROYAL_STAFF // DO NOT MODIFY! + bsA = 1; + bsB = ROYAL_STAFF; + if(bsA!=bsB) + ssErrors << "Defined ROYAL_STAFF with value 1 from .dat file mismatches hardcoded c++ define value of " << ROYAL_STAFF << "!" << std::endl; +#endif + + #ifdef RUBBER // DO NOT MODIFY! bsA = 4185; bsB = RUBBER; @@ -7416,6 +7808,14 @@ class definesvalidator{ #endif +#ifdef SEA // DO NOT MODIFY! + bsA = 3; + bsB = SEA; + if(bsA!=bsB) + ssErrors << "Defined SEA with value 3 from .dat file mismatches hardcoded c++ define value of " << SEA << "!" << std::endl; +#endif + + #ifdef SEARCHING // DO NOT MODIFY! bsA = 65536; bsB = SEARCHING; @@ -8256,6 +8656,14 @@ class definesvalidator{ #endif +#ifdef TENT_WALL // DO NOT MODIFY! + bsA = 9; + bsB = TENT_WALL; + if(bsA!=bsB) + ssErrors << "Defined TENT_WALL with value 9 from .dat file mismatches hardcoded c++ define value of " << TENT_WALL << "!" << std::endl; +#endif + + #ifdef TERRA // DO NOT MODIFY! bsA = 7; bsB = TERRA; diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index 84deb51c8..da1f7da5c 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -84,6 +84,22 @@ int main(int argc, char** argv) return 0; } + if(argc > 1 && festring(argv[1]) == "--defgen") + { + std::cout << "Generate defines validator file. " << std::endl; + game::InitGlobalValueMap(); + definesvalidator::GenerateDefinesValidator("generate"); + return 0; + } + + if(argc > 1 && festring(argv[1]) == "--defval") + { + std::cout << "Generate defines validator file. " << std::endl; + game::InitGlobalValueMap(); + definesvalidator::GenerateDefinesValidator("validate"); + return 0; + } + #ifdef __DJGPP__ /* Saves numlock state and toggles it off */ From c274916e1d5b30abde7aebcd71c4909f76c04d66 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 1 Mar 2020 20:35:24 -0300 Subject: [PATCH 002/235] WIP: fixing developer console to prefer festring everywhere possible; --- Main/Include/bugworkaround.h | 2 +- Main/Include/definesvalidator.h | 4 +- Main/Include/devcons.h | 8 ++-- Main/Source/bugworkaround.cpp | 2 +- Main/Source/definesvalidator.cpp | 12 +++--- Main/Source/devcons.cpp | 67 ++++++++++++++++++-------------- 6 files changed, 51 insertions(+), 44 deletions(-) diff --git a/Main/Include/bugworkaround.h b/Main/Include/bugworkaround.h index 0849644c0..2c72c1fd0 100644 --- a/Main/Include/bugworkaround.h +++ b/Main/Include/bugworkaround.h @@ -26,7 +26,7 @@ class bugfixdp{ private: static character* BugWorkaroundDupPlayer(); - static void DevConsCmd(std::string strCmdParams); + static void DevConsCmd(festring fsCmdParams); static void GatherAllItemInLevel(); static bool ItemWork(character* Char, item* it, bool bFix, const char* cInfo, std::vector* pvItem,bool bSendToHell); diff --git a/Main/Include/definesvalidator.h b/Main/Include/definesvalidator.h index 78a091617..081df9864 100644 --- a/Main/Include/definesvalidator.h +++ b/Main/Include/definesvalidator.h @@ -24,8 +24,8 @@ class definesvalidator{ public: static void init(); - static void DevConsCmd(std::string); - static void GenerateDefinesValidator(std::string); + //static void DevConsCmd(std::string); + static void GenerateDefinesValidator(festring); static void Validate() { std::stringstream ssErrors; diff --git a/Main/Include/devcons.h b/Main/Include/devcons.h index fc3b524b6..e62c73539 100644 --- a/Main/Include/devcons.h +++ b/Main/Include/devcons.h @@ -19,17 +19,17 @@ #define DEVCMDMSG(fmt,x,...) ADD_MESSAGE(" > " fmt,x); -typedef void (*callcmd)(std::string); +typedef void (*callcmd)(festring); class devcons{ - static callcmd Find(std::string strCmd); - static void Help(std::string strFilter); + static callcmd Find(festring fsCmd); + static void Help(festring fsFilter); static void runCommand(festring fsCmd); public: static void Init(); static void OpenCommandsConsole(); static void AddDevCmd(festring fsCmd, callcmd Call, festring fsHelp=festring(), bool bWizardModeOnly=false); - static void SetVar(std::string strParams); + static void SetVar(festring fsParams); static float GetVar(int iIndex,float fDefaultIf0); }; diff --git a/Main/Source/bugworkaround.cpp b/Main/Source/bugworkaround.cpp index 10f361f29..491073b45 100644 --- a/Main/Source/bugworkaround.cpp +++ b/Main/Source/bugworkaround.cpp @@ -497,7 +497,7 @@ std::vector bugfixdp::FindCharactersOnLevel(bool bOnlyPlayerFlag) bool bBufFixDPMode=false; bool bugfixdp::IsFixing(){return bBufFixDPMode;} -void bugfixdp::DevConsCmd(std::string strCmdParams) +void bugfixdp::DevConsCmd(festring fsCmdParams) { BugWorkaroundDupPlayer(); bBufFixDPMode=true; diff --git a/Main/Source/definesvalidator.cpp b/Main/Source/definesvalidator.cpp index 288f634cc..7df9e10bb 100644 --- a/Main/Source/definesvalidator.cpp +++ b/Main/Source/definesvalidator.cpp @@ -118,16 +118,16 @@ void DefinesValidatorClose(){ DefinesValidator.close(); } #include "definesvalidator.h" //tip: 1st run this was commented -void CmdDevConsGenDefVal(std::string strOpt){ - definesvalidator::GenerateDefinesValidator(strOpt); +void CmdDevConsGenDefVal(festring fsOpt){ + definesvalidator::GenerateDefinesValidator(fsOpt); } void definesvalidator::init(){ devcons::AddDevCmd("DefVal", CmdDevConsGenDefVal, " generate the validator at user config path or validate the file 'define.dat' (may abort)"); } -void definesvalidator::GenerateDefinesValidator(std::string strOpt) +void definesvalidator::GenerateDefinesValidator(festring fsOpt) { - if(strOpt=="generate"){ + if(fsOpt=="generate"){ DefinesValidatorTop(); for(const valuemap::value_type& p : game::GetGlobalValueMap()) @@ -136,10 +136,10 @@ void definesvalidator::GenerateDefinesValidator(std::string strOpt) DefinesValidatorClose(); ADD_MESSAGE("generated the defines validator"); }else - if(strOpt=="validate"){ + if(fsOpt=="validate"){ definesvalidator::Validate(); //tip: 1st run this was commented ADD_MESSAGE("validated 'defines.dat'"); }else{ - ADD_MESSAGE("invalid option: '%s'",strOpt.c_str()); + ADD_MESSAGE("invalid option: '%s'",fsOpt.CStr()); } } diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 7f32f0aa9..20f5b19ae 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -21,6 +21,7 @@ #include "error.h" #include "feio.h" #include "felist.h" +#include "festring.h" #include "game.h" #include "message.h" #include "stack.h" @@ -40,10 +41,10 @@ */ #ifdef WIZARD -void ListChars(std::string strFilter){ +void ListChars(festring fsFilter){ ulong idFilter=0; - if(!strFilter.empty()) - idFilter=atoi(strFilter.c_str()); + if(!fsFilter.IsEmpty()) + idFilter=atoi(fsFilter.CStr()); DEVCMDMSG("params: %d",idFilter); @@ -80,13 +81,13 @@ void ListChars(std::string strFilter){ // ); } } -void ListItems(std::string strParams){ +void ListItems(festring fsParams){ ulong idCharFilter=0; ulong idFilter=0; - if(!strParams.empty()){ + if(!fsParams.IsEmpty()){ std::string part; - std::stringstream iss(strParams); + std::stringstream iss(fsParams.CStr()); if(iss >> part){ if(part=="c"){ if(iss >> part) @@ -209,11 +210,11 @@ void devcons::Init() const int iVarTot=10; float afVars[iVarTot]; -void devcons::SetVar(std::string strParams) +void devcons::SetVar(festring fsParams) { - if(!strParams.empty()){ + if(!fsParams.IsEmpty()){ std::string part; - std::stringstream iss(strParams); + std::stringstream iss(fsParams.CStr()); iss >> part; int index = atoi(part.c_str()); //TODO use string IDs instead of index and create a map @@ -284,12 +285,12 @@ void devcons::OpenCommandsConsole() bOpenCommandsConsole=false; } -typedef void (*callcmd)(std::string); +//typedef void (*callcmd)(festring&); struct DevCmd{ - std::string strCmd=""; - std::string strCmdLowerCase=""; - std::string strHelp=""; + festring fsCmd=""; + festring fsCmdLowerCase=""; + festring fsHelp=""; callcmd Call=NULL; bool bWizardModeOnly=false; }; @@ -303,39 +304,41 @@ void devcons::AddDevCmd(festring fsCmd, callcmd Call, festring fsHelp,bool bWiza ABORT("command %s already set %p %p",fsCmd.CStr(),cc,Call); DevCmd dc; - dc.strCmd=fsCmd.CStr(); - dc.strCmdLowerCase=fsCmd.CStr(); - std::transform(dc.strCmdLowerCase.begin(), dc.strCmdLowerCase.end(), dc.strCmdLowerCase.begin(), ::tolower); - dc.strHelp=fsHelp.CStr(); + dc.fsCmd=fsCmd.CStr(); + + std::string strCmdLowerCase=fsCmd.CStr(); + std::transform(strCmdLowerCase.begin(), strCmdLowerCase.end(), strCmdLowerCase.begin(), ::tolower); + dc.fsCmdLowerCase=strCmdLowerCase.c_str(); + + dc.fsHelp=fsHelp.CStr(); dc.Call = Call; dc.bWizardModeOnly=bWizardModeOnly; - int i=dc.strCmd.find(" "); + int i=std::string(dc.fsCmd.CStr()).find(" "); if(i!=std::string::npos) - ABORT("command must not contain spaces '%s'",dc.strCmd.c_str()); + ABORT("command must not contain spaces '%s'",dc.fsCmd.CStr()); vCmd.push_back(dc); } -void devcons::Help(std::string strFilter) +void devcons::Help(festring fsFilter) { + festring fsWM; for(int j=0;j Date: Mon, 16 Mar 2020 22:11:30 -0300 Subject: [PATCH 003/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework. --- Main/Include/craft.h | 2 +- Main/Include/ivandef.h | 3 ++- Main/Source/actions.cpp | 10 +++++++++- Main/Source/cmdcraft.cpp | 10 +++++----- Main/Source/wskill.cpp | 3 ++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index 1f61afa81..7c52889e4 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -249,7 +249,7 @@ class craftcore { static int CurrentDungeonLevelID(); - static float CraftSkill(character* Char); + static float CraftSkillBonus(character* Char); static bool MoreCraftDeniedFilters(item* it); static bool EmptyContentsIfPossible(recipedata& rpd,item* itContainer,bool bMoveToInventory=false); diff --git a/Main/Include/ivandef.h b/Main/Include/ivandef.h index a06028c4a..c62780d9f 100644 --- a/Main/Include/ivandef.h +++ b/Main/Include/ivandef.h @@ -346,7 +346,7 @@ cv2 SILHOUETTE_SIZE(48, 64); // it is TILE_SIZE*3,TILE_SIZE*4 tho.. #define FRIEND 4 #define MARTIAL_SKILL_CATEGORIES 3 -#define WEAPON_SKILL_CATEGORIES 11 +#define WEAPON_SKILL_CATEGORIES 12 #define UNARMED 0 #define KICK 1 @@ -359,6 +359,7 @@ cv2 SILHOUETTE_SIZE(48, 64); // it is TILE_SIZE*3,TILE_SIZE*4 tho.. #define POLE_ARMS 8 #define WHIPS 9 #define SHIELDS 10 +#define CRAFTING 11 #define LOCKED 1 diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index d3c95df58..a293bb7de 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -280,7 +280,15 @@ void craft::Handle() if(rpd.bSuccesfullyCompleted) { Actor->DexterityAction(rpd.iAddDexterity); //TODO is this necessary/useful? below also affects dex - + + /** + * the minimum to advance 1st level on success is at SWeaponSkillLevelMap[1] + */ + int iAddCraftSkill = SWeaponSkillLevelMap[1] * rpd.fDifficulty; + if(rpd.bSpawnBroken) iAddCraftSkill /= 10; // learns something if fumble + if(iAddCraftSkill<=0) iAddCraftSkill=1; // something at least... + Actor->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); + /* If the door was boobytrapped etc. and the character is dead, Action has already been deleted */ if(!Actor->IsEnabled()) return; diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index fb7416243..a60ecd147 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -105,7 +105,7 @@ void craftcore::SendToHellSafely(item* it) // **rit=NULL; } -float craftcore::CraftSkill(character* Char){ //is the current capability of successfully crafting +float craftcore::CraftSkillBonus(character* Char){ //is the current capability of successfully crafting float Skill = 0; // influence/weights of each stat will be the FINAL divider! float fWeight = 0; float fDivFinal = 0; @@ -1318,7 +1318,7 @@ struct srpCutWeb : public recipe{ if(bSelfPos) tot *= 3; //to make it worther than just trying to move, and to compensate for not moving too as player won't insta flee from attacks if(rpd.itTool!=NULL) //float multiplier last thing! - tot *= 1 + craftcore::CraftSkill(h)/10; + tot *= 1 + craftcore::CraftSkillBonus(h)/10; DBG1(tot); bool bSuccess = false; for(int i=0;i vrpAllRecipes; void updateCraftDesc(){ craftRecipes.EmptyDescription(); - float fSkill=craftcore::CraftSkill(PLAYER); //TODO should this dynamic value show too where stats are? + float fSkill=craftcore::CraftSkillBonus(PLAYER); //TODO should this dynamic value show too where stats are? festring fsSkill="Crafting Proficiency: "; // It's actually different from skills, so don't call it a skill. static char cSkill[20]; sprintf(cSkill, "%.1f",fSkill); @@ -3001,7 +3001,7 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif * LAST turn calc thing!!! * ex.: initial dex=10 wis=10 is 1.0 means wont modify turns **********************************************************************/ - rpd.iBaseTurnsToFinish /= craftcore::CraftSkill(Char)/10.0; + rpd.iBaseTurnsToFinish /= craftcore::CraftSkillBonus(Char)/10.0; if(rpd.iBaseTurnsToFinish==0) //if div zeroed it rpd.iBaseTurnsToFinish=1; if(rpd.iBaseTurnsToFinish99)iFumbleBase=99; //%1 granted luck int iDiv=0; iDiv=1;if(iFumbleBase>iDiv && iLuckPerc<=iFumbleBase/iDiv)iFumblePower++; //ex.: <=30% diff --git a/Main/Source/wskill.cpp b/Main/Source/wskill.cpp index 478489f1b..91458fb2c 100644 --- a/Main/Source/wskill.cpp +++ b/Main/Source/wskill.cpp @@ -43,7 +43,8 @@ cchar* CWeaponSkillName[WEAPON_SKILL_CATEGORIES] "axes", "polearms", "whips", - "shields" + "shields", + "crafting" }; int SWeaponSkillLevelMap[] From 4aa7de8b1622eb88e19ad250440922ab9e4f166e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 16 Mar 2020 22:15:41 -0300 Subject: [PATCH 004/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework. --- Main/Source/actions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index a293bb7de..624b662df 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -282,9 +282,9 @@ void craft::Handle() Actor->DexterityAction(rpd.iAddDexterity); //TODO is this necessary/useful? below also affects dex /** - * the minimum to advance 1st level on success is at SWeaponSkillLevelMap[1] + * the minimum to advance 1st level on success is at GetLevelMap(1) */ - int iAddCraftSkill = SWeaponSkillLevelMap[1] * rpd.fDifficulty; + int iAddCraftSkill = Actor->GetCWeaponSkill(CRAFTING)->GetLevelMap(1) * rpd.fDifficulty; if(rpd.bSpawnBroken) iAddCraftSkill /= 10; // learns something if fumble if(iAddCraftSkill<=0) iAddCraftSkill=1; // something at least... Actor->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); From 65130a9f1a0f1cb0c07d5c26c513e497a88dc306 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 16 Mar 2020 22:37:26 -0300 Subject: [PATCH 005/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework. --- Main/Include/craft.h | 2 +- Main/Source/cmdcraft.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index 7c52889e4..1f61afa81 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -249,7 +249,7 @@ class craftcore { static int CurrentDungeonLevelID(); - static float CraftSkillBonus(character* Char); + static float CraftSkill(character* Char); static bool MoreCraftDeniedFilters(item* it); static bool EmptyContentsIfPossible(recipedata& rpd,item* itContainer,bool bMoveToInventory=false); diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index a60ecd147..3ae44d3ec 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -105,12 +105,12 @@ void craftcore::SendToHellSafely(item* it) // **rit=NULL; } -float craftcore::CraftSkillBonus(character* Char){ //is the current capability of successfully crafting - float Skill = 0; // influence/weights of each stat will be the FINAL divider! +float craftcore::CraftSkill(character* Char){ //is the current capability of successfully crafting + float fBonus = 0; // influence/weights of each stat will be the FINAL divider! float fWeight = 0; float fDivFinal = 0; - #define CALCSK(weig,attr) fWeight = weig; Skill += Char->GetAttribute(attr)*weig; fDivFinal+=weig; + #define CALCSK(weig,attr) fWeight = weig; fBonus += Char->GetAttribute(attr)*weig; fDivFinal+=weig; CALCSK(15.0,DEXTERITY); //by importance order CALCSK(7.5,WISDOM); CALCSK(3.0,PERCEPTION); //TODO could counter fumbles directly @@ -123,7 +123,7 @@ float craftcore::CraftSkillBonus(character* Char){ //is the current capability o //TODO CHARISMA //if one day there is item quality, well finished, attribute that could increase sell price //TODO MANA //if one day anything magical is allowed to be crafted - return Skill/fDivFinal; // in short, if all stats are 10, craft skill will be 10 + return Char->GetCWeaponSkill(CRAFTING)->GetLevel() + fBonus/fDivFinal; // in short, if all stats are 10, craft skill will be 10 } bool craftcore::canBeCrafted(item* it){ @@ -1318,7 +1318,7 @@ struct srpCutWeb : public recipe{ if(bSelfPos) tot *= 3; //to make it worther than just trying to move, and to compensate for not moving too as player won't insta flee from attacks if(rpd.itTool!=NULL) //float multiplier last thing! - tot *= 1 + craftcore::CraftSkillBonus(h)/10; + tot *= 1 + craftcore::CraftSkill(h)/10; DBG1(tot); bool bSuccess = false; for(int i=0;i vrpAllRecipes; void updateCraftDesc(){ craftRecipes.EmptyDescription(); - float fSkill=craftcore::CraftSkillBonus(PLAYER); //TODO should this dynamic value show too where stats are? + float fSkill=craftcore::CraftSkill(PLAYER); //TODO should this dynamic value show too where stats are? festring fsSkill="Crafting Proficiency: "; // It's actually different from skills, so don't call it a skill. static char cSkill[20]; sprintf(cSkill, "%.1f",fSkill); @@ -3001,7 +3001,7 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif * LAST turn calc thing!!! * ex.: initial dex=10 wis=10 is 1.0 means wont modify turns **********************************************************************/ - rpd.iBaseTurnsToFinish /= craftcore::CraftSkillBonus(Char)/10.0; + rpd.iBaseTurnsToFinish /= craftcore::CraftSkill(Char)/10.0; if(rpd.iBaseTurnsToFinish==0) //if div zeroed it rpd.iBaseTurnsToFinish=1; if(rpd.iBaseTurnsToFinish99)iFumbleBase=99; //%1 granted luck int iDiv=0; iDiv=1;if(iFumbleBase>iDiv && iLuckPerc<=iFumbleBase/iDiv)iFumblePower++; //ex.: <=30% From be1ef93855bf4f721da715ce17172d80876732bf Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 16 Mar 2020 22:47:51 -0300 Subject: [PATCH 006/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework. --- Main/Source/actions.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index 624b662df..42942a6f9 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -283,11 +283,14 @@ void craft::Handle() /** * the minimum to advance 1st level on success is at GetLevelMap(1) + * it must have some difficulty to advance */ - int iAddCraftSkill = Actor->GetCWeaponSkill(CRAFTING)->GetLevelMap(1) * rpd.fDifficulty; - if(rpd.bSpawnBroken) iAddCraftSkill /= 10; // learns something if fumble - if(iAddCraftSkill<=0) iAddCraftSkill=1; // something at least... - Actor->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); + if(rpd.fDifficulty > 1.0){ + int iAddCraftSkill = Actor->GetCWeaponSkill(CRAFTING)->GetLevelMap(1) * rpd.fDifficulty; + if(rpd.bSpawnBroken) iAddCraftSkill /= 10; // learns something if fumble + if(iAddCraftSkill<=0) iAddCraftSkill=1; // something at least... + Actor->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); + } /* If the door was boobytrapped etc. and the character is dead, Action has already been deleted */ if(!Actor->IsEnabled()) From 669fe6ceb21c8bc8fc61c3f6361a43d63c9faee8 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 16 Mar 2020 23:02:25 -0300 Subject: [PATCH 007/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework. --- Main/Source/actions.cpp | 2 +- Main/Source/cmdcraft.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index 42942a6f9..804791c63 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -286,7 +286,7 @@ void craft::Handle() * it must have some difficulty to advance */ if(rpd.fDifficulty > 1.0){ - int iAddCraftSkill = Actor->GetCWeaponSkill(CRAFTING)->GetLevelMap(1) * rpd.fDifficulty; + int iAddCraftSkill = (Actor->GetCWeaponSkill(CRAFTING)->GetLevelMap(1)) * rpd.fDifficulty; if(rpd.bSpawnBroken) iAddCraftSkill /= 10; // learns something if fumble if(iAddCraftSkill<=0) iAddCraftSkill=1; // something at least... Actor->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 3ae44d3ec..2065e5149 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -123,7 +123,12 @@ float craftcore::CraftSkill(character* Char){ //is the current capability of suc //TODO CHARISMA //if one day there is item quality, well finished, attribute that could increase sell price //TODO MANA //if one day anything magical is allowed to be crafted - return Char->GetCWeaponSkill(CRAFTING)->GetLevel() + fBonus/fDivFinal; // in short, if all stats are 10, craft skill will be 10 + float fSkill = 0; + fSkill += Char->GetCWeaponSkill(CRAFTING)->GetLevel(); // base/learned + fSkill += fBonus/fDivFinal; // in short, if all stats are 10, craft skill would be 10 + fSkill -= 10.0; // to make advancing important + if(fSkill<=0)fSkill=0.1; //safety + return fSkill; } bool craftcore::canBeCrafted(item* it){ From e98ac4e62e050a35b7828d200cd213fbc057b143 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 16 Mar 2020 23:40:59 -0300 Subject: [PATCH 008/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework; WIP-craft: fixing crash in case a key (or anything that has a missing BROKEN .dat config) breaks when crafted; --- Main/Include/database.h | 1 + Main/Include/item.h | 1 + Main/Source/cmdcraft.cpp | 14 ++++++++++---- Main/Source/database.cpp | 13 +++++++++++++ Main/Source/item.cpp | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Main/Include/database.h b/Main/Include/database.h index fce0b5f7a..987c8ea53 100644 --- a/Main/Include/database.h +++ b/Main/Include/database.h @@ -34,6 +34,7 @@ template class databasecreator typedef std::map*> databasemembermap; static void ReadFrom(inputfile&); static void FindDataBase(const database*&, const prototype*, int); + static truth InstallDataBaseIfPossible(type*, int, int); static void InstallDataBase(type*, int); static void CreateDataBaseMemberMap(); static int CreateDivineConfigurations(const prototype*, database**, int); diff --git a/Main/Include/item.h b/Main/Include/item.h index 6039d0f98..546dcf19c 100644 --- a/Main/Include/item.h +++ b/Main/Include/item.h @@ -545,6 +545,7 @@ class item : public object virtual void FinalProcessForBone() { } virtual truth SuckSoul(character*, character* = 0) { return false; } void SetConfig(int, int = 0); + truth SetConfigIfPossible(int, int = 0); god* GetMasterGod() const; idholder* GetCloneMotherID() const { return CloneMotherID; } virtual void SignalStackAdd(stackslot*, void (stack::*)(item*, truth)); diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 2065e5149..7c894689c 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -3085,7 +3085,8 @@ item* crafthandle::CheckBreakItem(bool bAllowBreak, recipedata& rpd, item* itSpa bool bBreak = rpd.bSpawnBroken; if(bAllowBreak && bBreak && !itSpawn->IsBroken()){ - if(itSpawn->CanBeBroken()){ + bool bCanBeBroken = itSpawn->CanBeBroken(); + if(bCanBeBroken){ /** * IMPORTANT!!! * @@ -3101,9 +3102,14 @@ item* crafthandle::CheckBreakItem(bool bAllowBreak, recipedata& rpd, item* itSpa * This below was taken from Break() and seems safe. * TODO create a method there like SetSelfAsBroken() to re-use the code to grant they will be in sync */ - itSpawn->SetConfig(rpd.itSpawnCfg | BROKEN); - itSpawn->SetSize(itSpawn->GetSize() >> 1); - }else{ + if(itSpawn->SetConfigIfPossible(rpd.itSpawnCfg | BROKEN)){ + itSpawn->SetSize(itSpawn->GetSize() >> 1); + }else{ + bCanBeBroken=false; // missing BROKEN config at .dat file but no problem, see below + } + } + + if(!bCanBeBroken){ /** * things that can't be broken are special. * if it can't be broken, will just create a messy lump. diff --git a/Main/Source/database.cpp b/Main/Source/database.cpp index bfdb65101..277a4de25 100644 --- a/Main/Source/database.cpp +++ b/Main/Source/database.cpp @@ -944,6 +944,19 @@ template inline void databasecreator::FindDataBase(const data template void databasecreator::FindDataBase(const database*&, const prototype*, int); template void databasecreator::FindDataBase(const database*&, const prototype*, int); +template truth databasecreator::InstallDataBaseIfPossible(type* Instance, int Config, int OldConfig) +{ + const prototype* Proto = Instance->FindProtoType(); + FindDataBase(Instance->DataBase, Proto, Config); + + if(!Instance->DataBase){ + FindDataBase(Instance->DataBase, Proto, OldConfig); //restore DataBase field + return false; + } + + return true; +} + template void databasecreator::InstallDataBase(type* Instance, int Config) { const prototype* Proto = Instance->FindProtoType(); diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp index e2fa013cb..b06c8e74a 100644 --- a/Main/Source/item.cpp +++ b/Main/Source/item.cpp @@ -1424,6 +1424,20 @@ void item::SetConfig(int NewConfig, int SpecialFlags) UpdatePictures(); } +truth item::SetConfigIfPossible(int NewConfig, int SpecialFlags) +{ + if(databasecreator::InstallDataBaseIfPossible(this, NewConfig, GetConfig())){ + CalculateAll(); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); + + return true; + } + + return false; +} + god* item::GetMasterGod() const { return game::GetGod(GetConfig()); From f2eacdcd0756c18219fecc3de50ce55d35c7165a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 17 Mar 2020 00:14:31 -0300 Subject: [PATCH 009/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework; WIP-craft: fixing crash in case a key (or anything that has a missing BROKEN .dat config) breaks when crafted; --- Main/Source/database.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Main/Source/database.cpp b/Main/Source/database.cpp index 277a4de25..dabccfe59 100644 --- a/Main/Source/database.cpp +++ b/Main/Source/database.cpp @@ -944,6 +944,15 @@ template inline void databasecreator::FindDataBase(const data template void databasecreator::FindDataBase(const database*&, const prototype*, int); template void databasecreator::FindDataBase(const database*&, const prototype*, int); +template void databasecreator::InstallDataBase(type* Instance, int Config) +{ + const prototype* Proto = Instance->FindProtoType(); + FindDataBase(Instance->DataBase, Proto, Config); + + if(!Instance->DataBase) + ABORT("Undefined %s configuration #%d sought!", const_cast(Proto->GetClassID()), Config); +} + template truth databasecreator::InstallDataBaseIfPossible(type* Instance, int Config, int OldConfig) { const prototype* Proto = Instance->FindProtoType(); @@ -957,17 +966,9 @@ template truth databasecreator::InstallDataBaseIfPossible(typ return true; } -template void databasecreator::InstallDataBase(type* Instance, int Config) -{ - const prototype* Proto = Instance->FindProtoType(); - FindDataBase(Instance->DataBase, Proto, Config); - - if(!Instance->DataBase) - ABORT("Undefined %s configuration #%d sought!", const_cast(Proto->GetClassID()), Config); -} - #define INST_INSTALL_DATABASE(type)\ -template void databasecreator::InstallDataBase(type*, int) +template void databasecreator::InstallDataBase(type*, int);\ +template truth databasecreator::InstallDataBaseIfPossible(type*, int, int) INST_INSTALL_DATABASE(material); INST_INSTALL_DATABASE(character); From 020b2c50de707429704bb4896c5d10755c74a3fa Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 17 Mar 2020 00:17:05 -0300 Subject: [PATCH 010/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework; WIP-craft: fixing crash in case a key (or anything that has a missing BROKEN .dat config) breaks when crafted; --- Main/Source/database.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Main/Source/database.cpp b/Main/Source/database.cpp index dabccfe59..bf98984d3 100644 --- a/Main/Source/database.cpp +++ b/Main/Source/database.cpp @@ -966,6 +966,7 @@ template truth databasecreator::InstallDataBaseIfPossible(typ return true; } +// this allows final compilation linking step to succeed! It apparently requires each type to be explicitly declared once; #define INST_INSTALL_DATABASE(type)\ template void databasecreator::InstallDataBase(type*, int);\ template truth databasecreator::InstallDataBaseIfPossible(type*, int, int) From 52d091c33e7e7a8253237dc210dadc15dc27ebb1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 17 Mar 2020 00:57:24 -0300 Subject: [PATCH 011/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework; craft: fixed crash in case a key (or anything that has a missing BROKEN .dat config) breaks when crafted; --- Main/Source/actions.cpp | 12 +++++------- Main/Source/cmdcraft.cpp | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index 804791c63..fa38b0a52 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -283,14 +283,12 @@ void craft::Handle() /** * the minimum to advance 1st level on success is at GetLevelMap(1) - * it must have some difficulty to advance */ - if(rpd.fDifficulty > 1.0){ - int iAddCraftSkill = (Actor->GetCWeaponSkill(CRAFTING)->GetLevelMap(1)) * rpd.fDifficulty; - if(rpd.bSpawnBroken) iAddCraftSkill /= 10; // learns something if fumble - if(iAddCraftSkill<=0) iAddCraftSkill=1; // something at least... - Actor->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); - } + int iAddCraftSkill = Actor->GetCWeaponSkill(CRAFTING)->GetLevelMap(1) * rpd.fDifficulty; + if(rpd.fDifficulty <= 1.0)iAddCraftSkill/=3.0; // too easy stuff will learn less + if(rpd.bSpawnBroken) iAddCraftSkill /= 10; // learns something if fumble + if(iAddCraftSkill<1) iAddCraftSkill=1; // add a minimum + Actor->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); /* If the door was boobytrapped etc. and the character is dead, Action has already been deleted */ if(!Actor->IsEnabled()) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 7c894689c..d2e094469 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -127,7 +127,7 @@ float craftcore::CraftSkill(character* Char){ //is the current capability of suc fSkill += Char->GetCWeaponSkill(CRAFTING)->GetLevel(); // base/learned fSkill += fBonus/fDivFinal; // in short, if all stats are 10, craft skill would be 10 fSkill -= 10.0; // to make advancing important - if(fSkill<=0)fSkill=0.1; //safety + if(fSkill<1.0)fSkill=1.0; //safety return fSkill; } @@ -1323,7 +1323,7 @@ struct srpCutWeb : public recipe{ if(bSelfPos) tot *= 3; //to make it worther than just trying to move, and to compensate for not moving too as player won't insta flee from attacks if(rpd.itTool!=NULL) //float multiplier last thing! - tot *= 1 + craftcore::CraftSkill(h)/10; + tot *= 1 + craftcore::CraftSkill(h)/10.0; DBG1(tot); bool bSuccess = false; for(int i=0;i Date: Tue, 17 Mar 2020 01:29:20 -0300 Subject: [PATCH 012/235] WIP-craft: added crafting skill, using existing weapon skill advancement framework; --- Main/Include/craft.h | 1 + Main/Source/cmdcraft.cpp | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index 1f61afa81..de19f719d 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -285,6 +285,7 @@ class craftcore { static int CitType(item* it); static bool CheckFumble(recipedata& rpd, bool& bCriticalFumble,int& iFumblePower); + static void CraftSkillAdvance(recipedata&); }; class crafthandle { diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index d2e094469..4f23c56fc 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -1410,6 +1410,7 @@ struct srpCutWeb : public recipe{ } h->EditAP(-500); //to let time pass + craftcore::CraftSkillAdvance(rpd); //TODO this should be related to collect spider silk to craft one day with it rpd.bSpendCurrentTurn=true; @@ -1676,9 +1677,10 @@ struct srpJoinLumps : public recipe{ } joinLumpsEqualToFirst(rpd); + craftcore::CraftSkillAdvance(rpd); rpd.bAlreadyExplained = true; - + return true; } };srpJoinLumps rpJoinLumps; @@ -1884,7 +1886,9 @@ struct srpDismantle : public recipe{ //TODO this is instantaneous, should take t lumpMix(vitInv(rpd),RmnM,rpd.bSpendCurrentTurn); if(dynamic_cast(RmnS)!=NULL) lumpMix(vitInv(rpd),RmnS,rpd.bSpendCurrentTurn); - + + craftcore::CraftSkillAdvance(rpd); + return true; } };srpDismantle rpDismantle; @@ -1916,6 +1920,7 @@ struct srpInspect : public recipe{ //TODO this is instantaneous, should take tim fs<<"."; if(matM||matS){ ADD_MESSAGE("%s",fs.CStr()); + craftcore::CraftSkillAdvance(rpd); }else{ ADD_MESSAGE("You can't inspect %s.",it0->GetName(INDEFINITE).CStr()); } @@ -1980,6 +1985,7 @@ struct srpResistanceVS : public recipe{ //TODO this is instantaneous, should tak } rpd.bAlreadyExplained=true; + craftcore::CraftSkillAdvance(rpd); return true; } @@ -3388,6 +3394,17 @@ void crafthandle::GradativeCraftOverride(recipedata& rpd) DBG7(iSpawnNow,spawnedVol,matMRemVol,rpd.itSpawnTot,rpd.iBaseTurnsToFinish,rpd.iRemainingTurnsToFinish,fRemain); } +void craftcore::CraftSkillAdvance(recipedata& rpd){ + /** + * the minimum to advance 1st level on success is at GetLevelMap(1) + */ + int iAddCraftSkill = rpd.rc.H()->GetCWeaponSkill(CRAFTING)->GetLevelMap(1) * rpd.fDifficulty; + if(rpd.fDifficulty <= 1.0) iAddCraftSkill /= 10.0; // too easy stuff will learn less + if(rpd.bSpawnBroken) iAddCraftSkill /= 3.0; // learns something if fumble + if(iAddCraftSkill<1) iAddCraftSkill=1; // add a minimum + rpd.rc.H()->GetCWeaponSkill(CRAFTING)->AddHit(iAddCraftSkill); +} + bool craftcore::CheckFumble(recipedata& rpd, bool& bCriticalFumble,int& iFumblePower) { /** From 1cc45ba958025314705dd0b900c17bcf921f5997 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 17 Mar 2020 03:02:29 -0300 Subject: [PATCH 013/235] updated definesvalidator.h better commandline --help and info. --- Main/Include/definesvalidator.h | 6 +++--- Main/Source/definesvalidator.cpp | 2 +- Main/Source/main.cpp | 14 +++++++++++++- Script/define.dat | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Main/Include/definesvalidator.h b/Main/Include/definesvalidator.h index 081df9864..79b5d6913 100644 --- a/Main/Include/definesvalidator.h +++ b/Main/Include/definesvalidator.h @@ -24,7 +24,7 @@ class definesvalidator{ public: static void init(); - //static void DevConsCmd(std::string); + static void DevConsCmd(std::string); static void GenerateDefinesValidator(festring); static void Validate() { @@ -9473,10 +9473,10 @@ class definesvalidator{ #ifdef WEAPON_SKILL_CATEGORIES // DO NOT MODIFY! - bsA = 11; + bsA = 12; bsB = WEAPON_SKILL_CATEGORIES; if(bsA!=bsB) - ssErrors << "Defined WEAPON_SKILL_CATEGORIES with value 11 from .dat file mismatches hardcoded c++ define value of " << WEAPON_SKILL_CATEGORIES << "!" << std::endl; + ssErrors << "Defined WEAPON_SKILL_CATEGORIES with value 12 from .dat file mismatches hardcoded c++ define value of " << WEAPON_SKILL_CATEGORIES << "!" << std::endl; #endif diff --git a/Main/Source/definesvalidator.cpp b/Main/Source/definesvalidator.cpp index 7df9e10bb..d860c7c72 100644 --- a/Main/Source/definesvalidator.cpp +++ b/Main/Source/definesvalidator.cpp @@ -77,7 +77,7 @@ void DefinesValidatorTop() DefinesValidatorAppend(" public:"); DefinesValidatorAppend(" static void init();"); DefinesValidatorAppend(" static void DevConsCmd(std::string);"); - DefinesValidatorAppend(" static void GenerateDefinesValidator(std::string);"); + DefinesValidatorAppend(" static void GenerateDefinesValidator(festring);"); DefinesValidatorAppend(""); DefinesValidatorAppend(" static void Validate() {"); DefinesValidatorAppend(" std::stringstream ssErrors;"); diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index da1f7da5c..1f6f5d527 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -88,15 +88,27 @@ int main(int argc, char** argv) { std::cout << "Generate defines validator file. " << std::endl; game::InitGlobalValueMap(); + std::cout << "DONE: InitGlobalValueMap()" << std::endl; definesvalidator::GenerateDefinesValidator("generate"); + std::cout << "Finished: Generate DefinesValidator" << std::endl; return 0; } if(argc > 1 && festring(argv[1]) == "--defval") { - std::cout << "Generate defines validator file. " << std::endl; + std::cout << "Validate defines. " << std::endl; game::InitGlobalValueMap(); + std::cout << "DONE: InitGlobalValueMap()" << std::endl; definesvalidator::GenerateDefinesValidator("validate"); + std::cout << "Finished: Validate defines" << std::endl; + return 0; + } + + if(argc > 1 && festring(argv[1]) == "--help") + { + std::cout << "--defgen Generate defines validator source file. " << std::endl; + std::cout << "--defval Validate defines. " << std::endl; + std::cout << "--version Show current game version. " << std::endl; return 0; } diff --git a/Script/define.dat b/Script/define.dat index ffb092721..e8b7dc91f 100644 --- a/Script/define.dat +++ b/Script/define.dat @@ -177,7 +177,7 @@ #define FRIEND 4 #define MARTIAL_SKILL_CATEGORIES 3 -#define WEAPON_SKILL_CATEGORIES 11 +#define WEAPON_SKILL_CATEGORIES 12 #define UNARMED 0 #define KICK 1 From b7eeff4759c902473701dbc8480c29f6c7a74d9e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 17 Mar 2020 23:39:37 -0300 Subject: [PATCH 014/235] WIP-lights: changing light emitation of cut raw things (stone,stick,lump); --- Main/Source/cmdcraft.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 4f23c56fc..9004a8f3f 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -3162,6 +3162,7 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) item* itSpawn = NULL; material* matS = NULL; bool bAllowBreak=false;DBG3(rpd.itSpawnType,rpd.itSpawnCfg,rpd.itSpawnMatSecCfg); + bool bChangeEmit=false; switch(rpd.itSpawnType){ case CIT_POTION: /** @@ -3182,12 +3183,15 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) break; case CIT_STONE: itSpawn = stone::Spawn(rpd.itSpawnCfg, NO_MATERIALS); + bChangeEmit=true; break; case CIT_LUMP: itSpawn = lump::Spawn(rpd.itSpawnCfg, NO_MATERIALS); + bChangeEmit=true; break; case CIT_STICK: itSpawn = stick::Spawn(rpd.itSpawnCfg, NO_MATERIALS); + bChangeEmit=true; break; } @@ -3238,7 +3242,27 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) itSpawn->GetName(DEFINITE).CStr() ); } - + + /** + * a good light emmiting crystal stone is about 100 to 200 cm3 + * smaller stones/sticks/lumps or etc, should emit less light... + */ + static col24 colBlack24 = MakeRGB24(0,0,0); + if(bChangeEmit && rpd.itSpawnMatMainVol<100){ + col24 col = itSpawn->GetEmitation(); + if(col != colBlack24){ + col24 cRed = GetRed24(col); + col24 cGreen = GetGreen24(col); + col24 cBlue = GetBlue24(col); + float fPerc = rpd.itSpawnMatMainVol/100.0; + itSpawn->SignalEmitationDecrease(MakeRGB24( + cRed - (cRed*fPerc), + cGreen - (cGreen*fPerc), + cBlue - (cBlue*fPerc) + )); + } + } + itSpawn = CheckBreakItem(bAllowBreak, rpd, itSpawn, fsCreated); if(itSpawn!=NULL){ if(fsCreated.GetSize()<200) // this will prevent a crash about "stack smashing detected". TODO to test it and provide a better solution, just comment this `if` line and split a corpse in 40 parts or more From a4231dd376864fa3a6fec35e4a0ed6d2a85a5190 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 18 Mar 2020 00:41:52 -0300 Subject: [PATCH 015/235] WIP-lights: changing light emitation of small things based on main material volume; --- Main/Source/cmdcraft.cpp | 20 -------------------- Main/Source/object.cpp | 28 +++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 9004a8f3f..720e6613f 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -3243,26 +3243,6 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) ); } - /** - * a good light emmiting crystal stone is about 100 to 200 cm3 - * smaller stones/sticks/lumps or etc, should emit less light... - */ - static col24 colBlack24 = MakeRGB24(0,0,0); - if(bChangeEmit && rpd.itSpawnMatMainVol<100){ - col24 col = itSpawn->GetEmitation(); - if(col != colBlack24){ - col24 cRed = GetRed24(col); - col24 cGreen = GetGreen24(col); - col24 cBlue = GetBlue24(col); - float fPerc = rpd.itSpawnMatMainVol/100.0; - itSpawn->SignalEmitationDecrease(MakeRGB24( - cRed - (cRed*fPerc), - cGreen - (cGreen*fPerc), - cBlue - (cBlue*fPerc) - )); - } - } - itSpawn = CheckBreakItem(bAllowBreak, rpd, itSpawn, fsCreated); if(itSpawn!=NULL){ if(fsCreated.GetSize()<200) // this will prevent a crash about "stack smashing detected". TODO to test it and provide a better solution, just comment this `if` line and split a corpse in 40 parts or more diff --git a/Main/Source/object.cpp b/Main/Source/object.cpp index b00d74bd4..913833a2b 100644 --- a/Main/Source/object.cpp +++ b/Main/Source/object.cpp @@ -468,13 +468,39 @@ truth object::AddBurningAdjective(festring& String, truth Articled) const } } +col24 CalcEmitationBasedOnVolume(col24 Emit,ulong vol) +{ + /** + * a good light emmiting crystal stone is about 100 to 200 cm3 + * smaller stones/sticks/lumps or etc, should emit less light... + */ + static col24 colBlack24 = MakeRGB24(0,0,0); + if(vol<100){ + if(Emit != colBlack24){ + col24 cRed = GetRed24(Emit); + col24 cGreen = GetGreen24(Emit); + col24 cBlue = GetBlue24(Emit); + float fPerc = vol/100.0; + Emit = MakeRGB24(cRed*fPerc, cGreen*fPerc, cBlue*fPerc); + } + } + + return Emit; +} + +bool bEnableLightsBasedOnMainMaterialVolume=true; //may affect performance? TODO user option? void object::CalculateEmitation() { Emitation = GetBaseEmitation(); if(MainMaterial) { - game::CombineLights(Emitation, MainMaterial->GetEmitation()); + game::CombineLights( + Emitation, + bEnableLightsBasedOnMainMaterialVolume ? + CalcEmitationBasedOnVolume( MainMaterial->GetEmitation(), MainMaterial->GetVolume() ) : + MainMaterial->GetEmitation() + ); if(MainMaterial->IsBurning()) { int CurrentBurnLevel = MainMaterial->GetBurnLevel(); From 35b51afe47879365002fb86d722a77e4a1fbb964 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 18 Mar 2020 01:57:44 -0300 Subject: [PATCH 016/235] WIP-lights: changing light emitation of small things based on main material volume; --- Main/Source/object.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Main/Source/object.cpp b/Main/Source/object.cpp index 913833a2b..57a7d95fe 100644 --- a/Main/Source/object.cpp +++ b/Main/Source/object.cpp @@ -19,6 +19,7 @@ #include "game.h" #include "bitmap.h" #include "save.h" +#include "dbgmsgproj.h" v2 RightArmSparkleValidityArray[128]; v2 LeftArmSparkleValidityArray[128]; @@ -477,11 +478,12 @@ col24 CalcEmitationBasedOnVolume(col24 Emit,ulong vol) static col24 colBlack24 = MakeRGB24(0,0,0); if(vol<100){ if(Emit != colBlack24){ - col24 cRed = GetRed24(Emit); - col24 cGreen = GetGreen24(Emit); - col24 cBlue = GetBlue24(Emit); float fPerc = vol/100.0; - Emit = MakeRGB24(cRed*fPerc, cGreen*fPerc, cBlue*fPerc); + col24 cRed = GetRed24(Emit)*fPerc; + col24 cGreen = GetGreen24(Emit)*fPerc; + col24 cBlue = GetBlue24(Emit)*fPerc; + Emit = MakeRGB24(cRed, cGreen, cBlue); + DBG6(Emit,vol,fPerc,cRed,cGreen,cBlue); } } @@ -492,15 +494,18 @@ bool bEnableLightsBasedOnMainMaterialVolume=true; //may affect performance? TODO void object::CalculateEmitation() { Emitation = GetBaseEmitation(); + DBG5("Base",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); if(MainMaterial) { + DBG4(MainMaterial->GetEmitation(),GetRed24(MainMaterial->GetEmitation()),GetGreen24(MainMaterial->GetEmitation()),GetBlue24(MainMaterial->GetEmitation())); game::CombineLights( Emitation, bEnableLightsBasedOnMainMaterialVolume ? CalcEmitationBasedOnVolume( MainMaterial->GetEmitation(), MainMaterial->GetVolume() ) : MainMaterial->GetEmitation() ); + DBG5("Final",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); if(MainMaterial->IsBurning()) { int CurrentBurnLevel = MainMaterial->GetBurnLevel(); @@ -508,6 +513,7 @@ void object::CalculateEmitation() game::CombineLights(Emitation, MakeRGB24(150 - 10 * CurrentBurnLevel, 120 - 8 * CurrentBurnLevel, 90 - 6 * CurrentBurnLevel)); + DBG5("FinalBurning",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); } } } From fed2c6669c27c22d328203f796964981375c8100 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 18 Mar 2020 03:38:32 -0300 Subject: [PATCH 017/235] lights: changing light emitation of small things based on main material volume; --- Main/Source/object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/object.cpp b/Main/Source/object.cpp index 57a7d95fe..767f0d352 100644 --- a/Main/Source/object.cpp +++ b/Main/Source/object.cpp @@ -478,7 +478,7 @@ col24 CalcEmitationBasedOnVolume(col24 Emit,ulong vol) static col24 colBlack24 = MakeRGB24(0,0,0); if(vol<100){ if(Emit != colBlack24){ - float fPerc = vol/100.0; + float fPerc = (50.0+(vol/2))/100.0; //there is at least 1 square of light only from 50 on, less than 50 is full darkness col24 cRed = GetRed24(Emit)*fPerc; col24 cGreen = GetGreen24(Emit)*fPerc; col24 cBlue = GetBlue24(Emit)*fPerc; From e2fa03f8d04fc921b3198d65df7dfa013d7856ed Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 18 Mar 2020 04:06:26 -0300 Subject: [PATCH 018/235] lights: changing light emitation of fluids based on it's material volume; --- Main/Include/object.h | 1 + Main/Source/item.cpp | 12 ++++++++---- Main/Source/object.cpp | 11 +++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Main/Include/object.h b/Main/Include/object.h index 6d5a6f785..7f9587e6a 100644 --- a/Main/Include/object.h +++ b/Main/Include/object.h @@ -46,6 +46,7 @@ class object : public entity, public id virtual int GetForcedVisualEffects() const { return 0; } int GetAnimationFrames() const { return GraphicData.AnimationFrames; } virtual truth IsAnimated() const { return GraphicData.AnimationFrames > 1; } + static col24 CalcEmitationBasedOnVolume(col24 Emit,ulong vol); virtual void CalculateEmitation(); void LoadMaterial(inputfile&, material*&); virtual const fearray& GetMaterialConfigChances() const = 0; diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp index b06c8e74a..681fad5eb 100644 --- a/Main/Source/item.cpp +++ b/Main/Source/item.cpp @@ -1710,10 +1710,14 @@ void item::CalculateEmitation() { object::CalculateEmitation(); - if(Fluid) - for(int c = 0; c < SquaresUnder; ++c) - for(const fluid* F = Fluid[c]; F; F = F->Next) - game::CombineLights(Emitation, F->GetEmitation()); + if(Fluid){ + for(int c = 0; c < SquaresUnder; ++c){ + for(const fluid* F = Fluid[c]; F; F = F->Next){ + game::CombineLights(Emitation, + CalcEmitationBasedOnVolume( F->GetEmitation(), F->GetLiquid()->GetVolume() ) ); + } + } + } } void item::FillFluidVector(fluidvector& Vector, int SquareIndex) const diff --git a/Main/Source/object.cpp b/Main/Source/object.cpp index 767f0d352..457f0d2d7 100644 --- a/Main/Source/object.cpp +++ b/Main/Source/object.cpp @@ -469,7 +469,7 @@ truth object::AddBurningAdjective(festring& String, truth Articled) const } } -col24 CalcEmitationBasedOnVolume(col24 Emit,ulong vol) +col24 object::CalcEmitationBasedOnVolume(col24 Emit,ulong vol) { /** * a good light emmiting crystal stone is about 100 to 200 cm3 @@ -490,7 +490,6 @@ col24 CalcEmitationBasedOnVolume(col24 Emit,ulong vol) return Emit; } -bool bEnableLightsBasedOnMainMaterialVolume=true; //may affect performance? TODO user option? void object::CalculateEmitation() { Emitation = GetBaseEmitation(); @@ -499,12 +498,8 @@ void object::CalculateEmitation() if(MainMaterial) { DBG4(MainMaterial->GetEmitation(),GetRed24(MainMaterial->GetEmitation()),GetGreen24(MainMaterial->GetEmitation()),GetBlue24(MainMaterial->GetEmitation())); - game::CombineLights( - Emitation, - bEnableLightsBasedOnMainMaterialVolume ? - CalcEmitationBasedOnVolume( MainMaterial->GetEmitation(), MainMaterial->GetVolume() ) : - MainMaterial->GetEmitation() - ); + game::CombineLights(Emitation, + CalcEmitationBasedOnVolume( MainMaterial->GetEmitation(), MainMaterial->GetVolume() ) ); DBG5("Final",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); if(MainMaterial->IsBurning()) { From 5bcc8600c20e4a75286595fddc11e74389097717 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 18 Mar 2020 04:36:29 -0300 Subject: [PATCH 019/235] lights: changing light emitation of fluids based on it's material volume (needs more coding tho...); --- Main/Source/item.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp index 681fad5eb..e79464304 100644 --- a/Main/Source/item.cpp +++ b/Main/Source/item.cpp @@ -1073,7 +1073,10 @@ void item::Be() game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); } else + { --LifeExpectancy; + //TODO fluids emitation on weapons require updating in case it lowers with time, this didnt work: if(Fluid)CalculateEmitation(); + } } } @@ -1713,6 +1716,7 @@ void item::CalculateEmitation() if(Fluid){ for(int c = 0; c < SquaresUnder; ++c){ for(const fluid* F = Fluid[c]; F; F = F->Next){ + DBG2(F->GetEmitation(),F->GetLiquid()->GetVolume()); game::CombineLights(Emitation, CalcEmitationBasedOnVolume( F->GetEmitation(), F->GetLiquid()->GetVolume() ) ); } From e3582ea7dd97732e70d0ca44d55c186658c06a4e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 18 Mar 2020 17:52:09 -0300 Subject: [PATCH 020/235] sfx: 3 new web tear down sounds; --- Sound/SoundEffects.cfg | 2 +- Sound/teardown1.wav | Bin 0 -> 46100 bytes Sound/teardown2.wav | Bin 0 -> 44618 bytes Sound/teardown3.wav | Bin 0 -> 163636 bytes 4 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 Sound/teardown1.wav create mode 100644 Sound/teardown2.wav create mode 100644 Sound/teardown3.wav diff --git a/Sound/SoundEffects.cfg b/Sound/SoundEffects.cfg index 8b3df2906..31066850a 100644 --- a/Sound/SoundEffects.cfg +++ b/Sound/SoundEffects.cfg @@ -112,7 +112,7 @@ mine; mine.wav;.*You.*hear.*a.*faint.*thump.* #TODO: gas trap slip; slip.wav;.*on banana peel.* -web; teardown.wav;.*tear the web down.* +web; teardown1.wav, teardown2.wav, teardown3.wav;.*tear the web down.* ################### ### Player Hurt ### diff --git a/Sound/teardown1.wav b/Sound/teardown1.wav new file mode 100644 index 0000000000000000000000000000000000000000..2eff28692dc47f41b2dcab6dba157a64844e75e9 GIT binary patch literal 46100 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 up?8Nc;^yW#S7y$-s(DexP>>G2H9yHI7KYz+co+ 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

XI?fud2_ade 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 literal 0 HcmV?d00001 diff --git a/Sound/teardown3.wav b/Sound/teardown3.wav new file mode 100644 index 0000000000000000000000000000000000000000..47f9d4923e761a4827bd117b6590c829f17b1c4e GIT binary patch literal 163636 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

Ehed{98A|^_ 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 literal 0 HcmV?d00001 From 822be84d70e71474234e6ea8f1b70afd9e3c5bd3 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 20 Mar 2020 23:21:02 -0300 Subject: [PATCH 021/235] WIP-lights: trying to fix a bug when splitting a crystal stone: 1st - 1cm3 receives full light strength. 2nd - player w/o any light source receives the same light. 3rd - the dungeon map square where it was cut receives that same light; --- Main/Source/cmdcraft.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 720e6613f..aed5a7714 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -3249,7 +3249,12 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) fsCreated << itSpawn->GetName(INDEFINITE); itSpawn->MoveTo(rpd.rc.H()->GetStack());DBGLN; } - + + //TODO splitting a crystal stone that emits light, is creating a bug that is only fixed after the savegame is reloaded... these below cause no harm but also doesnt fix it... + itSpawn->CalculateAll(); + //rpd.rc.H()->CalculateAll(); + //rpd.rc.H()->SignalEmitationDecrease(rpd.rc.H()->GetEmitation()); + return itSpawn; } From 3b43d0cf58f07c0c75bd0ae4a6ad8ec745779b9f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 21 Mar 2020 00:12:05 -0300 Subject: [PATCH 022/235] Lights: added experimental (still buggy after split command) optional feature to lower light emitation from small crystals; --- Main/Include/iconf.h | 2 ++ Main/Source/cmdcraft.cpp | 1 + Main/Source/iconf.cpp | 5 +++++ Main/Source/object.cpp | 4 ++++ 4 files changed, 12 insertions(+) diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index 5781ae8d9..e1ad12ae5 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -38,6 +38,7 @@ class ivanconfig static truth IsAllowImportOldSavegame(){ return AllowImportOldSavegame.Value; } static long GetAltSilhouette() { return AltSilhouette.Value; } static truth IsHideWeirdHitAnimationsThatLookLikeMiss(){return HideWeirdHitAnimationsThatLookLikeMiss.Value;} + static truth IsUseLightEmiterBasedOnVolume(){return UseLightEmiterBasedOnVolume.Value;} static int GetAltSilhouettePreventColorGlitch(){return AltSilhouettePreventColorGlitch.Value;} static int GetShowMap(){return ShowMap.Value;} static truth IsShowMapAtDetectMaterial() { return ShowMapAtDetectMaterial.Value; } @@ -192,6 +193,7 @@ class ivanconfig static truthoption AllowImportOldSavegame; static cycleoption ShowItemsAtPlayerSquare; static truthoption HideWeirdHitAnimationsThatLookLikeMiss; + static truthoption UseLightEmiterBasedOnVolume; static cycleoption AltSilhouettePreventColorGlitch; static cycleoption ShowMap; static truthoption ShowMapAtDetectMaterial; diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index aed5a7714..d332ea0f9 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -3254,6 +3254,7 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) itSpawn->CalculateAll(); //rpd.rc.H()->CalculateAll(); //rpd.rc.H()->SignalEmitationDecrease(rpd.rc.H()->GetEmitation()); + //rpd.rc.H()->GetSquareUnder()-> return itSpawn; } diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 7df4c94a5..8d7face05 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -174,6 +174,10 @@ truthoption ivanconfig::HideWeirdHitAnimationsThatLookLikeMiss("HideWeirdHitAnim "Hide hit animations that look like miss", "", true); +truthoption ivanconfig::UseLightEmiterBasedOnVolume("UseLightEmiterBasedOnVolume", + "Small crystal rocks etc. will emit less light.", + "This experimental feature still has bugs that happen when splitting rocks etc. Most are fixed after restarting the game.", + false); truthoption ivanconfig::ShowFullDungeonName("ShowFullDungeonName", "Show full name of current dungeon", "", @@ -1122,6 +1126,7 @@ void ivanconfig::Initialize() fsCategory="Advanced Options"; configsystem::AddOption(fsCategory,&AllowImportOldSavegame); configsystem::AddOption(fsCategory,&HideWeirdHitAnimationsThatLookLikeMiss); + configsystem::AddOption(fsCategory,&UseLightEmiterBasedOnVolume); /******************************** * LOAD AND APPLY some SETTINGS * diff --git a/Main/Source/object.cpp b/Main/Source/object.cpp index 457f0d2d7..71fcbd5ec 100644 --- a/Main/Source/object.cpp +++ b/Main/Source/object.cpp @@ -19,6 +19,7 @@ #include "game.h" #include "bitmap.h" #include "save.h" +#include "iconf.h" #include "dbgmsgproj.h" v2 RightArmSparkleValidityArray[128]; @@ -471,6 +472,9 @@ truth object::AddBurningAdjective(festring& String, truth Articled) const col24 object::CalcEmitationBasedOnVolume(col24 Emit,ulong vol) { + if(!ivanconfig::IsUseLightEmiterBasedOnVolume()) + return Emit; + /** * a good light emmiting crystal stone is about 100 to 200 cm3 * smaller stones/sticks/lumps or etc, should emit less light... From 9dec26cc8612745dd8c46a2be90c9ab4a324b282 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 22 Mar 2020 20:11:10 -0300 Subject: [PATCH 023/235] WIP-Lights: lower light emitation from small crystals (less buggy now); --- Main/Include/craft.h | 1 + Main/Source/cmdcraft.cpp | 43 ++++++++++++++++++++++++++++------------ Main/Source/object.cpp | 12 +++++------ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index de19f719d..03bfd1a56 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -270,6 +270,7 @@ class craftcore { static bool IsBone(material* mat); static item* PrepareRemains(recipedata&,material*,int ForceType=CIT_NONE); + static void FinishSpawning(recipedata& rpd,item* itSpawn); static void AddSuspended(const recipedata& rpd); static void RemoveIfSuspended(const recipedata&rpd); diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index d332ea0f9..2b116f2ff 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2107,7 +2107,7 @@ struct srpSplitLump : public recipe{ return false; } - if(rpd.itSpawnTot<0){ //cut mode + if(rpd.itSpawnTot<0){ //single cut mode if(bHumanoidCorpse){ ADD_MESSAGE("This needs to be split first."); //see 'why' about necromancers above... TODO a better message? rpd.bAlreadyExplained=true; @@ -2126,6 +2126,7 @@ struct srpSplitLump : public recipe{ item* cut = craftcore::PrepareRemains(rpd,matM,craftcore::CitType(ToSplit)); cut->GetMainMaterial()->SetVolume(iCutVol); matM->SetVolume(matM->GetVolume() - iCutVol); + ToSplit->CalculateAll(); rpd.bAlreadyExplained=true; //no need to say anything return true; //TODO should take more turns? } @@ -3162,7 +3163,6 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) item* itSpawn = NULL; material* matS = NULL; bool bAllowBreak=false;DBG3(rpd.itSpawnType,rpd.itSpawnCfg,rpd.itSpawnMatSecCfg); - bool bChangeEmit=false; switch(rpd.itSpawnType){ case CIT_POTION: /** @@ -3183,15 +3183,12 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) break; case CIT_STONE: itSpawn = stone::Spawn(rpd.itSpawnCfg, NO_MATERIALS); - bChangeEmit=true; break; case CIT_LUMP: itSpawn = lump::Spawn(rpd.itSpawnCfg, NO_MATERIALS); - bChangeEmit=true; break; case CIT_STICK: itSpawn = stick::Spawn(rpd.itSpawnCfg, NO_MATERIALS); - bChangeEmit=true; break; } @@ -3247,15 +3244,20 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) if(itSpawn!=NULL){ if(fsCreated.GetSize()<200) // this will prevent a crash about "stack smashing detected". TODO to test it and provide a better solution, just comment this `if` line and split a corpse in 40 parts or more fsCreated << itSpawn->GetName(INDEFINITE); - itSpawn->MoveTo(rpd.rc.H()->GetStack());DBGLN; + craftcore::FinishSpawning(rpd,itSpawn); +// itSpawn->MoveTo(rpd.rc.H()->GetStack());DBGLN; +// +// //TODO splitting a crystal stone that emits light, is creating a bug that is only fixed after the savegame is reloaded... these below cause no harm but also doesnt fix it... +// DBG3("EmitDbgSpawned",itSpawn->GetEmitation(),itSpawn->GetVolume()); +// itSpawn->SignalEmitationDecrease(itSpawn->GetEmitation()); +// itSpawn->CalculateEmitation(); +// rpd.rc.H()->SignalEmitationDecrease(itSpawn->GetEmitation()); +// rpd.rc.H()->CalculateEmitation(); +// rpd.rc.H()->GetLSquareUnder()->SignalEmitationDecrease(itSpawn->GetEmitation()); +// rpd.rc.H()->GetLSquareUnder()->CalculateLuminance(); +// itSpawn->CalculateEmitation(); } - //TODO splitting a crystal stone that emits light, is creating a bug that is only fixed after the savegame is reloaded... these below cause no harm but also doesnt fix it... - itSpawn->CalculateAll(); - //rpd.rc.H()->CalculateAll(); - //rpd.rc.H()->SignalEmitationDecrease(rpd.rc.H()->GetEmitation()); - //rpd.rc.H()->GetSquareUnder()-> - return itSpawn; } @@ -3793,13 +3795,28 @@ item* craftcore::PrepareRemains(recipedata& rpd, material* mat, int ForceType) / craftcore::CopyDegradation(mat,itTmp->GetMainMaterial()); - rpd.rc.H()->GetStack()->AddItem(itTmp); + //rpd.rc.H()->GetStack()->AddItem(itTmp); + FinishSpawning(rpd,itTmp); ADD_MESSAGE("%s was recovered.", itTmp->GetName(DEFINITE).CStr()); return itTmp; } +void craftcore::FinishSpawning(recipedata& rpd,item* itSpawn){ + itSpawn->MoveTo(rpd.rc.H()->GetStack());DBGLN; + + //TODO splitting a crystal stone that emits light, is creating a bug that is only fixed after the savegame is reloaded... these below cause no harm but also doesnt fix it... + DBG3("EmitDbgSpawned",itSpawn->GetEmitation(),itSpawn->GetVolume()); + itSpawn->SignalEmitationDecrease(itSpawn->GetEmitation()); + itSpawn->CalculateEmitation(); + rpd.rc.H()->SignalEmitationDecrease(itSpawn->GetEmitation()); + rpd.rc.H()->CalculateEmitation(); + rpd.rc.H()->GetLSquareUnder()->SignalEmitationDecrease(itSpawn->GetEmitation()); + rpd.rc.H()->GetLSquareUnder()->CalculateLuminance(); + itSpawn->CalculateEmitation(); +} + std::vector vBone; bool craftcore::IsBone(material* mat) { diff --git a/Main/Source/object.cpp b/Main/Source/object.cpp index 71fcbd5ec..68ab06879 100644 --- a/Main/Source/object.cpp +++ b/Main/Source/object.cpp @@ -479,15 +479,15 @@ col24 object::CalcEmitationBasedOnVolume(col24 Emit,ulong vol) * a good light emmiting crystal stone is about 100 to 200 cm3 * smaller stones/sticks/lumps or etc, should emit less light... */ - static col24 colBlack24 = MakeRGB24(0,0,0); + //static col24 colBlack24 = MakeRGB24(0,0,0); if(vol<100){ - if(Emit != colBlack24){ + if(Emit != 0){//colBlack24){ float fPerc = (50.0+(vol/2))/100.0; //there is at least 1 square of light only from 50 on, less than 50 is full darkness col24 cRed = GetRed24(Emit)*fPerc; col24 cGreen = GetGreen24(Emit)*fPerc; col24 cBlue = GetBlue24(Emit)*fPerc; Emit = MakeRGB24(cRed, cGreen, cBlue); - DBG6(Emit,vol,fPerc,cRed,cGreen,cBlue); + DBG7("EmitDbgVol",Emit,vol,fPerc,cRed,cGreen,cBlue); } } @@ -497,14 +497,14 @@ col24 object::CalcEmitationBasedOnVolume(col24 Emit,ulong vol) void object::CalculateEmitation() { Emitation = GetBaseEmitation(); - DBG5("Base",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); + DBG5("EmitDbgBase",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); if(MainMaterial) { DBG4(MainMaterial->GetEmitation(),GetRed24(MainMaterial->GetEmitation()),GetGreen24(MainMaterial->GetEmitation()),GetBlue24(MainMaterial->GetEmitation())); game::CombineLights(Emitation, CalcEmitationBasedOnVolume( MainMaterial->GetEmitation(), MainMaterial->GetVolume() ) ); - DBG5("Final",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); + DBG5("EmitDbgFinal",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); if(MainMaterial->IsBurning()) { int CurrentBurnLevel = MainMaterial->GetBurnLevel(); @@ -512,7 +512,7 @@ void object::CalculateEmitation() game::CombineLights(Emitation, MakeRGB24(150 - 10 * CurrentBurnLevel, 120 - 8 * CurrentBurnLevel, 90 - 6 * CurrentBurnLevel)); - DBG5("FinalBurning",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); + DBG5("EmitDbgFinalBurning",Emitation,GetRed24(Emitation),GetGreen24(Emitation),GetBlue24(Emitation)); } } } From 91e0f278c95402207052baf503d49af783220ee2 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 22 Mar 2020 21:38:39 -0300 Subject: [PATCH 024/235] WIP-Lights: lower light emitation from small crystals (making sure CalculateAll() is called after volume change); --- Main/Include/craft.h | 2 +- Main/Source/cmdcraft.cpp | 44 +++++++++++++++++----------------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index 03bfd1a56..841c7e77d 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -269,7 +269,7 @@ class craftcore { static bool IsWooden(material* mat); static bool IsBone(material* mat); - static item* PrepareRemains(recipedata&,material*,int ForceType=CIT_NONE); + static item* PrepareRemains(recipedata&,material*,int ForceType=CIT_NONE, long volume = 0); static void FinishSpawning(recipedata& rpd,item* itSpawn); static void AddSuspended(const recipedata& rpd); diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 2b116f2ff..f623b1604 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -1049,8 +1049,8 @@ struct recipe{ // bool bForceLump = CI.fUsablePercVol<1.0; // item* lumpR = craftcore::PrepareRemains(rpd,matM,bForceLump); - item* lumpR = craftcore::PrepareRemains(rpd,matM); - lumpR->GetMainMaterial()->SetVolume(lRemainingVol); + item* lumpR = craftcore::PrepareRemains(rpd,matM,CIT_NONE,lRemainingVol); + //lumpR->GetMainMaterial()->SetVolume(lRemainingVol); lumpMix(vi,lumpR,rpd.bSpendCurrentTurn); @@ -1151,6 +1151,7 @@ struct recipe{ if(lumpAtInv->GetMainMaterial()->GetConfig() == lumpToMix->GetMainMaterial()->GetConfig()){ lumpAtInv->GetMainMaterial()->SetVolume( lumpAtInv->GetMainMaterial()->GetVolume() + lumpToMix->GetMainMaterial()->GetVolume()); + lumpAtInv->CalculateAll(); craftcore::SendToHellSafely(lumpToMix); DBG5("SentToHell",lumpToMix,lumpToMix->GetID(),lumpAtInv,lumpAtInv->GetID()); // lumpToMix->RemoveFromSlot(); @@ -2123,8 +2124,8 @@ struct srpSplitLump : public recipe{ return false; } - item* cut = craftcore::PrepareRemains(rpd,matM,craftcore::CitType(ToSplit)); - cut->GetMainMaterial()->SetVolume(iCutVol); + item* cut = craftcore::PrepareRemains(rpd,matM,craftcore::CitType(ToSplit),iCutVol); + //cut->GetMainMaterial()->SetVolume(iCutVol); matM->SetVolume(matM->GetVolume() - iCutVol); ToSplit->CalculateAll(); rpd.bAlreadyExplained=true; //no need to say anything @@ -3245,20 +3246,9 @@ item* crafthandle::SpawnItem(recipedata& rpd, festring& fsCreated) if(fsCreated.GetSize()<200) // this will prevent a crash about "stack smashing detected". TODO to test it and provide a better solution, just comment this `if` line and split a corpse in 40 parts or more fsCreated << itSpawn->GetName(INDEFINITE); craftcore::FinishSpawning(rpd,itSpawn); -// itSpawn->MoveTo(rpd.rc.H()->GetStack());DBGLN; -// -// //TODO splitting a crystal stone that emits light, is creating a bug that is only fixed after the savegame is reloaded... these below cause no harm but also doesnt fix it... -// DBG3("EmitDbgSpawned",itSpawn->GetEmitation(),itSpawn->GetVolume()); -// itSpawn->SignalEmitationDecrease(itSpawn->GetEmitation()); -// itSpawn->CalculateEmitation(); -// rpd.rc.H()->SignalEmitationDecrease(itSpawn->GetEmitation()); -// rpd.rc.H()->CalculateEmitation(); -// rpd.rc.H()->GetLSquareUnder()->SignalEmitationDecrease(itSpawn->GetEmitation()); -// rpd.rc.H()->GetLSquareUnder()->CalculateLuminance(); -// itSpawn->CalculateEmitation(); + return itSpawn; } - - return itSpawn; + return NULL; } void crafthandle::CraftWorkTurn(recipedata& rpd){ DBG1(rpd.iRemainingTurnsToFinish); @@ -3720,9 +3710,10 @@ cfestring crafthandle::DestroyIngredients(recipedata& rpd){ * @param rpd * @param mat * @param ForceType CIT_... stick, lump, stone + * @param volume is the main material volume, it is important to be set before item->CalculateAll() * @return */ -item* craftcore::PrepareRemains(recipedata& rpd, material* mat, int ForceType) //TODO force type could be a class (type) reference? +item* craftcore::PrepareRemains(recipedata& rpd, material* mat, int ForceType, long volume) //TODO force type could be a class (type) reference? { if(mat==NULL) ABORT("NULL remains material"); @@ -3792,10 +3783,12 @@ item* craftcore::PrepareRemains(recipedata& rpd, material* mat, int ForceType) / // delete itTmp->SetMainMaterial(material::MakeMaterial(mat->GetConfig(),mat->GetVolume())); delete itTmp->SetMainMaterial(CreateMaterial(mat)); + + if(volume>0) + itTmp->GetMainMaterial()->SetVolume(volume); craftcore::CopyDegradation(mat,itTmp->GetMainMaterial()); - //rpd.rc.H()->GetStack()->AddItem(itTmp); FinishSpawning(rpd,itTmp); ADD_MESSAGE("%s was recovered.", itTmp->GetName(DEFINITE).CStr()); @@ -3806,15 +3799,16 @@ item* craftcore::PrepareRemains(recipedata& rpd, material* mat, int ForceType) / void craftcore::FinishSpawning(recipedata& rpd,item* itSpawn){ itSpawn->MoveTo(rpd.rc.H()->GetStack());DBGLN; - //TODO splitting a crystal stone that emits light, is creating a bug that is only fixed after the savegame is reloaded... these below cause no harm but also doesnt fix it... + //TODO splitting a crystal stone that emits light, + //is creating a bug that is only fixed after the savegame is + //reloaded... these below cause no harm but also doesnt fix it... DBG3("EmitDbgSpawned",itSpawn->GetEmitation(),itSpawn->GetVolume()); - itSpawn->SignalEmitationDecrease(itSpawn->GetEmitation()); itSpawn->CalculateEmitation(); - rpd.rc.H()->SignalEmitationDecrease(itSpawn->GetEmitation()); - rpd.rc.H()->CalculateEmitation(); - rpd.rc.H()->GetLSquareUnder()->SignalEmitationDecrease(itSpawn->GetEmitation()); +// itSpawn->SignalEmitationDecrease(itSpawn->GetEmitation()); +// rpd.rc.H()->SignalEmitationDecrease(itSpawn->GetEmitation()); +// rpd.rc.H()->CalculateEmitation(); + rpd.rc.H()->GetLSquareUnder()->SignalEmitationDecrease(rpd.rc.H()->GetLSquareUnder()->GetEmitation()); rpd.rc.H()->GetLSquareUnder()->CalculateLuminance(); - itSpawn->CalculateEmitation(); } std::vector vBone; From c931664e56cd6ec1de9f29c63919c785d62731fc Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 22 Mar 2020 22:02:25 -0300 Subject: [PATCH 025/235] Lights: lower light emitation from small crystals, looks good now, just needs more tests...; --- Main/Source/cmdcraft.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index f623b1604..c5837b152 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -3799,16 +3799,19 @@ item* craftcore::PrepareRemains(recipedata& rpd, material* mat, int ForceType, l void craftcore::FinishSpawning(recipedata& rpd,item* itSpawn){ itSpawn->MoveTo(rpd.rc.H()->GetStack());DBGLN; - //TODO splitting a crystal stone that emits light, - //is creating a bug that is only fixed after the savegame is - //reloaded... these below cause no harm but also doesnt fix it... DBG3("EmitDbgSpawned",itSpawn->GetEmitation(),itSpawn->GetVolume()); - itSpawn->CalculateEmitation(); -// itSpawn->SignalEmitationDecrease(itSpawn->GetEmitation()); -// rpd.rc.H()->SignalEmitationDecrease(itSpawn->GetEmitation()); -// rpd.rc.H()->CalculateEmitation(); - rpd.rc.H()->GetLSquareUnder()->SignalEmitationDecrease(rpd.rc.H()->GetLSquareUnder()->GetEmitation()); - rpd.rc.H()->GetLSquareUnder()->CalculateLuminance(); + if(itSpawn->GetEmitation()>0){ //TODO is there a better way to do this emitation fix? + // uses the previous emitation to fix everywhere before recalculating item emitation + rpd.rc.H()->SignalEmitationDecrease(itSpawn->GetEmitation()); + rpd.rc.H()->CalculateEmitation(); + + rpd.rc.H()->GetLSquareUnder()->SignalEmitationDecrease(itSpawn->GetEmitation()); + rpd.rc.H()->GetLSquareUnder()->CalculateLuminance(); + + // last + itSpawn->SignalEmitationDecrease(itSpawn->GetEmitation()); + itSpawn->CalculateEmitation(); + } } std::vector vBone; From 2a3f7d104872ce69da7bd991757dcbf5c85689df Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 23 Mar 2020 21:42:21 -0300 Subject: [PATCH 026/235] minor fix: add explicit braces to avoid dangling else --- Main/Source/devcons.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 20f5b19ae..2f1df1a2f 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -326,7 +326,7 @@ void devcons::Help(festring fsFilter) festring fsWM; for(int j=0;j Date: Mon, 23 Mar 2020 22:12:52 -0300 Subject: [PATCH 027/235] minor fix: add explicit braces to avoid dangling else --- Main/Source/devcons.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 2f1df1a2f..be7110bac 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -325,8 +325,13 @@ void devcons::Help(festring fsFilter) { festring fsWM; for(int j=0;j Date: Thu, 26 Mar 2020 21:21:42 -0300 Subject: [PATCH 028/235] WIP-CustomMoveKeys; --- FeLib/Include/felibdef.h | 1 + Main/Include/command.h | 1 + Main/Include/game.h | 2 ++ Main/Source/command.cpp | 2 ++ Main/Source/game.cpp | 10 ++++++++++ Main/Source/iconf.cpp | 29 ++++++++++++++++------------- 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/FeLib/Include/felibdef.h b/FeLib/Include/felibdef.h index 3d40e85b5..c2b006116 100644 --- a/FeLib/Include/felibdef.h +++ b/FeLib/Include/felibdef.h @@ -262,5 +262,6 @@ inline int GetMinColor24(col24 Color) #define DIR_NORM 0 #define DIR_ALT 1 #define DIR_HACK 2 +#define DIR_CUSTOM 3 #endif diff --git a/Main/Include/command.h b/Main/Include/command.h index 98283872a..dc90eec0f 100644 --- a/Main/Include/command.h +++ b/Main/Include/command.h @@ -32,6 +32,7 @@ class command char Key1; char Key2; char Key3; + int Key4; // custom keys can be more than 1 char long ex.: KEY_HOME is 0x147 or 0x0147 truth UsableInWilderness; truth WizardModeFunction; }; diff --git a/Main/Include/game.h b/Main/Include/game.h index 2c04481bf..1dc17cae8 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -505,6 +505,7 @@ class game static int GetCurrentDungeonTurnsCount(){return iCurrentDungeonTurn;} static int GetSaveFileVersionHardcoded(); static void ValidateCommandKeys(char Key1,char Key2,char Key3); + static void AssignCustomCommandKeys(); private: static void UpdateCameraCoordinate(int&, int, int, int); static cchar* const Alignment[]; @@ -514,6 +515,7 @@ class game static cint MoveNormalCommandKey[]; static cint MoveAbnormalCommandKey[]; static cint MoveNetHackCommandKey[]; + static int MoveCustomCommandKey[]; static cv2 MoveVector[]; static cv2 ClockwiseMoveVector[]; static cv2 RelativeMoveVector[]; diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 276c65a97..9854e33f1 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -68,6 +68,8 @@ char command::GetKey() const return Key2; case DIR_HACK: // Nethack return Key3; + case DIR_CUSTOM: + return Key4; default: ABORT("This is not Vim!"); return Key1; diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 0fc6aa59e..b9ac5d882 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -154,6 +154,7 @@ cint game::MoveNormalCommandKey[] = { KEY_HOME, KEY_UP, KEY_PAGE_UP, KEY_LEFT, KEY_RIGHT, KEY_END, KEY_DOWN, KEY_PAGE_DOWN, '.' }; cint game::MoveAbnormalCommandKey[] = { '7', '8', '9', 'u', 'o', 'j', 'k', 'l', '.' }; cint game::MoveNetHackCommandKey[] = { 'y', 'k', 'u', 'h', 'l', 'b', 'j', 'n', '.' }; +int game::MoveCustomCommandKey[] = { '.', '.', '.', '.', '.', '.', '.', '.', '.' }; cv2 game::MoveVector[] = { v2(-1, -1), v2(0, -1), v2(1, -1), v2(-1, 0), v2(1, 0), v2(-1, 1), v2(0, 1), v2(1, 1), v2(0, 0) }; @@ -5760,6 +5761,13 @@ bonesghost* game::CreateGhost() return Ghost; } +void game::AssignCustomCommandKeys() +{ + for(int i=0;i<=8;i++){ //TODO show on screen and save to a new cfg file + game::MoveCustomCommandKey[i]='.'; + } +} + void game::ValidateCommandKeys(char Key1,char Key2,char Key3) { static const int iTot=9; @@ -5796,6 +5804,8 @@ int game::GetMoveCommandKey(int I) return MoveAbnormalCommandKey[I]; case DIR_HACK: return MoveNetHackCommandKey[I]; + case DIR_CUSTOM: + return MoveCustomCommandKey[I]; default: ABORT("This is not Emacs!"); return MoveNormalCommandKey[I]; diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 8d7face05..f96437675 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -279,7 +279,7 @@ cycleoption ivanconfig::AltSilhouettePreventColorGlitch("AltSilhouettePreventCol cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", "Movement control scheme", "Select keybindings for movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys.", - DIR_NORM, 3, // {default value, number of options to cycle through} + DIR_NORM, 4, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SmartOpenCloseApply("SmartOpenCloseApply", "Smart open/close/apply behavior", @@ -532,18 +532,21 @@ void ivanconfig::AltSilhouetteDisplayer(const cycleoption* O, festring& Entry) void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) { - switch(O->Value) - { - case DIR_NORM: - Entry << CONST_S("Normal"); - break; - case DIR_ALT: - Entry << CONST_S("Alternative"); - break; - case DIR_HACK: - Entry << CONST_S("NetHack"); - break; - } + switch(O->Value) + { + case DIR_NORM: + Entry << CONST_S("Normal"); + break; + case DIR_ALT: + Entry << CONST_S("Alternative"); + break; + case DIR_HACK: + Entry << CONST_S("NetHack"); + break; + case DIR_CUSTOM: + Entry << CONST_S("Custom"); + break; + } } void ivanconfig::MIDIOutputDeviceDisplayer(const cycleoption* O, festring& Entry) From bc145aa078870eeeb7b931e80db9757ba80e183a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 26 Mar 2020 21:47:40 -0300 Subject: [PATCH 029/235] WIP-CustomMoveKeys: now needs test file; --- Main/Include/game.h | 2 +- Main/Source/game.cpp | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Main/Include/game.h b/Main/Include/game.h index 1dc17cae8..7cc184da8 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -505,7 +505,7 @@ class game static int GetCurrentDungeonTurnsCount(){return iCurrentDungeonTurn;} static int GetSaveFileVersionHardcoded(); static void ValidateCommandKeys(char Key1,char Key2,char Key3); - static void AssignCustomCommandKeys(); + static void LoadCustomCommandKeys(); private: static void UpdateCameraCoordinate(int&, int, int, int); static cchar* const Alignment[]; diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index b9ac5d882..3dd562ee6 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5761,10 +5761,19 @@ bonesghost* game::CreateGhost() return Ghost; } -void game::AssignCustomCommandKeys() -{ - for(int i=0;i<=8;i++){ //TODO show on screen and save to a new cfg file - game::MoveCustomCommandKey[i]='.'; +void game::LoadCustomCommandKeys() +{ + static festring fsFile = game::GetUserDataDir() + "CustomCommandKeys.cfg"; + FILE *fl = fopen(fsFile.CStr(), "rt"); + + festring Line; + int index=0; + while((Line = getstr(fNew, false)) != "") + { + std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; + game::MoveCustomCommandKey[index]=atoi(Line.CStr()); + std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; + index++; } } @@ -5783,6 +5792,7 @@ void game::ValidateCommandKeys(char Key1,char Key2,char Key3) pa=game::MoveAbnormalCommandKey; Key=Key2; break; case DIR_HACK: pa=game::MoveNetHackCommandKey; Key=Key3; break; + //TODO should? DIR_CUSTOM be checked here too? } for(int j=0;j Date: Thu, 26 Mar 2020 22:31:33 -0300 Subject: [PATCH 030/235] WIP-CustomMoveKeys: needs testing now; --- Main/Include/game.h | 1 + Main/Source/game.cpp | 9 +++++++-- Main/Source/iconf.cpp | 4 +++- Main/Source/main.cpp | 23 +++++++++++++++++++++++ Main/Source/message.cpp | 1 - 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Main/Include/game.h b/Main/Include/game.h index 7cc184da8..73d4b879e 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -169,6 +169,7 @@ class areachangerequest { }; typedef void (*dbgdrawoverlay)(); #define AUTOSAVE_SUFFIX ".AutoSave" +#define CUSTOM_KEYS_FILENAME "CustomCommandKeys.cfg" class game { public: diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 3dd562ee6..fb05cc560 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5763,13 +5763,18 @@ bonesghost* game::CreateGhost() void game::LoadCustomCommandKeys() { - static festring fsFile = game::GetUserDataDir() + "CustomCommandKeys.cfg"; + static festring fsFile = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; FILE *fl = fopen(fsFile.CStr(), "rt"); + if(!fl)return; festring Line; int index=0; - while((Line = getstr(fNew, false)) != "") + static int iBuffSz=7; + char str[iBuffSz]; +// while((Line = getstr(fl, false)) != "") + while((Line = fgets(str, iBuffSz, fl)) != "") { + if(index>8)break; //can just ignore tho.. ABORT("more than 9 lines"); std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; game::MoveCustomCommandKey[index]=atoi(Line.CStr()); std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index f96437675..072fb71fc 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -278,7 +278,7 @@ cycleoption ivanconfig::AltSilhouettePreventColorGlitch("AltSilhouettePreventCol &AltSilhouettePreventColorGlitchDisplayer); cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", "Movement control scheme", - "Select keybindings for movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys.", + "Select keybindings for movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys. To set custom keys you need to exit the game and run it with the parameter --genmvkeys (for now).", DIR_NORM, 4, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SmartOpenCloseApply("SmartOpenCloseApply", @@ -1151,6 +1151,8 @@ void ivanconfig::Initialize() CalculateContrastLuminance(); audio::ChangeMIDIOutputDevice(MIDIOutputDevice.Value); audio::SetVolumeLevel(Volume.Value); + + game::LoadCustomCommandKeys(); //TODO re-use changer methods for above configs too to avoid duplicating the algo? FrameSkipChanger(NULL,FrameSkip.Value); diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index 1f6f5d527..a67a592e8 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -11,6 +11,7 @@ */ #include +#include #ifdef __DJGPP__ #include @@ -104,10 +105,32 @@ int main(int argc, char** argv) return 0; } + if(argc > 1 && festring(argv[1]) == "--genmvkeys") + { + festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; + FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); + for(int i=0;i<=8;i++){ + switch(i){ + case 0: std::cout << "Upper Left" << std::endl; break; + case 1: std::cout << "Up" << std::endl; break; + case 2: std::cout << "Upper Right" << std::endl; break; + case 3: std::cout << "Left" << std::endl; break; + case 4: std::cout << "Right" << std::endl; break; + case 5: std::cout << "Lower Left" << std::endl; break; + case 6: std::cout << "Down" << std::endl; break; + case 7: std::cout << "Lower Right" << std::endl; break; + case 8: std::cout << "Stop" << std::endl; break; + } + //fprintf(fl, "%04d\n", globalwindowhandler::GetKey()); + fprintf(fl, "%04X\n", globalwindowhandler::GetKey()); + } + } + if(argc > 1 && festring(argv[1]) == "--help") { std::cout << "--defgen Generate defines validator source file. " << std::endl; std::cout << "--defval Validate defines. " << std::endl; + std::cout << "--genmvkeys Generate custom move keys cfg file. " << std::endl; std::cout << "--version Show current game version. " << std::endl; return 0; } diff --git a/Main/Source/message.cpp b/Main/Source/message.cpp index 43f906acb..c54bf14c7 100644 --- a/Main/Source/message.cpp +++ b/Main/Source/message.cpp @@ -312,7 +312,6 @@ int soundsystem::addFile(festring filename) { } bool eol = false; - festring getstr(FILE *f, truth word) { if(eol && word) return CONST_S(""); From d6081c48306ed0c21513bca84b8186e905c1d655 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 26 Mar 2020 23:01:53 -0300 Subject: [PATCH 031/235] WIP-CustomMoveKeys: needs fixing; --- Main/Source/game.cpp | 1 + Main/Source/iconf.cpp | 33 +++++++++++++++++++++++++++++++-- Main/Source/main.cpp | 43 ++++++++++++++++++++++--------------------- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index fb05cc560..26ed8913b 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5774,6 +5774,7 @@ void game::LoadCustomCommandKeys() // while((Line = getstr(fl, false)) != "") while((Line = fgets(str, iBuffSz, fl)) != "") { + if(Line.IsEmpty())break; if(index>8)break; //can just ignore tho.. ABORT("more than 9 lines"); std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; game::MoveCustomCommandKey[index]=atoi(Line.CStr()); diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 072fb71fc..935c2d7e8 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -278,7 +278,7 @@ cycleoption ivanconfig::AltSilhouettePreventColorGlitch("AltSilhouettePreventCol &AltSilhouettePreventColorGlitchDisplayer); cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", "Movement control scheme", - "Select keybindings for movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys. To set custom keys you need to exit the game and run it with the parameter --genmvkeys (for now).", + "Select keybindings for movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys.", DIR_NORM, 4, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SmartOpenCloseApply("SmartOpenCloseApply", @@ -530,6 +530,33 @@ void ivanconfig::AltSilhouetteDisplayer(const cycleoption* O, festring& Entry) } } + +void configureCustomKeys() +{ + festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; + FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); + int iKey; + festring fsAsk; + for(int i=0;i<=8;i++){ + switch(i){ + case 0: fsAsk="Upper Left";break; + case 1: fsAsk="Up"; break; + case 2: fsAsk="Upper Right"; break; + case 3: fsAsk="Left"; break; + case 4: fsAsk="Right"; break; + case 5: fsAsk="Lower Left"; break; + case 6: fsAsk="Down"; break; + case 7: fsAsk="Lower Right"; break; + case 8: fsAsk="Stop"; break; + } + iKey=game::AskForKeyPress(fsAsk); + //fprintf(fl, "%04d\n", globalwindowhandler::GetKey()); + //fprintf(fl, "%04X\n", globalwindowhandler::ReadKey()); + fprintf(fl, "%04X\n", iKey); + } + fclose(fl); +} + void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) { switch(O->Value) @@ -545,6 +572,7 @@ void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) break; case DIR_CUSTOM: Entry << CONST_S("Custom"); + configureCustomKeys(); break; } } @@ -1152,7 +1180,8 @@ void ivanconfig::Initialize() audio::ChangeMIDIOutputDevice(MIDIOutputDevice.Value); audio::SetVolumeLevel(Volume.Value); - game::LoadCustomCommandKeys(); + if(ivanconfig::GetDirectionKeyMap()==DIR_CUSTOM) + game::LoadCustomCommandKeys(); //TODO re-use changer methods for above configs too to avoid duplicating the algo? FrameSkipChanger(NULL,FrameSkip.Value); diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index a67a592e8..7f0b39524 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -105,32 +105,33 @@ int main(int argc, char** argv) return 0; } - if(argc > 1 && festring(argv[1]) == "--genmvkeys") - { - festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; - FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); - for(int i=0;i<=8;i++){ - switch(i){ - case 0: std::cout << "Upper Left" << std::endl; break; - case 1: std::cout << "Up" << std::endl; break; - case 2: std::cout << "Upper Right" << std::endl; break; - case 3: std::cout << "Left" << std::endl; break; - case 4: std::cout << "Right" << std::endl; break; - case 5: std::cout << "Lower Left" << std::endl; break; - case 6: std::cout << "Down" << std::endl; break; - case 7: std::cout << "Lower Right" << std::endl; break; - case 8: std::cout << "Stop" << std::endl; break; - } - //fprintf(fl, "%04d\n", globalwindowhandler::GetKey()); - fprintf(fl, "%04X\n", globalwindowhandler::GetKey()); - } - } +// if(argc > 1 && festring(argv[1]) == "--genmvkeys") +// { +// festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; +// FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); +// for(int i=0;i<=8;i++){ +// switch(i){ +// case 0: std::cout << "Upper Left" << std::endl; break; +// case 1: std::cout << "Up" << std::endl; break; +// case 2: std::cout << "Upper Right" << std::endl; break; +// case 3: std::cout << "Left" << std::endl; break; +// case 4: std::cout << "Right" << std::endl; break; +// case 5: std::cout << "Lower Left" << std::endl; break; +// case 6: std::cout << "Down" << std::endl; break; +// case 7: std::cout << "Lower Right" << std::endl; break; +// case 8: std::cout << "Stop" << std::endl; break; +// } +// //fprintf(fl, "%04d\n", globalwindowhandler::GetKey()); +// fprintf(fl, "%04X\n", globalwindowhandler::ReadKey()); +// } +// fclose(fl); +// } if(argc > 1 && festring(argv[1]) == "--help") { std::cout << "--defgen Generate defines validator source file. " << std::endl; std::cout << "--defval Validate defines. " << std::endl; - std::cout << "--genmvkeys Generate custom move keys cfg file. " << std::endl; +// std::cout << "--genmvkeys Generate custom move keys cfg file. " << std::endl; std::cout << "--version Show current game version. " << std::endl; return 0; } From 311e298dfaa4533e05d1a3600e096f5a53ca73d0 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 26 Mar 2020 23:39:34 -0300 Subject: [PATCH 032/235] WIP-CustomMoveKeys: needs fixing; --- Main/Source/game.cpp | 5 +++-- Main/Source/iconf.cpp | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 26ed8913b..d65290f92 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5775,11 +5775,12 @@ void game::LoadCustomCommandKeys() while((Line = fgets(str, iBuffSz, fl)) != "") { if(Line.IsEmpty())break; - if(index>8)break; //can just ignore tho.. ABORT("more than 9 lines"); std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; - game::MoveCustomCommandKey[index]=atoi(Line.CStr()); + //game::MoveCustomCommandKey[index]=atoi(Line.CStr()); + sscanf(Line.CStr(),"%x",&game::MoveCustomCommandKey[index]); std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; index++; + if(index>8)break; } } diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 935c2d7e8..a981c9e52 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -531,7 +531,7 @@ void ivanconfig::AltSilhouetteDisplayer(const cycleoption* O, festring& Entry) } -void configureCustomKeys() +void ConfigureCustomKeys() { festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); @@ -550,11 +550,12 @@ void configureCustomKeys() case 8: fsAsk="Stop"; break; } iKey=game::AskForKeyPress(fsAsk); - //fprintf(fl, "%04d\n", globalwindowhandler::GetKey()); - //fprintf(fl, "%04X\n", globalwindowhandler::ReadKey()); + if(iKey==KEY_ESC)return; fprintf(fl, "%04X\n", iKey); } fclose(fl); + + game::LoadCustomCommandKeys(); } void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) @@ -572,7 +573,7 @@ void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) break; case DIR_CUSTOM: Entry << CONST_S("Custom"); - configureCustomKeys(); + ConfigureCustomKeys(); break; } } From 67b6b22086090ae25b0f33865be40709d3f1d5d5 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 26 Mar 2020 23:49:55 -0300 Subject: [PATCH 033/235] WIP-CustomMoveKeys: needs fixing; --- Main/Source/iconf.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index a981c9e52..2fbc62c91 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -531,12 +531,13 @@ void ivanconfig::AltSilhouetteDisplayer(const cycleoption* O, festring& Entry) } -void ConfigureCustomKeys() +truth ConfigureCustomKeys() { festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); int iKey; festring fsAsk; + bool bRet=true; for(int i=0;i<=8;i++){ switch(i){ case 0: fsAsk="Upper Left";break; @@ -550,12 +551,13 @@ void ConfigureCustomKeys() case 8: fsAsk="Stop"; break; } iKey=game::AskForKeyPress(fsAsk); - if(iKey==KEY_ESC)return; + if(iKey==KEY_ESC){bRet=false;break;} fprintf(fl, "%04X\n", iKey); } fclose(fl); - game::LoadCustomCommandKeys(); + if(bRet)game::LoadCustomCommandKeys(); + return bRet; } void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) @@ -572,8 +574,12 @@ void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) Entry << CONST_S("NetHack"); break; case DIR_CUSTOM: - Entry << CONST_S("Custom"); - ConfigureCustomKeys(); + if(ConfigureCustomKeys()){ + Entry << CONST_S("Custom"); + }else{ + DirectionKeyMap.Value=DIR_NORM; + Entry << CONST_S("Normal"); + } break; } } From 7c70bbca47b361bec3f58b103ba2193c7beb7d03 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 00:26:38 -0300 Subject: [PATCH 034/235] WIP-CustomMoveKeys: needs fixing; --- Main/Source/game.cpp | 9 ++++++++- Main/Source/iconf.cpp | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index d65290f92..51ea4b66d 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5772,16 +5772,23 @@ void game::LoadCustomCommandKeys() static int iBuffSz=7; char str[iBuffSz]; // while((Line = getstr(fl, false)) != "") - while((Line = fgets(str, iBuffSz, fl)) != "") + while(true) { + char* pc = fgets(str, iBuffSz, fl); + if(pc==NULL)break; + Line = pc; if(Line.IsEmpty())break; + std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; //game::MoveCustomCommandKey[index]=atoi(Line.CStr()); sscanf(Line.CStr(),"%x",&game::MoveCustomCommandKey[index]); std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; + index++; if(index>8)break; } + + fclose(fl); } void game::ValidateCommandKeys(char Key1,char Key2,char Key3) diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 2fbc62c91..e5367ec63 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -552,7 +552,7 @@ truth ConfigureCustomKeys() } iKey=game::AskForKeyPress(fsAsk); if(iKey==KEY_ESC){bRet=false;break;} - fprintf(fl, "%04X\n", iKey); + fprintf(fl, "0x%04X\n", iKey); } fclose(fl); @@ -574,7 +574,7 @@ void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) Entry << CONST_S("NetHack"); break; case DIR_CUSTOM: - if(ConfigureCustomKeys()){ + if(game::IsRunning() && ConfigureCustomKeys()){ Entry << CONST_S("Custom"); }else{ DirectionKeyMap.Value=DIR_NORM; From b342dc5605cbf7efb1fb85aa938baad73ad46032 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 00:45:42 -0300 Subject: [PATCH 035/235] WIP-CustomMoveKeys: needs fixing; --- Main/Source/game.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 51ea4b66d..abf2392d4 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5779,10 +5779,12 @@ void game::LoadCustomCommandKeys() Line = pc; if(Line.IsEmpty())break; - std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; + //std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; //game::MoveCustomCommandKey[index]=atoi(Line.CStr()); - sscanf(Line.CStr(),"%x",&game::MoveCustomCommandKey[index]); - std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; + int iVal; + sscanf(Line.CStr(),"%x",&iVal); + game::MoveCustomCommandKey[index]=iVal; + //std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; index++; if(index>8)break; From 84ad14513a4154b30eca38636735c31dd4ec044f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 14:33:56 -0300 Subject: [PATCH 036/235] WIP-CustomMoveKeys: understood and fixing...; --- Main/Include/command.h | 2 +- Main/Source/command.cpp | 6 +++--- Main/Source/game.cpp | 13 +++++++++---- Main/Source/iconf.cpp | 8 ++++---- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Main/Include/command.h b/Main/Include/command.h index dc90eec0f..ec9806793 100644 --- a/Main/Include/command.h +++ b/Main/Include/command.h @@ -32,7 +32,7 @@ class command char Key1; char Key2; char Key3; - int Key4; // custom keys can be more than 1 char long ex.: KEY_HOME is 0x147 or 0x0147 + int Key4; // custom keys can be more than 1 char long as ex.: KEY_HOME is 0x147 or 0x0147 truth UsableInWilderness; truth WizardModeFunction; }; diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 9854e33f1..bff032c68 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -52,7 +52,7 @@ command::command(truth (*LinkedFunction)(character*), cchar* Description, char Key1, char Key2, char Key3, truth UsableInWilderness, truth WizardModeFunction) -: LinkedFunction(LinkedFunction), Description(Description), Key1(Key1), Key2(Key2), Key3(Key3), +: LinkedFunction(LinkedFunction), Description(Description), Key1(Key1), Key2(Key2), Key3(Key3), Key4(0), UsableInWilderness(UsableInWilderness), WizardModeFunction(WizardModeFunction) { game::ValidateCommandKeys(Key1,Key2,Key3); @@ -69,7 +69,7 @@ char command::GetKey() const case DIR_HACK: // Nethack return Key3; case DIR_CUSTOM: - return Key4; + return Key4==0?Key1:Key4; //default is "Normal" in case of missing custom cfg TODO custom is not implemented yet anyway... default: ABORT("This is not Vim!"); return Key1; @@ -158,7 +158,7 @@ command* commandsystem::Command[] = #endif - 0 + 0 //this is important as an end of array indicator }; #ifndef WIZARD diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index abf2392d4..dd40abe30 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5769,9 +5769,8 @@ void game::LoadCustomCommandKeys() festring Line; int index=0; - static int iBuffSz=7; + static const int iBuffSz=7; char str[iBuffSz]; -// while((Line = getstr(fl, false)) != "") while(true) { char* pc = fgets(str, iBuffSz, fl); @@ -5781,18 +5780,23 @@ void game::LoadCustomCommandKeys() //std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; //game::MoveCustomCommandKey[index]=atoi(Line.CStr()); + int iVal; sscanf(Line.CStr(),"%x",&iVal); game::MoveCustomCommandKey[index]=iVal; + //std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; index++; - if(index>8)break; + if(index>7)break; //skip the last to keep as '.' } fclose(fl); } +/** + * check all other command keys versus MOVEMENT command keys on their specific branch + */ void game::ValidateCommandKeys(char Key1,char Key2,char Key3) { static const int iTot=9; @@ -5808,7 +5812,8 @@ void game::ValidateCommandKeys(char Key1,char Key2,char Key3) pa=game::MoveAbnormalCommandKey; Key=Key2; break; case DIR_HACK: pa=game::MoveNetHackCommandKey; Key=Key3; break; - //TODO should? DIR_CUSTOM be checked here too? +/*TODO case DIR_CUSTOM: + pa=game::MoveCustomCommandKey; Key=???; break; */ } for(int j=0;j Date: Fri, 27 Mar 2020 16:44:01 -0300 Subject: [PATCH 037/235] WIP-CustomMoveKeys: fixing and improving; --- Main/Include/iconf.h | 3 +++ Main/Source/iconf.cpp | 29 ++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index e1ad12ae5..e87ea12e4 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -67,6 +67,7 @@ class ivanconfig static int GetAltListItemWidth() { return AltListItemWidth.Value; } static int GetStackListPageLength() { return StackListPageLength.Value; } static truth GetSmartOpenCloseApply() { return SmartOpenCloseApply.Value; } + static truth IsSetupCustomKeys() { return SetupCustomKeys.Value; } static truth GetBeNice() { return BeNice.Value; } static int GetAltListItemPos() { return AltListItemPos.Value; } static truth GetPlaySounds() { return PlaySounds.Value; } @@ -166,6 +167,7 @@ class ivanconfig static void SilhouetteScaleChanger(cycleoption*, long); static void SaveGameSortModeChanger(cycleoption* O, long What); static void XBRZScaleChanger(truthoption*, truth); + static void SetupCustomKeysChanger(truthoption*, truth); static void ContrastHandler(long); static void VolumeHandler(long); static void BackGroundDrawer(); @@ -227,6 +229,7 @@ class ivanconfig static numberoption XBRZSquaresAroundPlayer; static cycleoption DirectionKeyMap; + static truthoption SetupCustomKeys; static cycleoption GoOnStopMode; static cycleoption SilhouetteScale; static cycleoption AltSilhouette; diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 26b35cfb8..7f7ff501c 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -278,9 +278,16 @@ cycleoption ivanconfig::AltSilhouettePreventColorGlitch("AltSilhouettePreventCol &AltSilhouettePreventColorGlitchDisplayer); cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", "Movement control scheme", - "Select keybindings for movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys. Any other command keys may be changed also to not conflict with this movement keys choice.", + "Select a pre-defined keybindings scheme for the movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys. Any other command keys may be auto changed also to not conflict with this movement keys choice.", DIR_NORM, 4, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); +truthoption ivanconfig::SetupCustomKeys( "SetupCustomKeys", + "Movement control custom keys", //TODO all keys one day + "Let you assign all 8 movement keys to any available key of your preference. All other command keys will remain as assigned by the scheme above.", + false, + &configsystem::NormalTruthDisplayer, + &configsystem::NormalTruthChangeInterface, + &SetupCustomKeysChanger); truthoption ivanconfig::SmartOpenCloseApply("SmartOpenCloseApply", "Smart open/close/apply behavior", "Automatically try to open doors when you walk into them, and don't ask for the target of close/apply actions when only one viable target is present.", @@ -552,6 +559,9 @@ truth ConfigureCustomKeys() } iKey=game::AskForKeyPress(fsAsk); if(iKey==KEY_ESC){bRet=false;break;} + + + fprintf(fl, "%04X\n", iKey); } fclose(fl); @@ -573,14 +583,6 @@ void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) case DIR_HACK: Entry << CONST_S("NetHack"); break; - case DIR_CUSTOM: - if(game::IsRunning() && ConfigureCustomKeys()){ - Entry << CONST_S("Custom"); - }else{ - DirectionKeyMap.Value=DIR_NORM; - Entry << CONST_S("Normal"); - } - break; } } @@ -994,6 +996,14 @@ void ivanconfig::FontGfxChanger(cycleoption* O, long What) O->Value = What; } +void ivanconfig::SetupCustomKeysChanger(truthoption* O, truth What) +{ + O->Value = What; + + if(O->Value) + ConfigureCustomKeys(); +} + void ivanconfig::XBRZScaleChanger(truthoption* O, truth What) { O->Value = What; @@ -1155,6 +1165,7 @@ void ivanconfig::Initialize() fsCategory="Input and Interface"; configsystem::AddOption(fsCategory,&DirectionKeyMap); + configsystem::AddOption(fsCategory,&SetupCustomKeys); configsystem::AddOption(fsCategory,&SaveGameSortMode); configsystem::AddOption(fsCategory,&ShowTurn); configsystem::AddOption(fsCategory,&ShowFullDungeonName); From 6b0439e1699b44718cfc4a9ae4440c04ba67a344 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 16:56:16 -0300 Subject: [PATCH 038/235] WIP-CustomMoveKeys: fixing and improving; --- FeLib/Include/felibdef.h | 1 - Main/Source/command.cpp | 2 -- Main/Source/game.cpp | 5 +++-- Main/Source/iconf.cpp | 4 ++-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/FeLib/Include/felibdef.h b/FeLib/Include/felibdef.h index c2b006116..3d40e85b5 100644 --- a/FeLib/Include/felibdef.h +++ b/FeLib/Include/felibdef.h @@ -262,6 +262,5 @@ inline int GetMinColor24(col24 Color) #define DIR_NORM 0 #define DIR_ALT 1 #define DIR_HACK 2 -#define DIR_CUSTOM 3 #endif diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index bff032c68..e7ab73105 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -68,8 +68,6 @@ char command::GetKey() const return Key2; case DIR_HACK: // Nethack return Key3; - case DIR_CUSTOM: - return Key4==0?Key1:Key4; //default is "Normal" in case of missing custom cfg TODO custom is not implemented yet anyway... default: ABORT("This is not Vim!"); return Key1; diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index dd40abe30..b4666240a 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5827,6 +5827,9 @@ void game::ValidateCommandKeys(char Key1,char Key2,char Key3) int game::GetMoveCommandKey(int I) { + if(ivanconfig::IsSetupCustomKeys()) + return MoveCustomCommandKey[I]; + switch(ivanconfig::GetDirectionKeyMap()) { case DIR_NORM: @@ -5835,8 +5838,6 @@ int game::GetMoveCommandKey(int I) return MoveAbnormalCommandKey[I]; case DIR_HACK: return MoveNetHackCommandKey[I]; - case DIR_CUSTOM: - return MoveCustomCommandKey[I]; default: ABORT("This is not Emacs!"); return MoveNormalCommandKey[I]; diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 7f7ff501c..f85f757cb 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -278,7 +278,7 @@ cycleoption ivanconfig::AltSilhouettePreventColorGlitch("AltSilhouettePreventCol &AltSilhouettePreventColorGlitchDisplayer); cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", "Movement control scheme", - "Select a pre-defined keybindings scheme for the movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys. Any other command keys may be auto changed also to not conflict with this movement keys choice.", + "Select a pre-defined keybinding scheme for the movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys. Any other command keys may be auto changed also to not conflict with this movement keys choice.", DIR_NORM, 4, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SetupCustomKeys( "SetupCustomKeys", @@ -1198,7 +1198,7 @@ void ivanconfig::Initialize() audio::ChangeMIDIOutputDevice(MIDIOutputDevice.Value); audio::SetVolumeLevel(Volume.Value); - if(ivanconfig::GetDirectionKeyMap()==DIR_CUSTOM) + if(ivanconfig::IsSetupCustomKeys()) game::LoadCustomCommandKeys(); //TODO re-use changer methods for above configs too to avoid duplicating the algo? From 5b4dd80b0bf1c752919b9d2fad356971f28bb7c1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 17:19:49 -0300 Subject: [PATCH 039/235] WIP-CustomMoveKeys: fixing and improving; --- Main/Source/iconf.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index f85f757cb..34fd38103 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -13,12 +13,14 @@ #include "area.h" #include "audio.h" #include "bitmap.h" +#include "command.h" #include "feio.h" #include "felist.h" #include "game.h" #include "graphics.h" #include "iconf.h" #include "igraph.h" +#include "message.h" #include "save.h" #include "stack.h" #include "whandler.h" @@ -545,8 +547,13 @@ truth ConfigureCustomKeys() int iKey; festring fsAsk; bool bRet=true; - for(int i=0;i<=7;i++){ //skip the last to keep as '.' - switch(i){ + int index=0; + while(true){ + if(index>=8)break; //skip the last to keep as '.' + + bool bRetry=false; + + switch(index){ case 0: fsAsk="Upper Left";break; case 1: fsAsk="Up"; break; case 2: fsAsk="Upper Right"; break; @@ -560,9 +567,18 @@ truth ConfigureCustomKeys() iKey=game::AskForKeyPress(fsAsk); if(iKey==KEY_ESC){bRet=false;break;} - + for(int c = 1; commandsystem::GetCommand(c); ++c){ + if(iKey==commandsystem::GetCommand(c)->GetKey()){ + ADD_MESSAGE("SYSTEM: conflicting key '%c', retry...",iKey); //TODO this messes the gameplay message log... but is better than a popup? + bRetry=true; + break; + } + } + if(bRetry)continue; fprintf(fl, "%04X\n", iKey); + + index++; } fclose(fl); From cdb12774f0f9166eb9ef5577c6e2c5c04e2af367 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 17:54:49 -0300 Subject: [PATCH 040/235] CustomMoveKeys: final test worked fine. Obs.: such keys are required also to navigate lists; --- Main/Source/game.cpp | 2 +- Main/Source/iconf.cpp | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index b4666240a..dca2452b3 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5769,7 +5769,7 @@ void game::LoadCustomCommandKeys() festring Line; int index=0; - static const int iBuffSz=7; + static const int iBuffSz=7; // 7 is max for now could be "0xFFFF\n", but is just "FFFF\n" char str[iBuffSz]; while(true) { diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 34fd38103..15a1d8738 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -284,8 +284,8 @@ cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", DIR_NORM, 4, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SetupCustomKeys( "SetupCustomKeys", - "Movement control custom keys", //TODO all keys one day - "Let you assign all 8 movement keys to any available key of your preference. All other command keys will remain as assigned by the scheme above.", + "Movement control custom keys", //TODO all keys one day, and let it work on main menu + "Let you assign all 8 movement keys to any available key of your preference. All other command keys will remain as assigned by the scheme above. This global configuration won't work at main menu, load/start some game.", false, &configsystem::NormalTruthDisplayer, &configsystem::NormalTruthChangeInterface, @@ -548,6 +548,7 @@ truth ConfigureCustomKeys() festring fsAsk; bool bRet=true; int index=0; + int aiKeyList[8]={0,0,0,0, 0,0,0,0}; while(true){ if(index>=8)break; //skip the last to keep as '.' @@ -569,7 +570,14 @@ truth ConfigureCustomKeys() for(int c = 1; commandsystem::GetCommand(c); ++c){ if(iKey==commandsystem::GetCommand(c)->GetKey()){ - ADD_MESSAGE("SYSTEM: conflicting key '%c', retry...",iKey); //TODO this messes the gameplay message log... but is better than a popup? + ADD_MESSAGE("SYSTEM: conflicting command key '%c'(code is %d or 0x%X), retry...",iKey,iKey,iKey); //TODO this messes the gameplay message log... but is better than a popup? + bRetry=true; + break; + } + } + for(int c = 0; c<8; ++c){ + if(iKey==aiKeyList[c]){ + ADD_MESSAGE("SYSTEM: conflicting movement key '%c'(code is %d or 0x%X), retry...",iKey,iKey,iKey); //TODO this messes the gameplay message log... but is better than a popup? bRetry=true; break; } @@ -577,6 +585,7 @@ truth ConfigureCustomKeys() if(bRetry)continue; fprintf(fl, "%04X\n", iKey); + aiKeyList[index]=iKey; index++; } @@ -1014,10 +1023,11 @@ void ivanconfig::FontGfxChanger(cycleoption* O, long What) void ivanconfig::SetupCustomKeysChanger(truthoption* O, truth What) { - O->Value = What; - - if(O->Value) - ConfigureCustomKeys(); + if(game::IsRunning() || !What){ + O->Value = What; + if(O->Value) + ConfigureCustomKeys(); + } } void ivanconfig::XBRZScaleChanger(truthoption* O, truth What) From 1701525956dd002e44b03a8ef43f98847795e19e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 18:08:58 -0300 Subject: [PATCH 041/235] DirectionKeyMap: restored max of 3 cycle options. --- Main/Source/iconf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 15a1d8738..12eb75c0a 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -281,7 +281,7 @@ cycleoption ivanconfig::AltSilhouettePreventColorGlitch("AltSilhouettePreventCol cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", "Movement control scheme", "Select a pre-defined keybinding scheme for the movement of your character. Normal scheme uses NumPad, or arrow keys along with Home, End, PgUp and PgDn for diagonal directions. Alternative scheme is better suited for laptops and uses number and letter keys on the main keyboard. NetHack scheme uses vi keys. After you select a movement control scheme, you may also check the in game keybindings help to see the currently active movement keys. Any other command keys may be auto changed also to not conflict with this movement keys choice.", - DIR_NORM, 4, // {default value, number of options to cycle through} + DIR_NORM, 3, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SetupCustomKeys( "SetupCustomKeys", "Movement control custom keys", //TODO all keys one day, and let it work on main menu From 45569bd81f350947ce5b3fdcf060cac874d1c4b7 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 27 Mar 2020 19:40:23 -0300 Subject: [PATCH 042/235] WIP-itemInfo: now shops will show full item info on look mode; --- Main/Source/game.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index dca2452b3..fd8dc5745 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -4497,7 +4497,11 @@ v2 game::LookKeyHandler(v2 CursorPos, int Key) stack* Stack = LSquare->GetStack(); if(LSquare->IsTransparent() && Stack->GetVisibleItems(Player)) - Stack->DrawContents(Player, "Items here", NO_SELECT|(GetSeeWholeMapCheatMode() ? 0 : NO_SPECIAL_INFO)); + Stack->DrawContents(Player, "Items here", + NO_SELECT| + (GetSeeWholeMapCheatMode() || Stack->GetItem(0)->GetRoom() || Player->GetLSquareUnder()==LSquare ? + 0 : NO_SPECIAL_INFO) + ); else ADD_MESSAGE("You see no items here."); } From 5db90e12014dc156137cc3e8d50304b6b279780b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 28 Mar 2020 18:17:52 -0300 Subject: [PATCH 043/235] ListNavigation: UpArrow and DownArrow will always work to navigate on lists now; --- FeLib/Source/felist.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FeLib/Source/felist.cpp b/FeLib/Source/felist.cpp index a26f27fcb..ce68dfd36 100644 --- a/FeLib/Source/felist.cpp +++ b/FeLib/Source/felist.cpp @@ -652,7 +652,7 @@ uint felist::DrawFiltered(bool& bJustExitTheList) break; } - if((Flags & SELECTABLE) && Pressed == UpKey) + if((Flags & SELECTABLE) && (Pressed == UpKey || Pressed == KEY_UP)) {DBGLN; if(Selected) { @@ -677,7 +677,7 @@ uint felist::DrawFiltered(bool& bJustExitTheList) continue; } - if((Flags & SELECTABLE) && Pressed == DownKey) + if((Flags & SELECTABLE) && (Pressed == DownKey || Pressed == KEY_DOWN)) {DBGLN; if(!LastEntryVisible || Selected != Selectables - 1) { From 545cdfc8e80554a1a21868e306e040068b683038 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 28 Mar 2020 20:45:23 -0300 Subject: [PATCH 044/235] Every command key can now be customized thru CustomCommandKeys.cfg. First generate it in a running gameplay, suggested direction keys vs Normal keybinding scheme: 789 ; ' vb/ Close the game. Edit that file ex.: change 'z' to 'Z' Start the game, load a savegame and test it! --- Main/Include/command.h | 1 + Main/Source/command.cpp | 7 ++++++- Main/Source/game.cpp | 25 ++++++++++++++++++++----- Main/Source/iconf.cpp | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Main/Include/command.h b/Main/Include/command.h index ec9806793..ed6c23c9f 100644 --- a/Main/Include/command.h +++ b/Main/Include/command.h @@ -26,6 +26,7 @@ class command char GetKey() const; truth IsUsableInWilderness() const { return UsableInWilderness; } truth IsWizardModeFunction() const { return WizardModeFunction; } + int SetCustomKey(int iKey){ int iKeyBkp=Key4; Key4 = iKey; return iKeyBkp; } private: truth (*LinkedFunction)(character*); cchar* Description; diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index e7ab73105..9f35518c1 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -60,6 +60,11 @@ command::command(truth (*LinkedFunction)(character*), cchar* Description, char K char command::GetKey() const { + if(ivanconfig::IsSetupCustomKeys()){ + if(Key4>0) + return (char)Key4; //TODO everything related should be integer now... + } + switch(ivanconfig::GetDirectionKeyMap()) { case DIR_NORM: // Normal @@ -70,7 +75,7 @@ char command::GetKey() const return Key3; default: ABORT("This is not Vim!"); - return Key1; + return Key1; //? } } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index fd8dc5745..6e3af71ec 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5773,13 +5773,11 @@ void game::LoadCustomCommandKeys() festring Line; int index=0; - static const int iBuffSz=7; // 7 is max for now could be "0xFFFF\n", but is just "FFFF\n" + static const int iBuffSz=0xFF; char str[iBuffSz]; - while(true) + while(fgets(str, iBuffSz, fl)) { - char* pc = fgets(str, iBuffSz, fl); - if(pc==NULL)break; - Line = pc; + Line = str; if(Line.IsEmpty())break; //std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; @@ -5795,6 +5793,23 @@ void game::LoadCustomCommandKeys() if(index>7)break; //skip the last to keep as '.' } + while(fgets(str, iBuffSz, fl)){ + Line=str; + command* cmd; + for(int c = 1; cmd=commandsystem::GetCommand(c); ++c){ + festring::sizetype pos = Line.Find(cmd->GetDescription()); + if(pos==1){ + char ch = Line[Line.GetSize()-3]; // -3 skips '\n' and ending "'" + cmd->SetCustomKey((int)ch); //the last char between ' + DBG4(pos,ch,cmd->GetDescription(),Line.CStr()); +// ADD_MESSAGE("SYSTEM: set custom command key for \"%s\" '%c'", +// commandsystem::GetCommand(c)->GetDescription(), +// commandsystem::GetCommand(c)->GetKey()); //TODO this messes the gameplay message log... but is better than a popup? + break; + } + } + } + fclose(fl); } diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 12eb75c0a..2603b46b7 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -543,6 +543,12 @@ void ivanconfig::AltSilhouetteDisplayer(const cycleoption* O, festring& Entry) truth ConfigureCustomKeys() { festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; + + festring fsFlBkp=fsFl+".bkp"; + std::ifstream src(fsFl.CStr() , std::ios::binary); + std::ofstream dst(fsFlBkp.CStr(), std::ios::binary); + dst << src.rdbuf(); + FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); int iKey; festring fsAsk; @@ -589,6 +595,15 @@ truth ConfigureCustomKeys() index++; } + + for(int c = 1; commandsystem::GetCommand(c); ++c){ + fprintf(fl, "\"%s\"=0x%04X='%c'\n", + commandsystem::GetCommand(c)->GetDescription(), + commandsystem::GetCommand(c)->GetKey(), + commandsystem::GetCommand(c)->GetKey() + ); + } + fclose(fl); if(bRet)game::LoadCustomCommandKeys(); From 4279501e0edfbda1a1216399468b47a9c20634b2 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 28 Mar 2020 21:52:08 -0300 Subject: [PATCH 045/235] fix: using the result of an assignment as a condition without parentheses WIP-GodFavours: not working yet; --- Main/Include/god.h | 2 ++ Main/Include/gods.h | 15 ++++++++ Main/Source/game.cpp | 2 +- Main/Source/gods.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/Main/Include/god.h b/Main/Include/god.h index febaa873d..6a9e235d3 100644 --- a/Main/Include/god.h +++ b/Main/Include/god.h @@ -86,10 +86,12 @@ class god protected: virtual void PrayGoodEffect() = 0; virtual void PrayBadEffect() = 0; + virtual bool Favour(cfestring fsWhat, int iDebit=0) = 0; int Relation, LastPray; festring fsLastKnownRelation; long Timer; truth Known; + std::vector spells; }; #ifdef __FILE_OF_STATIC_GOD_PROTOTYPE_DEFINITIONS__ diff --git a/Main/Include/gods.h b/Main/Include/gods.h index db3e5663e..654e9c808 100644 --- a/Main/Include/gods.h +++ b/Main/Include/gods.h @@ -29,6 +29,7 @@ GOD(valpurus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(legifer, god) @@ -44,6 +45,7 @@ GOD(legifer, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(atavus, god) @@ -60,6 +62,7 @@ GOD(atavus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(dulcis, god) @@ -75,6 +78,7 @@ GOD(dulcis, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(seges, god) @@ -93,6 +97,7 @@ GOD(seges, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(sophos, god) @@ -108,6 +113,7 @@ GOD(sophos, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(silva, god) @@ -123,6 +129,7 @@ GOD(silva, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(loricatus, god) @@ -138,6 +145,7 @@ GOD(loricatus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(mellis, god) @@ -153,6 +161,7 @@ GOD(mellis, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(cleptia, god) @@ -168,6 +177,7 @@ GOD(cleptia, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(nefas, god) @@ -183,6 +193,7 @@ GOD(nefas, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(scabies, god) @@ -202,6 +213,7 @@ GOD(scabies, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(infuscor, god) @@ -217,6 +229,7 @@ GOD(infuscor, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(cruentus, god) @@ -232,6 +245,7 @@ GOD(cruentus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; GOD(mortifer, god) @@ -248,6 +262,7 @@ GOD(mortifer, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); + virtual bool Favour(cfestring fsWhat, int iDebit=0); }; #endif diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 6e3af71ec..bc64af47e 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5796,7 +5796,7 @@ void game::LoadCustomCommandKeys() while(fgets(str, iBuffSz, fl)){ Line=str; command* cmd; - for(int c = 1; cmd=commandsystem::GetCommand(c); ++c){ + for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c){ festring::sizetype pos = Line.Find(cmd->GetDescription()); if(pos==1){ char ch = Line[Line.GetSize()-3]; // -3 skips '\n' and ending "'" diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index ebe40e47f..a88f213ba 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -10,6 +10,9 @@ * */ +#include + + /* Compiled through godset.cpp */ #define LAWFUL_BASIC_COLOR MakeRGB16(160, 160, 0) @@ -126,15 +129,34 @@ int mortifer::GetBasicAlignment() const { return EVIL; } col16 mortifer::GetColor() const { return CHAOS_BASIC_COLOR; } col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } +#define FAVOUR_TELEPORT "Teleport" + +bool sophos::Favour(cfestring fsWhat, int iDebit) +{ + if(RelationMove(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); + static bool bInitDummy=[this](){spells.push_back(CONST_S(FAVOUR_TELEPORT));return true;}(); + Relation-=iDebit; + return true; + } + + return false; +} + void sophos::PrayGoodEffect() { truth DidHelp = false; if(!PLAYER->StateIsActivated(TELEPORT_LOCK)) { - ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement!"); - game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); - PLAYER->Move(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); + Favour(FAVOUR_TELEPORT); DidHelp = true; } @@ -181,6 +203,10 @@ void sophos::PrayBadEffect() PLAYER->CheckDeath(CONST_S("shattered to pieces by the wrath of ") + GetName(), 0); } +bool valpurus::Favour(cfestring fsWhat, int iDebit) +{ +} + void valpurus::PrayGoodEffect() { if(!game::PlayerIsGodChampion()) @@ -214,6 +240,10 @@ void valpurus::PrayBadEffect() PLAYER->CheckDeath(CONST_S("faced the hammer of Justice from the hand of ") + GetName(), 0); } +bool legifer::Favour(cfestring fsWhat, int iDebit) +{ +} + void legifer::PrayGoodEffect() { // I think this is a remnant of past development that you call upon Inlux rather than Legifer. --red_kangaroo @@ -231,6 +261,10 @@ void legifer::PrayBadEffect() PLAYER->CheckDeath(CONST_S("burned to death by the holy flames of ") + GetName(), 0); } +bool dulcis::Favour(cfestring fsWhat, int iDebit) +{ +} + void dulcis::PrayGoodEffect() { truth HasHelped = false; @@ -326,6 +360,10 @@ void dulcis::PrayBadEffect() PLAYER->CheckDeath(CONST_S("became insane by listening ") + GetName() + " too much", 0); } +bool seges::Favour(cfestring fsWhat, int iDebit) +{ +} + void seges::PrayGoodEffect() { if(PLAYER->IsInBadCondition()) @@ -413,6 +451,10 @@ void seges::PrayBadEffect() ADD_MESSAGE("Seges tries to alter the contents of your stomach, but fails."); } +bool atavus::Favour(cfestring fsWhat, int iDebit) +{ +} + void atavus::PrayGoodEffect() { item* Enchantable; @@ -496,6 +538,10 @@ void atavus::PrayBadEffect() PLAYER->CheckDeath(CONST_S("killed by Atavus's humour")); } +bool silva::Favour(cfestring fsWhat, int iDebit) +{ +} + void silva::PrayGoodEffect() { if(PLAYER->GetNP() < HUNGER_LEVEL) @@ -675,6 +721,10 @@ void silva::PrayBadEffect() } } +bool loricatus::Favour(cfestring fsWhat, int iDebit) +{ +} + void loricatus::PrayGoodEffect() { item* MainWielded = PLAYER->GetMainWielded(); @@ -816,6 +866,10 @@ void loricatus::PrayBadEffect() } } +bool cleptia::Favour(cfestring fsWhat, int iDebit) +{ +} + void cleptia::PrayGoodEffect() { int Duration = 200 * PLAYER->GetAttribute(WISDOM) + Max(Relation, 0); @@ -863,6 +917,10 @@ void cleptia::PrayBadEffect() PLAYER->BeginTemporaryState(SLOW, 250); } +bool mortifer::Favour(cfestring fsWhat, int iDebit) +{ +} + void mortifer::PrayGoodEffect() { if(!game::PlayerIsGodChampion()) @@ -900,6 +958,10 @@ void mortifer::PrayBadEffect() PLAYER->CheckDeath(CONST_S("obliterated by the unholy power of ") + GetName(), 0); } +bool mellis::Favour(cfestring fsWhat, int iDebit) +{ +} + void mellis::PrayGoodEffect() { truth Success = false; @@ -1077,6 +1139,10 @@ void infuscor::PrayBadEffect() PLAYER->LoseConsciousness(1000 + RAND_N(1000)); } +bool nefas::Favour(cfestring fsWhat, int iDebit) +{ +} + void nefas::PrayGoodEffect() { if(PLAYER->GetNP() < HUNGER_LEVEL) @@ -1165,6 +1231,10 @@ void nefas::PrayBadEffect() PLAYER->CheckDeath(CONST_S("killed while enjoying the company of ") + GetName(), 0); } +bool scabies::Favour(cfestring fsWhat, int iDebit) +{ +} + void scabies::PrayGoodEffect() { if(PLAYER->IsImmuneToLeprosy()) // Spread leprosy whenever you won't harm your followers. @@ -1268,6 +1338,10 @@ void scabies::PrayBadEffect() } } +bool infuscor::Favour(cfestring fsWhat, int iDebit) +{ +} + void infuscor::PrayGoodEffect() { truth Success = false; @@ -1352,6 +1426,10 @@ void infuscor::PrayGoodEffect() return; } +bool cruentus::Favour(cfestring fsWhat, int iDebit) +{ +} + void cruentus::PrayGoodEffect() { // Blood for the god of blood! From bc466c1695b564a159d8de5ea845769cc55b5a7b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 28 Mar 2020 21:56:15 -0300 Subject: [PATCH 046/235] WIP-GodFavours: not working yet; --- Main/Include/god.h | 2 +- Main/Source/gods.cpp | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Main/Include/god.h b/Main/Include/god.h index 6a9e235d3..d80b6569e 100644 --- a/Main/Include/god.h +++ b/Main/Include/god.h @@ -86,7 +86,7 @@ class god protected: virtual void PrayGoodEffect() = 0; virtual void PrayBadEffect() = 0; - virtual bool Favour(cfestring fsWhat, int iDebit=0) = 0; + virtual bool Favour(cfestring fsWhat, int iDebit=0); int Relation, LastPray; festring fsLastKnownRelation; long Timer; diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index a88f213ba..7e884a093 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -131,13 +131,20 @@ col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } #define FAVOUR_TELEPORT "Teleport" -bool sophos::Favour(cfestring fsWhat, int iDebit) +bool god::Favour(cfestring fsWhat, int iDebit) { if(Relation Date: Sat, 28 Mar 2020 23:47:58 -0300 Subject: [PATCH 047/235] WIP-GodFavours: sophos may grant teleport now; --- Main/Include/command.h | 1 + Main/Include/god.h | 5 +++-- Main/Source/command.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ Main/Source/gods.cpp | 22 +++++++++++++++++++--- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/Main/Include/command.h b/Main/Include/command.h index ed6c23c9f..61431fe50 100644 --- a/Main/Include/command.h +++ b/Main/Include/command.h @@ -74,6 +74,7 @@ class commandsystem static truth Open(character*); static truth PickUp(character*); static truth Pray(character*); + static truth AskFavour(character*); static truth Craft(character*); static truth Quit(character*); static truth Read(character*); diff --git a/Main/Include/god.h b/Main/Include/god.h index d80b6569e..9c848ddf3 100644 --- a/Main/Include/god.h +++ b/Main/Include/god.h @@ -83,15 +83,16 @@ class god virtual int GetSex() const = 0; void SignalRandomAltarGeneration(const std::vector&); virtual truth LikesVomit() const { return false; } + virtual bool Favour(cfestring fsWhat, int iDebit=0); + const std::vector GetKnownSpells() const { return knownSpells; } protected: virtual void PrayGoodEffect() = 0; virtual void PrayBadEffect() = 0; - virtual bool Favour(cfestring fsWhat, int iDebit=0); int Relation, LastPray; festring fsLastKnownRelation; long Timer; truth Known; - std::vector spells; + std::vector knownSpells; }; #ifdef __FILE_OF_STATIC_GOD_PROTOTYPE_DEFINITIONS__ diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 9f35518c1..71fea5119 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -114,6 +114,7 @@ command* commandsystem::Command[] = new command(&IssueCommand, "issue commands to team members", 'I', 'I', 'I', false), new command(&Offer, "offer to gods", 'O', 'f', 'O', false), new command(&Pray, "pray to gods", 'p', 'p', 'p', false), + new command(&AskFavour, "pray for a favour", 'P', 'P', 'P', false), new command(&Sit, "sit down", '_', '_', '_', false), new command(&Rest, "rest and heal", 'h', 'h', 'H', true), new command(&Save, "save and quit", 'S', 'S', 'S', true), @@ -1156,6 +1157,46 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng return false; } +truth commandsystem::AskFavour(character* Char) +{ + felist felSpellList(CONST_S("To Whom you want to ask a favour?")); + + std::vector> vGS; + for(int c = 1; c <= GODS; ++c){ + std::vector v = game::GetGod(c)->GetKnownSpells(); + for(auto pfsSpell = v.begin(); pfsSpell != v.end(); pfsSpell++){ + felSpellList.AddEntry(CONST_S("")+ + game::GetGod(c)->GetName()+" may grant you a "+*pfsSpell,LIGHT_GRAY); + std::pair GS; + GS.first = game::GetGod(c); + GS.second = *pfsSpell; + vGS.push_back(GS); + } + } + + felSpellList.AddFlags(SELECTABLE); + int Select = felSpellList.Draw(); + + if(Select == LIST_WAS_EMPTY) + { + ADD_MESSAGE("You do not know about any favours yet..."); + return false; + } + + if(Select & FELIST_ERROR_BIT) + return false; + +// for(int c = 1; c <= GODS; ++c){ +// if(game::GetGod(c)->Favour(v[Select],-1)){ + if(vGS[Select].first->Favour(vGS[Select].second,-1)){ + Char->EditAP(-1000); + return true; + } +// } + + return false; +} + truth commandsystem::Pray(character* Char) { felist Panthenon(CONST_S("To Whom you want to address your prayers?")); diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 7e884a093..cf622b4af 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -141,15 +141,31 @@ bool god::Favour(cfestring fsWhat, int iDebit) return true; } +int CalcDebit(int iDebit,int iDefault){ + if(iDebit!=0){ + if(iDebit==-1)iDebit=iDefault; + iDebit -= game::GetPlayer()->GetAttribute(MANA); + if(iDebit<10)iDebit=10; //minimum + } + return iDebit; +} + +/** + * + * @param fsWhat + * @param iDebit if -1 will be automatic + * @return + */ bool sophos::Favour(cfestring fsWhat, int iDebit) { - if(!god::Favour(fsWhat,iDebit))return false; - if(fsWhat==FAVOUR_TELEPORT){ + iDebit=CalcDebit(iDebit,100); + if(!god::Favour(fsWhat,iDebit))return false; + ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement!"); game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); PLAYER->Move(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); - static bool bInitDummy=[this](){spells.push_back(CONST_S(FAVOUR_TELEPORT));return true;}(); + static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_TELEPORT));return true;}(); Relation-=iDebit; return true; } From ba16707842ab9dcd766e05c588ae4aef765ee614 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 00:37:05 -0300 Subject: [PATCH 048/235] SAVE_FILE_VERSION=135 to save known spells; WIP-GodFavours: added all Silva favours; --- Main/Source/command.cpp | 31 ++++++++++------- Main/Source/game.cpp | 2 +- Main/Source/god.cpp | 4 +++ Main/Source/gods.cpp | 74 ++++++++++++++++++++++++++++++++++------- 4 files changed, 86 insertions(+), 25 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 71fea5119..5c581a869 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1159,18 +1159,28 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng truth commandsystem::AskFavour(character* Char) { - felist felSpellList(CONST_S("To Whom you want to ask a favour?")); + felist felSpellList(CONST_S("To Whom you want to ask a %s favour?")+game::GetVerbalPlayerAlignment()); std::vector> vGS; for(int c = 1; c <= GODS; ++c){ - std::vector v = game::GetGod(c)->GetKnownSpells(); - for(auto pfsSpell = v.begin(); pfsSpell != v.end(); pfsSpell++){ - felSpellList.AddEntry(CONST_S("")+ - game::GetGod(c)->GetName()+" may grant you a "+*pfsSpell,LIGHT_GRAY); - std::pair GS; - GS.first = game::GetGod(c); - GS.second = *pfsSpell; - vGS.push_back(GS); + god* pgod = game::GetGod(c); + + bool bOk=false; + if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0)bOk=true; + if(pgod->GetBasicAlignment() == NEUTRAL && game::GetPlayerAlignment() == 0)bOk=true; + if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; + + if(bOk){ + std::vector v = pgod->GetKnownSpells(); + for(auto pfsSpell = v.begin(); pfsSpell != v.end(); pfsSpell++){ + felSpellList.AddEntry(CONST_S("")+ + pgod->GetName()+" may grant you a "+*pfsSpell,LIGHT_GRAY); + + std::pair GS; + GS.first = pgod; + GS.second = *pfsSpell; + vGS.push_back(GS); + } } } @@ -1186,13 +1196,10 @@ truth commandsystem::AskFavour(character* Char) if(Select & FELIST_ERROR_BIT) return false; -// for(int c = 1; c <= GODS; ++c){ -// if(game::GetGod(c)->Favour(v[Select],-1)){ if(vGS[Select].first->Favour(vGS[Select].second,-1)){ Char->EditAP(-1000); return true; } -// } return false; } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index bc64af47e..570c5687d 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -71,7 +71,7 @@ #include "dbgmsgproj.h" -#define SAVE_FILE_VERSION 134 // Increment this if changes make savefiles incompatible +#define SAVE_FILE_VERSION 135 // Increment this if changes make savefiles incompatible #define BONE_FILE_VERSION 119 // Increment this if changes make bonefiles incompatible #define LOADED 0 diff --git a/Main/Source/god.cpp b/Main/Source/god.cpp index b90a0fae0..a02557aef 100644 --- a/Main/Source/god.cpp +++ b/Main/Source/god.cpp @@ -553,6 +553,7 @@ void god::Save(outputfile& SaveFile) const SaveFile << static_cast(GetType()); SaveFile << Relation << Timer << Known << LastPray; SaveFile << fsLastKnownRelation; + SaveFile << knownSpells; } void god::Load(inputfile& SaveFile) @@ -561,6 +562,9 @@ void god::Load(inputfile& SaveFile) if(game::GetCurrentSavefileVersion()>=134){ SaveFile >> fsLastKnownRelation; } + if(game::GetCurrentSavefileVersion()>=135){ + SaveFile >> knownSpells; + } } void god::ApplyDivineTick() diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index cf622b4af..a7e2a1429 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -561,20 +561,29 @@ void atavus::PrayBadEffect() PLAYER->CheckDeath(CONST_S("killed by Atavus's humour")); } -bool silva::Favour(cfestring fsWhat, int iDebit) -{ -} +#define FAVOUR_FEED "Feed me up" +#define FAVOUR_CALLRAIN "Call Rain" +#define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" +#define FAVOUR_SUMMONWOLF "Summon a wolf friend" -void silva::PrayGoodEffect() +bool silva::Favour(cfestring fsWhat, int iDebit) { - if(PLAYER->GetNP() < HUNGER_LEVEL) - { + if(fsWhat==FAVOUR_FEED){ + iDebit=CalcDebit(iDebit,200); + if(!god::Favour(fsWhat,iDebit))return false; + ADD_MESSAGE("%s feeds you fruits and wild berries.", GetName()); PLAYER->SetNP(SATIATED_LEVEL); + + static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_FEED));return true;}(); + Relation-=iDebit; + return true; } - if(PLAYER->IsBurning() || PLAYER->PossessesItem(&item::IsOnFire)) - { + if(fsWhat==FAVOUR_CALLRAIN){ + iDebit=CalcDebit(iDebit,75); + if(!god::Favour(fsWhat,iDebit))return false; + beamdata Beam ( 0, @@ -588,9 +597,16 @@ void silva::PrayGoodEffect() Square->LiquidRain(Beam, WATER); ADD_MESSAGE("Silva allows a little spell of gentle rain to pour down from above."); + + static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_CALLRAIN));return true;}(); + Relation-=iDebit; + return true; } - else if(!game::GetCurrentLevel()->IsOnGround()) - { + + if(fsWhat==FAVOUR_EARTHQUAKE){ + iDebit=CalcDebit(iDebit,500); + if(!god::Favour(fsWhat,iDebit))return false; + ADD_MESSAGE("Suddenly a horrible earthquake shakes the level."); int c, Tunnels = 2 + RAND() % 3; if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) @@ -696,9 +712,16 @@ void silva::PrayGoodEffect() for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); + + static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_EARTHQUAKE));return true;}(); + Relation-=iDebit; + return true; } - else - { + + if(fsWhat==FAVOUR_SUMMONWOLF){ + iDebit=CalcDebit(iDebit,50); + if(!god::Favour(fsWhat,iDebit))return false; + int TryToCreate = 1 + RAND() % 7; int Created = 0; @@ -725,6 +748,33 @@ void silva::PrayGoodEffect() if(Created > 1) ADD_MESSAGE("Suddenly some tame wolves materialize around you."); + + static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_SUMMONWOLF));return true;}(); + Relation-=iDebit; + return true; + } + + return false; +} + +void silva::PrayGoodEffect() +{ + if(PLAYER->GetNP() < HUNGER_LEVEL) + { + Favour(FAVOUR_FEED); + } + + if(PLAYER->IsBurning() || PLAYER->PossessesItem(&item::IsOnFire)) + { + Favour(FAVOUR_CALLRAIN); + } + else if(!game::GetCurrentLevel()->IsOnGround()) + { + Favour(FAVOUR_EARTHQUAKE); + } + else + { + Favour(FAVOUR_SUMMONWOLF); } } From 97bb1373ebfb724b92d417d9f80d0ff224f6cdbc Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 00:51:58 -0300 Subject: [PATCH 049/235] WIP-GodFavours: improving; --- Main/Source/command.cpp | 5 +++-- Main/Source/gods.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 5c581a869..e30ebfd9c 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1159,7 +1159,7 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng truth commandsystem::AskFavour(character* Char) { - felist felSpellList(CONST_S("To Whom you want to ask a %s favour?")+game::GetVerbalPlayerAlignment()); + felist felSpellList(CONST_S("To Whom you want to ask a ")+game::GetVerbalPlayerAlignment()+" favour?"); std::vector> vGS; for(int c = 1; c <= GODS; ++c){ @@ -1174,7 +1174,7 @@ truth commandsystem::AskFavour(character* Char) std::vector v = pgod->GetKnownSpells(); for(auto pfsSpell = v.begin(); pfsSpell != v.end(); pfsSpell++){ felSpellList.AddEntry(CONST_S("")+ - pgod->GetName()+" may grant you a "+*pfsSpell,LIGHT_GRAY); + pgod->GetName()+" may grant you a \""+*pfsSpell+"\" favour.",LIGHT_GRAY); std::pair GS; GS.first = pgod; @@ -1184,6 +1184,7 @@ truth commandsystem::AskFavour(character* Char) } } + game::SetStandardListAttributes(felSpellList); felSpellList.AddFlags(SELECTABLE); int Select = felSpellList.Draw(); diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index a7e2a1429..2eda8f0e9 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -564,7 +564,7 @@ void atavus::PrayBadEffect() #define FAVOUR_FEED "Feed me up" #define FAVOUR_CALLRAIN "Call Rain" #define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" -#define FAVOUR_SUMMONWOLF "Summon a wolf friend" +#define FAVOUR_SUMMONWOLF "Summon wolf friends" bool silva::Favour(cfestring fsWhat, int iDebit) { From d1e4ebacdc89dfeb2e74cd0b00d2dae5faa2bcb1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 01:36:21 -0300 Subject: [PATCH 050/235] WIP-GodFavours: more favours added; --- Main/Source/command.cpp | 1 + Main/Source/gods.cpp | 96 ++++++++++++++++++++++++++++------------- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index e30ebfd9c..0d1b80db8 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1169,6 +1169,7 @@ truth commandsystem::AskFavour(character* Char) if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0)bOk=true; if(pgod->GetBasicAlignment() == NEUTRAL && game::GetPlayerAlignment() == 0)bOk=true; if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; + if(game::WizardModeIsReallyActive())bOk=true; if(bOk){ std::vector v = pgod->GetKnownSpells(); diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 2eda8f0e9..9254bfc31 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -150,6 +150,14 @@ int CalcDebit(int iDebit,int iDefault){ return iDebit; } +void AddKnownSpell(std::vector ks,festring fsNew) +{ + for(auto pfsSpell = ks.begin(); pfsSpell != ks.end(); pfsSpell++){ + if(*pfsSpell == fsNew)return; + } + ks.push_back(fsNew); +} + /** * * @param fsWhat @@ -165,7 +173,7 @@ bool sophos::Favour(cfestring fsWhat, int iDebit) ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement!"); game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); PLAYER->Move(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); - static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_TELEPORT));return true;}(); + static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_TELEPORT);return true;}(); Relation-=iDebit; return true; } @@ -575,7 +583,7 @@ bool silva::Favour(cfestring fsWhat, int iDebit) ADD_MESSAGE("%s feeds you fruits and wild berries.", GetName()); PLAYER->SetNP(SATIATED_LEVEL); - static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_FEED));return true;}(); + static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_FEED);return true;}(); Relation-=iDebit; return true; } @@ -598,7 +606,7 @@ bool silva::Favour(cfestring fsWhat, int iDebit) ADD_MESSAGE("Silva allows a little spell of gentle rain to pour down from above."); - static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_CALLRAIN));return true;}(); + static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_CALLRAIN);return true;}(); Relation-=iDebit; return true; } @@ -713,13 +721,13 @@ bool silva::Favour(cfestring fsWhat, int iDebit) for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); - static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_EARTHQUAKE));return true;}(); + static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_EARTHQUAKE);return true;}(); Relation-=iDebit; return true; } if(fsWhat==FAVOUR_SUMMONWOLF){ - iDebit=CalcDebit(iDebit,50); + iDebit=CalcDebit(iDebit,250); if(!god::Favour(fsWhat,iDebit))return false; int TryToCreate = 1 + RAND() % 7; @@ -749,7 +757,7 @@ bool silva::Favour(cfestring fsWhat, int iDebit) if(Created > 1) ADD_MESSAGE("Suddenly some tame wolves materialize around you."); - static bool bInitDummy=[this](){knownSpells.push_back(CONST_S(FAVOUR_SUMMONWOLF));return true;}(); + static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_SUMMONWOLF);return true;}(); Relation-=iDebit; return true; } @@ -794,8 +802,55 @@ void silva::PrayBadEffect() } } +#define FAVOUR_FIXEQUIPMENT "Fix one broken equipped item" +#define FAVOUR_STOPFIRE "Fix burns in one equipped item" + bool loricatus::Favour(cfestring fsWhat, int iDebit) { + if(fsWhat==FAVOUR_FIXEQUIPMENT){ + iDebit=CalcDebit(iDebit,250); + if(!god::Favour(fsWhat,iDebit))return false; + + for(int c = 0; c < PLAYER->GetEquipments(); ++c) + { + item* Equipment = PLAYER->GetEquipment(c); + + if(Equipment && Equipment->IsBroken()) + { + ADD_MESSAGE("%s fixes your %s.", GetName(), Equipment->CHAR_NAME(UNARTICLED)); + Equipment->Fix(); + break; + } + } + + static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_FIXEQUIPMENT);return true;}(); + Relation-=iDebit; + return true; + } + + if(fsWhat==FAVOUR_STOPFIRE){ + iDebit=CalcDebit(iDebit,50); + if(!god::Favour(fsWhat,iDebit))return false; + + for(int c = 0; c < PLAYER->GetEquipments(); ++c) + { + item* Equipment = PLAYER->GetEquipment(c); + + if(Equipment && Equipment->IsBurnt()) + { + ADD_MESSAGE("%s repairs the burns on your %s.", GetName(), Equipment->CHAR_NAME(UNARTICLED)); + Equipment->RemoveBurns(); + if(!Equipment->IsBurning()) + Equipment->ResetThermalEnergies(); + Equipment->ResetBurning(); + break; + } + } + + static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_STOPFIRE);return true;}(); + Relation-=iDebit; + return true; + } } void loricatus::PrayGoodEffect() @@ -868,32 +923,11 @@ void loricatus::PrayGoodEffect() } } - for(int c = 0; c < PLAYER->GetEquipments(); ++c) - { - item* Equipment = PLAYER->GetEquipment(c); - - if(Equipment && Equipment->IsBroken()) - { - ADD_MESSAGE("%s fixes your %s.", GetName(), Equipment->CHAR_NAME(UNARTICLED)); - Equipment->Fix(); - return; - } - } - - for(int c = 0; c < PLAYER->GetEquipments(); ++c) - { - item* Equipment = PLAYER->GetEquipment(c); + if(Favour(FAVOUR_FIXEQUIPMENT)) + return; - if(Equipment && Equipment->IsBurnt()) - { - ADD_MESSAGE("%s repairs the burns on your %s.", GetName(), Equipment->CHAR_NAME(UNARTICLED)); - Equipment->RemoveBurns(); - if(!Equipment->IsBurning()) - Equipment->ResetThermalEnergies(); - Equipment->ResetBurning(); - return; - } - } + if(Favour(FAVOUR_STOPFIRE)) + return; if(PLAYER->GetUsableArms()) ADD_MESSAGE("You feel a slight tingling in your hands."); From 2cfd08d12aaa7a33d04633a58d31e975c00ff526 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 19:11:09 -0300 Subject: [PATCH 051/235] WIP-GodFavours: broken, but fixing travisci warnings first; --- Main/Source/gods.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 9254bfc31..2b020ec67 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -236,6 +236,7 @@ void sophos::PrayBadEffect() bool valpurus::Favour(cfestring fsWhat, int iDebit) { + return false; } void valpurus::PrayGoodEffect() @@ -273,6 +274,7 @@ void valpurus::PrayBadEffect() bool legifer::Favour(cfestring fsWhat, int iDebit) { + return false; } void legifer::PrayGoodEffect() @@ -294,6 +296,7 @@ void legifer::PrayBadEffect() bool dulcis::Favour(cfestring fsWhat, int iDebit) { + return false; } void dulcis::PrayGoodEffect() @@ -393,6 +396,7 @@ void dulcis::PrayBadEffect() bool seges::Favour(cfestring fsWhat, int iDebit) { + return false; } void seges::PrayGoodEffect() @@ -484,6 +488,7 @@ void seges::PrayBadEffect() bool atavus::Favour(cfestring fsWhat, int iDebit) { + return false; } void atavus::PrayGoodEffect() @@ -851,6 +856,8 @@ bool loricatus::Favour(cfestring fsWhat, int iDebit) Relation-=iDebit; return true; } + + return false; } void loricatus::PrayGoodEffect() @@ -975,6 +982,7 @@ void loricatus::PrayBadEffect() bool cleptia::Favour(cfestring fsWhat, int iDebit) { + return false; } void cleptia::PrayGoodEffect() @@ -1026,6 +1034,7 @@ void cleptia::PrayBadEffect() bool mortifer::Favour(cfestring fsWhat, int iDebit) { + return false; } void mortifer::PrayGoodEffect() @@ -1067,6 +1076,7 @@ void mortifer::PrayBadEffect() bool mellis::Favour(cfestring fsWhat, int iDebit) { + return false; } void mellis::PrayGoodEffect() @@ -1248,6 +1258,7 @@ void infuscor::PrayBadEffect() bool nefas::Favour(cfestring fsWhat, int iDebit) { + return false; } void nefas::PrayGoodEffect() @@ -1340,6 +1351,7 @@ void nefas::PrayBadEffect() bool scabies::Favour(cfestring fsWhat, int iDebit) { + return false; } void scabies::PrayGoodEffect() @@ -1447,6 +1459,7 @@ void scabies::PrayBadEffect() bool infuscor::Favour(cfestring fsWhat, int iDebit) { + return false; } void infuscor::PrayGoodEffect() @@ -1535,6 +1548,7 @@ void infuscor::PrayGoodEffect() bool cruentus::Favour(cfestring fsWhat, int iDebit) { + return false; } void cruentus::PrayGoodEffect() From 4be0bfc07b1795be6fa18edc53a9e884ff13fe5e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 19:24:57 -0300 Subject: [PATCH 052/235] WIP-GodFavours: working; --- Main/Source/gods.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 2b020ec67..5f330766b 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -150,7 +150,7 @@ int CalcDebit(int iDebit,int iDefault){ return iDebit; } -void AddKnownSpell(std::vector ks,festring fsNew) +void AddKnownSpell(std::vector& ks,festring fsNew) { for(auto pfsSpell = ks.begin(); pfsSpell != ks.end(); pfsSpell++){ if(*pfsSpell == fsNew)return; From 2ee66fbd8e1230b958937ec0508b922929fbafd1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 19:44:13 -0300 Subject: [PATCH 053/235] WIP-GodFavours: improving; --- Main/Include/gods.h | 4 ++++ Main/Source/command.cpp | 14 +++++++++++++- Main/Source/gods.cpp | 6 +++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Main/Include/gods.h b/Main/Include/gods.h index 654e9c808..b632d5c2d 100644 --- a/Main/Include/gods.h +++ b/Main/Include/gods.h @@ -15,6 +15,10 @@ #include "god.h" +#define FAVOURDEBIT_AUTO -1 +#define FAVOURDEBIT_AUTOHALF -2 +#define FAVOURDEBIT_AUTODOUBLE -3 + GOD(valpurus, god) { public: diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 0d1b80db8..34aa7cd73 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -22,6 +22,7 @@ #include "game.h" #include "gear.h" #include "god.h" +#include "gods.h" #include "graphics.h" #include "human.h" #include "iconf.h" @@ -1197,8 +1198,19 @@ truth commandsystem::AskFavour(character* Char) if(Select & FELIST_ERROR_BIT) return false; + + god* G = vGS[Select].first; + festring fsFavour = vGS[Select].second; + int iDebit=FAVOURDEBIT_AUTO; + int DivineMaster = Char->GetLSquareUnder()->GetDivineMaster(); + if(DivineMaster){ + if(G == game::GetGod(DivineMaster)) + iDebit=FAVOURDEBIT_AUTOHALF; + else + iDebit=FAVOURDEBIT_AUTODOUBLE; + } - if(vGS[Select].first->Favour(vGS[Select].second,-1)){ + if(G->Favour(fsFavour,iDebit)){ Char->EditAP(-1000); return true; } diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 5f330766b..6a5420213 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -143,7 +143,11 @@ bool god::Favour(cfestring fsWhat, int iDebit) int CalcDebit(int iDebit,int iDefault){ if(iDebit!=0){ - if(iDebit==-1)iDebit=iDefault; + switch(iDebit){ + case FAVOURDEBIT_AUTO: iDebit=iDefault ;break; + case FAVOURDEBIT_AUTOHALF: iDebit=iDefault/2;break; + case FAVOURDEBIT_AUTODOUBLE: iDebit=iDefault*2;break; + } iDebit -= game::GetPlayer()->GetAttribute(MANA); if(iDebit<10)iDebit=10; //minimum } From cf65b4a0c53e51a36e7450ed886ecf450e301045 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 19:56:43 -0300 Subject: [PATCH 054/235] WIP-GodFavours: improving; --- Main/Source/command.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 34aa7cd73..b23fdfe0e 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1167,16 +1167,17 @@ truth commandsystem::AskFavour(character* Char) god* pgod = game::GetGod(c); bool bOk=false; - if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0)bOk=true; + if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0)bOk=true; if(pgod->GetBasicAlignment() == NEUTRAL && game::GetPlayerAlignment() == 0)bOk=true; - if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; - if(game::WizardModeIsReallyActive())bOk=true; + if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; - if(bOk){ + if(bOk || game::WizardModeIsReallyActive()){ std::vector v = pgod->GetKnownSpells(); for(auto pfsSpell = v.begin(); pfsSpell != v.end(); pfsSpell++){ felSpellList.AddEntry(CONST_S("")+ - pgod->GetName()+" may grant you a \""+*pfsSpell+"\" favour.",LIGHT_GRAY); + game::GetAlignment(pgod->GetAlignment())+" "+ + pgod->GetName()+" may grant you a \""+*pfsSpell+"\" favour.", + bOk ? LIGHT_GRAY : RED); std::pair GS; GS.first = pgod; From 16dd209398184ef82a9aba66e74fc1c8ea30174b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 20:55:48 -0300 Subject: [PATCH 055/235] WIP-GodFavours: improving code; --- Main/Include/god.h | 4 +- Main/Source/gods.cpp | 115 ++++++++++++++++++++++++++++++------------- 2 files changed, 85 insertions(+), 34 deletions(-) diff --git a/Main/Include/god.h b/Main/Include/god.h index 9c848ddf3..fc2be6ff5 100644 --- a/Main/Include/god.h +++ b/Main/Include/god.h @@ -38,6 +38,7 @@ class godprototype cchar* ClassID; }; +typedef bool (*CallFavourType)(god*); class god { public: @@ -85,7 +86,8 @@ class god virtual truth LikesVomit() const { return false; } virtual bool Favour(cfestring fsWhat, int iDebit=0); const std::vector GetKnownSpells() const { return knownSpells; } - protected: + bool CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat, int iDebit, int iDbtDefault); +protected: virtual void PrayGoodEffect() = 0; virtual void PrayBadEffect() = 0; int Relation, LastPray; diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 6a5420213..14259a935 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -129,6 +129,16 @@ int mortifer::GetBasicAlignment() const { return EVIL; } col16 mortifer::GetColor() const { return CHAOS_BASIC_COLOR; } col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } +/** + * these are also used as maching IDs, + * this means that changing these texts will change what is saved on the savegame file... + */ +#define FAVOUR_CALLRAIN "Make it Rain" +#define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" +#define FAVOUR_FEED "Feed me up" +#define FAVOUR_FIXEQUIPMENT "Fix one broken equipped item" +#define FAVOUR_STOPFIRE "Undo the burns of one equipped item" +#define FAVOUR_SUMMONWOLF "Summon wolf friend(s)" #define FAVOUR_TELEPORT "Teleport" bool god::Favour(cfestring fsWhat, int iDebit) @@ -162,21 +172,22 @@ void AddKnownSpell(std::vector& ks,festring fsNew) ks.push_back(fsNew); } -/** - * - * @param fsWhat - * @param iDebit if -1 will be automatic - * @return - */ -bool sophos::Favour(cfestring fsWhat, int iDebit) +bool FavourTeleport(god* G) { - if(fsWhat==FAVOUR_TELEPORT){ - iDebit=CalcDebit(iDebit,100); - if(!god::Favour(fsWhat,iDebit))return false; - ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement!"); game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); PLAYER->Move(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); + return true; +} + +bool god::CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat, int iDebit, int iDbtDefault) +{ + if(fsCallFavour!=fsWhat)return false; + + iDebit=CalcDebit(iDebit,iDbtDefault); + if(!god::Favour(fsWhat,iDebit))return false; + + if((*call)(this)){ static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_TELEPORT);return true;}(); Relation-=iDebit; return true; @@ -185,6 +196,18 @@ bool sophos::Favour(cfestring fsWhat, int iDebit) return false; } +/** + * + * @param fsWhat + * @param iDebit if -1 will be automatic + * @return + */ +bool sophos::Favour(cfestring fsWhat, int iDebit) +{ + if(CallFavour(&FavourTeleport,FAVOUR_TELEPORT,fsWhat,iDebit,100))return true; + return false; +} + void sophos::PrayGoodEffect() { truth DidHelp = false; @@ -578,28 +601,55 @@ void atavus::PrayBadEffect() PLAYER->CheckDeath(CONST_S("killed by Atavus's humour")); } -#define FAVOUR_FEED "Feed me up" -#define FAVOUR_CALLRAIN "Call Rain" -#define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" -#define FAVOUR_SUMMONWOLF "Summon wolf friends" +bool FavourFeed(god* G) +{ + ADD_MESSAGE("%s feeds you fruits and wild berries.", G->GetName()); + PLAYER->SetNP(SATIATED_LEVEL); + return true; +} + +bool FavourCallRain(god* G) +{ + beamdata Beam + ( + 0, + CONST_S("drowned by the tears of ") + G->GetName(), + YOURSELF, + 0 + ); + + lsquare* Square = PLAYER->GetLSquareUnder(); + PLAYER->SpillFluid(0, liquid::Spawn(WATER, 400 + RAND() % 800)); + Square->LiquidRain(Beam, WATER); + + ADD_MESSAGE("Silva allows a little spell of gentle rain to pour down from above."); + + return true; +} bool silva::Favour(cfestring fsWhat, int iDebit) { - if(fsWhat==FAVOUR_FEED){ - iDebit=CalcDebit(iDebit,200); - if(!god::Favour(fsWhat,iDebit))return false; + if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; + if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,fsWhat,iDebit,75))return true; +/* + IF_CODEFAVOUR(FAVOUR_FEED,200); +// if(fsWhat==FAVOUR_FEED){ +// iDebit=CalcDebit(iDebit,200); +// if(!god::Favour(fsWhat,iDebit))return false; ADD_MESSAGE("%s feeds you fruits and wild berries.", GetName()); PLAYER->SetNP(SATIATED_LEVEL); - static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_FEED);return true;}(); - Relation-=iDebit; - return true; - } - - if(fsWhat==FAVOUR_CALLRAIN){ - iDebit=CalcDebit(iDebit,75); - if(!god::Favour(fsWhat,iDebit))return false; +// static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_FEED);return true;}(); +// Relation-=iDebit; +// return true; +// } + ENDIF_CODEFAVOUR(FAVOUR_FEED); + + IF_CODEFAVOUR(FAVOUR_CALLRAIN,75); +// if(fsWhat==FAVOUR_CALLRAIN){ +// iDebit=CalcDebit(iDebit,75); +// if(!god::Favour(fsWhat,iDebit))return false; beamdata Beam ( @@ -615,10 +665,12 @@ bool silva::Favour(cfestring fsWhat, int iDebit) ADD_MESSAGE("Silva allows a little spell of gentle rain to pour down from above."); - static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_CALLRAIN);return true;}(); - Relation-=iDebit; - return true; - } +// static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_CALLRAIN);return true;}(); +// Relation-=iDebit; +// return true; +// } + ENDIF_CODEFAVOUR(FAVOUR_CALLRAIN); +*/ if(fsWhat==FAVOUR_EARTHQUAKE){ iDebit=CalcDebit(iDebit,500); @@ -811,9 +863,6 @@ void silva::PrayBadEffect() } } -#define FAVOUR_FIXEQUIPMENT "Fix one broken equipped item" -#define FAVOUR_STOPFIRE "Fix burns in one equipped item" - bool loricatus::Favour(cfestring fsWhat, int iDebit) { if(fsWhat==FAVOUR_FIXEQUIPMENT){ From 2f405041b7741e64c22c1cf2c6cbeffa0500272e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 21:09:13 -0300 Subject: [PATCH 056/235] WIP-GodFavours: added for valpurus and legifer. Cleaned dead messy macroed new code; --- Main/Source/gods.cpp | 74 +++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 14259a935..e99119da0 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -140,6 +140,8 @@ col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } #define FAVOUR_STOPFIRE "Undo the burns of one equipped item" #define FAVOUR_SUMMONWOLF "Summon wolf friend(s)" #define FAVOUR_TELEPORT "Teleport" +#define FAVOUR_HOLYGREN "Paladin's Holy Grenade" +#define FAVOUR_FIRESTORM "Fiery Firestorm" bool god::Favour(cfestring fsWhat, int iDebit) { @@ -261,8 +263,17 @@ void sophos::PrayBadEffect() PLAYER->CheckDeath(CONST_S("shattered to pieces by the wrath of ") + GetName(), 0); } +bool FavourHolyGrenade(god* G) +{ + ADD_MESSAGE("You hear a booming voice: \"I GRANT THEE THIS HOLY HAND GRENADE " + "THAT WITH IT THOU MAYEST BLOW THY ENEMIES TO TINY BITS, MY PALADIN!\""); + PLAYER->GetGiftStack()->AddItem(holyhandgrenade::Spawn()); + return true; +} + bool valpurus::Favour(cfestring fsWhat, int iDebit) { + if(CallFavour(&FavourHolyGrenade,FAVOUR_HOLYGREN,fsWhat,iDebit,300))return true; return false; } @@ -286,9 +297,7 @@ void valpurus::PrayGoodEffect() } else // Player already received championship gift, give holy handgrenade instead. { - ADD_MESSAGE("You hear a booming voice: \"I GRANT THEE THIS HOLY HAND GRENADE " - "THAT WITH IT THOU MAYEST BLOW THY ENEMIES TO TINY BITS, MY PALADIN!\""); - PLAYER->GetGiftStack()->AddItem(holyhandgrenade::Spawn()); + Favour(FAVOUR_HOLYGREN); } } @@ -299,19 +308,26 @@ void valpurus::PrayBadEffect() PLAYER->CheckDeath(CONST_S("faced the hammer of Justice from the hand of ") + GetName(), 0); } +bool FavourFirestorm(god* G) +{ + // I think this is a remnant of past development that you call upon Inlux rather than Legifer. --red_kangaroo + // No, my bad. Inlux is an anagram of Linux, which will hopefully save us from the horrid Bill. ;) + ADD_MESSAGE("A booming voice echoes: \"Inlux! Inlux! Save us!\" A huge firestorm engulfs everything around you."); + //ADD_MESSAGE("You are surrounded by the righteous flames of %s.", GetName()); + game::GetCurrentLevel()->Explosion(PLAYER, CONST_S("killed by the holy flames of ") + G->GetName(), PLAYER->GetPos(), + (Max(20 * PLAYER->GetAttribute(WISDOM), 1) + Max(G->GetRelation(), 0)) >> 3, false); + return true; +} + bool legifer::Favour(cfestring fsWhat, int iDebit) { + if(CallFavour(&FavourFirestorm,FAVOUR_FIRESTORM,fsWhat,iDebit,200))return true; return false; } void legifer::PrayGoodEffect() { - // I think this is a remnant of past development that you call upon Inlux rather than Legifer. --red_kangaroo - // No, my bad. Inlux is an anagram of Linux, which will hopefully save us from the horrid Bill. ;) - ADD_MESSAGE("A booming voice echoes: \"Inlux! Inlux! Save us!\" A huge firestorm engulfs everything around you."); - //ADD_MESSAGE("You are surrounded by the righteous flames of %s.", GetName()); - game::GetCurrentLevel()->Explosion(PLAYER, CONST_S("killed by the holy flames of ") + GetName(), PLAYER->GetPos(), - (Max(20 * PLAYER->GetAttribute(WISDOM), 1) + Max(GetRelation(), 0)) >> 3, false); + Favour(FAVOUR_FIRESTORM); } void legifer::PrayBadEffect() @@ -631,46 +647,6 @@ bool silva::Favour(cfestring fsWhat, int iDebit) { if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,fsWhat,iDebit,75))return true; -/* - IF_CODEFAVOUR(FAVOUR_FEED,200); -// if(fsWhat==FAVOUR_FEED){ -// iDebit=CalcDebit(iDebit,200); -// if(!god::Favour(fsWhat,iDebit))return false; - - ADD_MESSAGE("%s feeds you fruits and wild berries.", GetName()); - PLAYER->SetNP(SATIATED_LEVEL); - -// static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_FEED);return true;}(); -// Relation-=iDebit; -// return true; -// } - ENDIF_CODEFAVOUR(FAVOUR_FEED); - - IF_CODEFAVOUR(FAVOUR_CALLRAIN,75); -// if(fsWhat==FAVOUR_CALLRAIN){ -// iDebit=CalcDebit(iDebit,75); -// if(!god::Favour(fsWhat,iDebit))return false; - - beamdata Beam - ( - 0, - CONST_S("drowned by the tears of ") + GetName(), - YOURSELF, - 0 - ); - - lsquare* Square = PLAYER->GetLSquareUnder(); - PLAYER->SpillFluid(0, liquid::Spawn(WATER, 400 + RAND() % 800)); - Square->LiquidRain(Beam, WATER); - - ADD_MESSAGE("Silva allows a little spell of gentle rain to pour down from above."); - -// static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_CALLRAIN);return true;}(); -// Relation-=iDebit; -// return true; -// } - ENDIF_CODEFAVOUR(FAVOUR_CALLRAIN); -*/ if(fsWhat==FAVOUR_EARTHQUAKE){ iDebit=CalcDebit(iDebit,500); From db206374fcd61ff534f0596f12f69ae50d17834b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 21:52:44 -0300 Subject: [PATCH 057/235] WIP-GodFavours: added dulcis; --- Main/Source/gods.cpp | 102 ++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index e99119da0..3823cdc19 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -135,13 +135,15 @@ col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } */ #define FAVOUR_CALLRAIN "Make it Rain" #define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" -#define FAVOUR_FEED "Feed me up" +#define FAVOUR_FEED "Feed you up" #define FAVOUR_FIXEQUIPMENT "Fix one broken equipped item" #define FAVOUR_STOPFIRE "Undo the burns of one equipped item" -#define FAVOUR_SUMMONWOLF "Summon wolf friend(s)" +#define FAVOUR_SUMMONWOLF "Summon Wolf friend(s)" #define FAVOUR_TELEPORT "Teleport" #define FAVOUR_HOLYGREN "Paladin's Holy Grenade" #define FAVOUR_FIRESTORM "Fiery Firestorm" +#define FAVOUR_EXTINGUISHFIRE "Put out these Flames" +#define FAVOUR_TAME "Tame this Monster" bool god::Favour(cfestring fsWhat, int iDebit) { @@ -337,49 +339,16 @@ void legifer::PrayBadEffect() PLAYER->CheckDeath(CONST_S("burned to death by the holy flames of ") + GetName(), 0); } -bool dulcis::Favour(cfestring fsWhat, int iDebit) +bool FavourExtinguishFire(god* G) { - return false; + PLAYER->Extinguish(true); + return true; } -void dulcis::PrayGoodEffect() +bool FavourTame(god* G) { - truth HasHelped = false; - - for(int d = 0; d < PLAYER->GetNeighbourSquares(); ++d) - { - square* Square = PLAYER->GetNeighbourSquare(d); - - if(Square) - { - character* Char = Square->GetCharacter(); - - if(Char) - if(Char->IsBurning()) - if(Char->GetTeam() == PLAYER->GetTeam()) - { - Char->Extinguish(true); - HasHelped = true; - } - } - } - if(PLAYER->IsBurning()) - { - PLAYER->Extinguish(true); - if(HasHelped) - ADD_MESSAGE("Dulcis helps you and your companions to put out the flames."); - else - ADD_MESSAGE("Dulcis helps you to put out the flames."); - - HasHelped = true; - } - else if(HasHelped) - ADD_MESSAGE("Dulcis helps your companions to put out the flames."); - if(HasHelped) - return; - else - ADD_MESSAGE("A beautiful melody echoes around you."); - + bool HasHelped = false; + for(int d = 0; d < PLAYER->GetNeighbourSquares(); ++d) { square* Square = PLAYER->GetNeighbourSquare(d); @@ -420,8 +389,59 @@ void dulcis::PrayGoodEffect() } } } + + return HasHelped; +} + +bool dulcis::Favour(cfestring fsWhat, int iDebit) +{ + if(CallFavour(&FavourExtinguishFire,FAVOUR_EXTINGUISHFIRE,fsWhat,iDebit,50))return true; + if(CallFavour(&FavourTame,FAVOUR_TAME,fsWhat,iDebit,250))return true; + return false; +} + +void dulcis::PrayGoodEffect() +{ + truth HasHelped = false; + + for(int d = 0; d < PLAYER->GetNeighbourSquares(); ++d) + { + square* Square = PLAYER->GetNeighbourSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char) + if(Char->IsBurning()) + if(Char->GetTeam() == PLAYER->GetTeam()) + { + Char->Extinguish(true); + HasHelped = true; + } + } + } + if(PLAYER->IsBurning()) + { + Favour(FAVOUR_EXTINGUISHFIRE); + if(HasHelped) + ADD_MESSAGE("Dulcis helps you and your companions to put out the flames."); + else + ADD_MESSAGE("Dulcis helps you to put out the flames."); + + HasHelped = true; + } + else if(HasHelped) + ADD_MESSAGE("Dulcis helps your companions to put out the flames."); + if(HasHelped) + return; + else + ADD_MESSAGE("A beautiful melody echoes around you."); + + HasHelped = Favour(FAVOUR_TAME); if(HasHelped) return; + if (GetRelation() >= 50) { ADD_MESSAGE("You feel the music resonate within you.", GetName()); From f7a2acca476ca1b2eafeabfeec2c688b95506cd2 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 22:36:51 -0300 Subject: [PATCH 058/235] WIP-GodFavours: added seges; --- Main/Source/gods.cpp | 119 +++++++++++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 3823cdc19..153543228 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -133,17 +133,26 @@ col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } * these are also used as maching IDs, * this means that changing these texts will change what is saved on the savegame file... */ +#define FAVOUR_BLOATED "Feeds you a lot" //TODO consider price vs FAVOUR_FEED #define FAVOUR_CALLRAIN "Make it Rain" +#define FAVOUR_CURELEPROSY "Cure Leprosy" +#define FAVOUR_CURELYCANTHROPY "Cure Lycanthropy" +#define FAVOUR_CUREMINDWORM "Cure Mindworm" +#define FAVOUR_CUREPOISON "Cure Poison" +#define FAVOUR_CURETAPEWORM "Cure Tapeworm" +#define FAVOUR_CUREWOUNDS "Cure Wounds" #define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" -#define FAVOUR_FEED "Feed you up" +#define FAVOUR_EXTINGUISHFIRE "Put out these Flames" //TODO consider price vs FAVOUR_HEALBURNS +#define FAVOUR_FEED "Feeds you well" +#define FAVOUR_FIRESTORM "Fiery Firestorm" #define FAVOUR_FIXEQUIPMENT "Fix one broken equipped item" +#define FAVOUR_HEALBURNS "Heals your burns" +#define FAVOUR_HOLYGREN "Paladin's Holy Grenade" +#define FAVOUR_INVIGORATE "Invigorate" #define FAVOUR_STOPFIRE "Undo the burns of one equipped item" #define FAVOUR_SUMMONWOLF "Summon Wolf friend(s)" -#define FAVOUR_TELEPORT "Teleport" -#define FAVOUR_HOLYGREN "Paladin's Holy Grenade" -#define FAVOUR_FIRESTORM "Fiery Firestorm" -#define FAVOUR_EXTINGUISHFIRE "Put out these Flames" #define FAVOUR_TAME "Tame this Monster" +#define FAVOUR_TELEPORT "Teleport" bool god::Favour(cfestring fsWhat, int iDebit) { @@ -457,8 +466,77 @@ void dulcis::PrayBadEffect() PLAYER->CheckDeath(CONST_S("became insane by listening ") + GetName() + " too much", 0); } +bool FavourCureWounds(god* G) +{ + ADD_MESSAGE("%s cures your wounds.", G->GetName()); + PLAYER->RestoreLivingHP(); + return true; +} +bool FavourCurePoison(god* G) +{ + ADD_MESSAGE("%s removes the foul liquid in your veins.", G->GetName()); + PLAYER->DeActivateTemporaryState(POISONED); + return true; +} +bool FavourCureLeprosy(god* G) +{ + ADD_MESSAGE("%s cures your leprosy.", G->GetName()); + PLAYER->DeActivateTemporaryState(LEPROSY); + return true; +} +bool FavourCureLycanthropy(god* G) +{ + ADD_MESSAGE("%s cures your animalistic urges.", G->GetName()); + PLAYER->DeActivateTemporaryState(LYCANTHROPY); + return true; +} +bool FavourCureTapeworm(god* G) +{ + ADD_MESSAGE("%s removes the evil hidden in your guts.", G->GetName()); + PLAYER->DeActivateTemporaryState(PARASITE_TAPE_WORM); + return true; +} +bool FavourCureMindworm(god* G) +{ + ADD_MESSAGE("%s removes the evil hidden in your brain.", G->GetName()); + PLAYER->DeActivateTemporaryState(PARASITE_MIND_WORM); + return true; +} +bool FavourBloated(god* G) +{ + ADD_MESSAGE("Your stomach feels full again."); + PLAYER->SetNP(BLOATED_LEVEL); + return true; +} +bool FavourHealBurns(god* G) +{ + ADD_MESSAGE("%s heals your burns.", G->GetName()); + //PLAYER->RemoveBurns(); // removes the burns and restores HP + if(!PLAYER->IsBurning()) // the player would do well to put the flames out himself first + PLAYER->ResetThermalEnergies(); + PLAYER->ResetLivingBurning(); // In keeping with Seges' au natural theme. Does roughly the same as RemoveBurns(), + // only without the message(?) and it resets the burn level counter + return true; +} +bool FavourInvigorate(god* G) +{ + ADD_MESSAGE("You don't feel a bit tired anymore."); + PLAYER->RestoreStamina(); + return true; +} + bool seges::Favour(cfestring fsWhat, int iDebit) { + if(CallFavour(&FavourCureWounds,FAVOUR_CUREWOUNDS,fsWhat,iDebit,150))return true; + if(CallFavour(&FavourCurePoison,FAVOUR_CUREPOISON,fsWhat,iDebit,200))return true; + if(CallFavour(&FavourCureLeprosy,FAVOUR_CURELEPROSY,fsWhat,iDebit,250))return true; + if(CallFavour(&FavourCureLycanthropy,FAVOUR_CURELYCANTHROPY,fsWhat,iDebit,300))return true; + //TODO is vampirism bad in anyway? + if(CallFavour(&FavourCureTapeworm,FAVOUR_CURETAPEWORM,fsWhat,iDebit,250))return true; + if(CallFavour(&FavourCureMindworm,FAVOUR_CUREMINDWORM,fsWhat,iDebit,500))return true; + if(CallFavour(&FavourBloated,FAVOUR_BLOATED,fsWhat,iDebit,300))return true; + if(CallFavour(&FavourHealBurns,FAVOUR_HEALBURNS,fsWhat,iDebit,50))return true; + if(CallFavour(&FavourInvigorate,FAVOUR_INVIGORATE,fsWhat,iDebit,250))return true; return false; } @@ -466,29 +544,25 @@ void seges::PrayGoodEffect() { if(PLAYER->IsInBadCondition()) { - ADD_MESSAGE("%s cures your wounds.", GetName()); - PLAYER->RestoreLivingHP(); + Favour(FAVOUR_CUREWOUNDS); return; } if(PLAYER->TemporaryStateIsActivated(POISONED)) { - ADD_MESSAGE("%s removes the foul liquid in your veins.", GetName()); - PLAYER->DeActivateTemporaryState(POISONED); + Favour(FAVOUR_CUREPOISON); return; } if(PLAYER->StateIsActivated(LEPROSY)) { - ADD_MESSAGE("%s cures your leprosy.", GetName()); - PLAYER->DeActivateTemporaryState(LEPROSY); + Favour(FAVOUR_CURELEPROSY); return; } if(PLAYER->TemporaryStateIsActivated(LYCANTHROPY)) { - ADD_MESSAGE("%s cures your animalistic urges.", GetName()); - PLAYER->DeActivateTemporaryState(LYCANTHROPY); + Favour(FAVOUR_CURELYCANTHROPY); return; } @@ -501,39 +575,30 @@ void seges::PrayGoodEffect() if(PLAYER->TemporaryStateIsActivated(PARASITE_TAPE_WORM)) { - ADD_MESSAGE("%s removes the evil hidden in your guts.", GetName()); - PLAYER->DeActivateTemporaryState(PARASITE_TAPE_WORM); + Favour(FAVOUR_CURETAPEWORM); return; } if(PLAYER->TemporaryStateIsActivated(PARASITE_MIND_WORM)) { - ADD_MESSAGE("%s removes the evil hidden in your brain.", GetName()); - PLAYER->DeActivateTemporaryState(PARASITE_MIND_WORM); + Favour(FAVOUR_CUREMINDWORM); return; } if(PLAYER->GetNP() < SATIATED_LEVEL) { - ADD_MESSAGE("Your stomach feels full again."); - PLAYER->SetNP(BLOATED_LEVEL); + Favour(FAVOUR_BLOATED); return; } if(PLAYER->IsBurnt()) { - ADD_MESSAGE("%s heals your burns.", GetName()); - //PLAYER->RemoveBurns(); // removes the burns and restores HP - if(!PLAYER->IsBurning()) // the player would do well to put the flames out himself first - PLAYER->ResetThermalEnergies(); - PLAYER->ResetLivingBurning(); // In keeping with Seges' au natural theme. Does roughly the same as RemoveBurns(), - // only without the message(?) and it resets the burn level counter + Favour(FAVOUR_HEALBURNS); return; } // Always return at least some message. - ADD_MESSAGE("You don't feel a bit tired anymore."); - PLAYER->RestoreStamina(); + Favour(FAVOUR_INVIGORATE); return; } From ca6d4ca4831b9201e91b68d59dd88fb05df39785 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 22:42:55 -0300 Subject: [PATCH 059/235] WIP-GodFavours: improving; --- Main/Source/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index b23fdfe0e..6c7e78fee 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1193,7 +1193,7 @@ truth commandsystem::AskFavour(character* Char) if(Select == LIST_WAS_EMPTY) { - ADD_MESSAGE("You do not know about any favours yet..."); + ADD_MESSAGE("You can't feel the availability of any %s favours...", game::GetVerbalPlayerAlignment()); return false; } From 95fd4734db802f66ed959927b29d2335317aa027 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 23:01:53 -0300 Subject: [PATCH 060/235] WIP-GodFavours: fix favour learning; --- Main/Source/gods.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 153543228..6c4221298 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -197,11 +197,13 @@ bool god::CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat { if(fsCallFavour!=fsWhat)return false; + if(iDebit==0) //came thru normal praying + AddKnownSpell(knownSpells,fsCallFavour); + iDebit=CalcDebit(iDebit,iDbtDefault); if(!god::Favour(fsWhat,iDebit))return false; if((*call)(this)){ - static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_TELEPORT);return true;}(); Relation-=iDebit; return true; } From d659b985e9b2485bc0f0ad9b49a75ca8c7ed2c90 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 29 Mar 2020 23:55:06 -0300 Subject: [PATCH 061/235] WIP-GodFavours: restored normal praying (was broken); --- Main/Source/gods.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 6c4221298..2421a4ef8 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -201,10 +201,14 @@ bool god::CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat AddKnownSpell(knownSpells,fsCallFavour); iDebit=CalcDebit(iDebit,iDbtDefault); - if(!god::Favour(fsWhat,iDebit))return false; + + if(iDebit>0) + if(!god::Favour(fsWhat,iDebit)) + return false; if((*call)(this)){ - Relation-=iDebit; + if(iDebit>0) + Relation-=iDebit; return true; } From a9b5bbb7edb358173eb4a63adc82d3ad85ef2f55 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 31 Mar 2020 16:55:25 -0300 Subject: [PATCH 062/235] WIP-GodFavours: Translated god alignment to player's so they can match now (in a sense). Made favour cost less in case player is very well aligned with just one of the gods. Updated code to newest flow; --- Main/Include/game.h | 1 + Main/Source/game.cpp | 20 +++++++++++ Main/Source/gods.cpp | 81 +++++++++++++++++++++----------------------- 3 files changed, 60 insertions(+), 42 deletions(-) diff --git a/Main/Include/game.h b/Main/Include/game.h index 73d4b879e..4ab24a0ad 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -280,6 +280,7 @@ class game static int GetDirectionForVector(v2); static int GetPlayerAlignment(); static cchar* GetVerbalPlayerAlignment(); + static int GetGodAlignmentVsPlayer(god* G); static void CreateGods(); static int GetScreenXSize(); static int GetScreenYSize(); diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 570c5687d..4cdbeb260 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -4053,6 +4053,26 @@ int game::GetPlayerAlignment() return -4; } +int game::GetGodAlignmentVsPlayer(god* G) +{ + switch(G->GetAlignment()){ + case ALPP: return 4; //L++ + case ALP: return 3; //L+ + case AL: return 2; //L + case ALM: return 1; //L- + case ANP: return 1; //N+... 1 is to match the description at GetVerbalPlayerAlignment() + case AN: return 0; //N= + case ANM: return -1; //N-... -1 is to match the description at GetVerbalPlayerAlignment() + case ACP: return -1; //C+ + case AC: return -2; //C + case ACM: return -3; //C- + case ACMM: return -4; //C-- + } + + ABORT("unsupported alignment %d",G->GetAlignment()); + return -1000; //dummy +} + cchar* game::GetVerbalPlayerAlignment() { switch(GetPlayerAlignment()){ diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 2421a4ef8..42d55a41c 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -164,15 +164,23 @@ bool god::Favour(cfestring fsWhat, int iDebit) return true; } -int CalcDebit(int iDebit,int iDefault){ +int CalcDebit(god* G,int iDebit,int iDefault){ if(iDebit!=0){ switch(iDebit){ case FAVOURDEBIT_AUTO: iDebit=iDefault ;break; case FAVOURDEBIT_AUTOHALF: iDebit=iDefault/2;break; case FAVOURDEBIT_AUTODOUBLE: iDebit=iDefault*2;break; } + + // can ask more favours if very well aligned + if(game::GetPlayerAlignment() == game::GetGodAlignmentVsPlayer(G)){ + iDebit/=3; + } + + // skilled in praying :) iDebit -= game::GetPlayer()->GetAttribute(MANA); - if(iDebit<10)iDebit=10; //minimum + + if(iDebit<10)iDebit=10; //max of 100 vafours in the best case (master) only } return iDebit; } @@ -200,7 +208,7 @@ bool god::CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat if(iDebit==0) //came thru normal praying AddKnownSpell(knownSpells,fsCallFavour); - iDebit=CalcDebit(iDebit,iDbtDefault); + iDebit=CalcDebit(this,iDebit,iDbtDefault); if(iDebit>0) if(!god::Favour(fsWhat,iDebit)) @@ -734,15 +742,8 @@ bool FavourCallRain(god* G) return true; } -bool silva::Favour(cfestring fsWhat, int iDebit) +bool FavourEarthQuake(god* G) { - if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; - if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,fsWhat,iDebit,75))return true; - - if(fsWhat==FAVOUR_EARTHQUAKE){ - iDebit=CalcDebit(iDebit,500); - if(!god::Favour(fsWhat,iDebit))return false; - ADD_MESSAGE("Suddenly a horrible earthquake shakes the level."); int c, Tunnels = 2 + RAND() % 3; if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) @@ -848,16 +849,12 @@ bool silva::Favour(cfestring fsWhat, int iDebit) for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); - - static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_EARTHQUAKE);return true;}(); - Relation-=iDebit; - return true; - } - if(fsWhat==FAVOUR_SUMMONWOLF){ - iDebit=CalcDebit(iDebit,250); - if(!god::Favour(fsWhat,iDebit))return false; - + return true; +} + +bool FavourSummonWolf(god* G) +{ int TryToCreate = 1 + RAND() % 7; int Created = 0; @@ -885,10 +882,15 @@ bool silva::Favour(cfestring fsWhat, int iDebit) if(Created > 1) ADD_MESSAGE("Suddenly some tame wolves materialize around you."); - static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_SUMMONWOLF);return true;}(); - Relation-=iDebit; return true; - } +} + +bool silva::Favour(cfestring fsWhat, int iDebit) +{ + if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; + if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,fsWhat,iDebit,75))return true; + if(CallFavour(&FavourEarthQuake,FAVOUR_EARTHQUAKE,fsWhat,iDebit,500))return true; + if(CallFavour(&FavourSummonWolf,FAVOUR_SUMMONWOLF,fsWhat,iDebit,250))return true; return false; } @@ -930,40 +932,32 @@ void silva::PrayBadEffect() } } -bool loricatus::Favour(cfestring fsWhat, int iDebit) +bool FavourFixEquipment(god* G) { - if(fsWhat==FAVOUR_FIXEQUIPMENT){ - iDebit=CalcDebit(iDebit,250); - if(!god::Favour(fsWhat,iDebit))return false; - for(int c = 0; c < PLAYER->GetEquipments(); ++c) { item* Equipment = PLAYER->GetEquipment(c); if(Equipment && Equipment->IsBroken()) { - ADD_MESSAGE("%s fixes your %s.", GetName(), Equipment->CHAR_NAME(UNARTICLED)); + ADD_MESSAGE("%s fixes your %s.", G->GetName(), Equipment->CHAR_NAME(UNARTICLED)); Equipment->Fix(); break; } } - - static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_FIXEQUIPMENT);return true;}(); - Relation-=iDebit; - return true; - } - - if(fsWhat==FAVOUR_STOPFIRE){ - iDebit=CalcDebit(iDebit,50); - if(!god::Favour(fsWhat,iDebit))return false; + return true; +} + +bool FavourStopFire(god* G) +{ for(int c = 0; c < PLAYER->GetEquipments(); ++c) { item* Equipment = PLAYER->GetEquipment(c); if(Equipment && Equipment->IsBurnt()) { - ADD_MESSAGE("%s repairs the burns on your %s.", GetName(), Equipment->CHAR_NAME(UNARTICLED)); + ADD_MESSAGE("%s repairs the burns on your %s.", G->GetName(), Equipment->CHAR_NAME(UNARTICLED)); Equipment->RemoveBurns(); if(!Equipment->IsBurning()) Equipment->ResetThermalEnergies(); @@ -972,10 +966,13 @@ bool loricatus::Favour(cfestring fsWhat, int iDebit) } } - static bool bInitDummy=[this](){AddKnownSpell(knownSpells,FAVOUR_STOPFIRE);return true;}(); - Relation-=iDebit; return true; - } +} + +bool loricatus::Favour(cfestring fsWhat, int iDebit) +{ + if(CallFavour(&FavourFixEquipment,FAVOUR_FIXEQUIPMENT,fsWhat,iDebit,250))return true; + if(CallFavour(&FavourStopFire,FAVOUR_STOPFIRE,fsWhat,iDebit,50))return true; return false; } From a86fee8455372c8512134a81a299c31340a19f10 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 31 Mar 2020 17:19:40 -0300 Subject: [PATCH 063/235] WIP-GodFavours: adjustment to compensate neutral favours access vs other alignments; --- Main/Source/game.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 4cdbeb260..c2b003686 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -4060,9 +4060,16 @@ int game::GetGodAlignmentVsPlayer(god* G) case ALP: return 3; //L+ case AL: return 2; //L case ALM: return 1; //L- - case ANP: return 1; //N+... 1 is to match the description at GetVerbalPlayerAlignment() + + /** + * Neutral has 1 less god, so 1 less source of favours. To compensate, these 3 being 0 will make it easier in favour cost for neutrally aligned players. + * Btw, these "N" matches the base Neutral alignment for these gods. + * And... it won't match very well the description at GetVerbalPlayerAlignment() tho, but let player guess it :) + */ + case ANP: return 0; //N+ case AN: return 0; //N= - case ANM: return -1; //N-... -1 is to match the description at GetVerbalPlayerAlignment() + case ANM: return 0; //N- + case ACP: return -1; //C+ case AC: return -2; //C case ACM: return -3; //C- From 80242ee5913a63686897bb6d48ca92f11a2ea047 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 31 Mar 2020 19:07:45 -0300 Subject: [PATCH 064/235] WIP-GodFavours: added atavus, cleptia, mortifer, mellis, nefas, scabies; --- Main/Source/gods.cpp | 261 ++++++++++++++++++++++++++++--------------- 1 file changed, 174 insertions(+), 87 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 42d55a41c..9b147ca31 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -133,23 +133,31 @@ col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } * these are also used as maching IDs, * this means that changing these texts will change what is saved on the savegame file... */ -#define FAVOUR_BLOATED "Feeds you a lot" //TODO consider price vs FAVOUR_FEED #define FAVOUR_CALLRAIN "Make it Rain" +#define FAVOUR_CONFUSE "Cause Confusion amongst your enemies" #define FAVOUR_CURELEPROSY "Cure Leprosy" #define FAVOUR_CURELYCANTHROPY "Cure Lycanthropy" #define FAVOUR_CUREMINDWORM "Cure Mindworm" #define FAVOUR_CUREPOISON "Cure Poison" +#define FAVOUR_CURESLOWNESS "Cure Slowness" #define FAVOUR_CURETAPEWORM "Cure Tapeworm" #define FAVOUR_CUREWOUNDS "Cure Wounds" +#define FAVOUR_DISEASEIMMUNITY "Gain temporary Immunity to Diseases" #define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" +#define FAVOUR_ENCHANT "Enchant Equipment" +#define FAVOUR_ETHEREALMOV "Become Ethereal" #define FAVOUR_EXTINGUISHFIRE "Put out these Flames" //TODO consider price vs FAVOUR_HEALBURNS -#define FAVOUR_FEED "Feeds you well" +#define FAVOUR_FEED "Calms your Hunger" #define FAVOUR_FIRESTORM "Fiery Firestorm" #define FAVOUR_FIXEQUIPMENT "Fix one broken equipped item" #define FAVOUR_HEALBURNS "Heals your burns" #define FAVOUR_HOLYGREN "Paladin's Holy Grenade" +#define FAVOUR_INFRAVISION "See in the Darkness" #define FAVOUR_INVIGORATE "Invigorate" -#define FAVOUR_STOPFIRE "Undo the burns of one equipped item" +#define FAVOUR_INVISIBILITY "Become Invisible" +#define FAVOUR_SHOPPING "Black Friday" +#define FAVOUR_SPEEDUP "Make you Fast" +#define FAVOUR_STOPFIRE "Unburn one Equipment" #define FAVOUR_SUMMONWOLF "Summon Wolf friend(s)" #define FAVOUR_TAME "Tame this Monster" #define FAVOUR_TELEPORT "Teleport" @@ -516,12 +524,6 @@ bool FavourCureMindworm(god* G) PLAYER->DeActivateTemporaryState(PARASITE_MIND_WORM); return true; } -bool FavourBloated(god* G) -{ - ADD_MESSAGE("Your stomach feels full again."); - PLAYER->SetNP(BLOATED_LEVEL); - return true; -} bool FavourHealBurns(god* G) { ADD_MESSAGE("%s heals your burns.", G->GetName()); @@ -538,6 +540,23 @@ bool FavourInvigorate(god* G) PLAYER->RestoreStamina(); return true; } +bool FavourFeed(god* G) +{ + if(dynamic_cast(G)){ + ADD_MESSAGE("Your stomach feels full again."); + PLAYER->SetNP(BLOATED_LEVEL); + }else{ + if(dynamic_cast(G)) + ADD_MESSAGE("%s feeds you fruits and wild berries.", G->GetName()); + + if(dynamic_cast(G)) + ADD_MESSAGE("%s breast-feeds you.", G->GetName()); + + PLAYER->SetNP(SATIATED_LEVEL); + } + + return true; +} bool seges::Favour(cfestring fsWhat, int iDebit) { @@ -548,7 +567,7 @@ bool seges::Favour(cfestring fsWhat, int iDebit) //TODO is vampirism bad in anyway? if(CallFavour(&FavourCureTapeworm,FAVOUR_CURETAPEWORM,fsWhat,iDebit,250))return true; if(CallFavour(&FavourCureMindworm,FAVOUR_CUREMINDWORM,fsWhat,iDebit,500))return true; - if(CallFavour(&FavourBloated,FAVOUR_BLOATED,fsWhat,iDebit,300))return true; + if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,300))return true; //bloats if(CallFavour(&FavourHealBurns,FAVOUR_HEALBURNS,fsWhat,iDebit,50))return true; if(CallFavour(&FavourInvigorate,FAVOUR_INVIGORATE,fsWhat,iDebit,250))return true; return false; @@ -601,7 +620,7 @@ void seges::PrayGoodEffect() if(PLAYER->GetNP() < SATIATED_LEVEL) { - Favour(FAVOUR_BLOATED); + Favour(FAVOUR_FEED); return; } @@ -628,18 +647,13 @@ void seges::PrayBadEffect() ADD_MESSAGE("Seges tries to alter the contents of your stomach, but fails."); } -bool atavus::Favour(cfestring fsWhat, int iDebit) -{ - return false; -} - -void atavus::PrayGoodEffect() +bool FavourEnchantEquipment(god* G) { item* Enchantable; item* PairEnchantable; int LowEnchant = 99; truth Pair = false; - + for(int c = 0; c < PLAYER->GetEquipments(); ++c) { item* Equipment = PLAYER->GetEquipment(c); @@ -662,7 +676,7 @@ void atavus::PrayGoodEffect() } if(LowEnchant < 99) { - int EnchDiff = ((Enchantable->GetEnchantment()+2)*250 - GetRelation()) / 50; + int EnchDiff = ((Enchantable->GetEnchantment()+2)*250 - G->GetRelation()) / 50; if(EnchDiff <= 1 || !RAND_N(EnchDiff)) { if(Pair) { @@ -675,9 +689,22 @@ void atavus::PrayGoodEffect() ADD_MESSAGE("Your %s glows briefly blue. It feels very warm now.", Enchantable->CHAR_NAME(UNARTICLED)); Enchantable->EditEnchantment(1); } - return; + return true; } } + + return false; +} + +bool atavus::Favour(cfestring fsWhat, int iDebit) +{ + if(CallFavour(&FavourEnchantEquipment,FAVOUR_ENCHANT,fsWhat,iDebit,250))return true; + return false; +} + +void atavus::PrayGoodEffect() +{ + if(Favour(FAVOUR_ENCHANT))return; ADD_MESSAGE("You feel that %s is watching your actions closely.", GetName()); } @@ -716,13 +743,6 @@ void atavus::PrayBadEffect() PLAYER->CheckDeath(CONST_S("killed by Atavus's humour")); } -bool FavourFeed(god* G) -{ - ADD_MESSAGE("%s feeds you fruits and wild berries.", G->GetName()); - PLAYER->SetNP(SATIATED_LEVEL); - return true; -} - bool FavourCallRain(god* G) { beamdata Beam @@ -887,7 +907,7 @@ bool FavourSummonWolf(god* G) bool silva::Favour(cfestring fsWhat, int iDebit) { - if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; + if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; //satiated if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,fsWhat,iDebit,75))return true; if(CallFavour(&FavourEarthQuake,FAVOUR_EARTHQUAKE,fsWhat,iDebit,500))return true; if(CallFavour(&FavourSummonWolf,FAVOUR_SUMMONWOLF,fsWhat,iDebit,250))return true; @@ -1097,41 +1117,78 @@ void loricatus::PrayBadEffect() } } +int CalcDuration(god* G) +{ + if(dynamic_cast(G)) + return 200 * PLAYER->GetAttribute(WISDOM) + Max(G->GetRelation(), 0); + + if(dynamic_cast(G)) + return 300 * PLAYER->GetAttribute(WISDOM) + G->GetRelation() * 5; + + ABORT("invalid duration calc for god %d",G->GetName()); +} +bool FavourCureSlowness(god* G) +{ + ADD_MESSAGE("%s restores the swiftness of your movement.", G->GetName()); + PLAYER->DeActivateTemporaryState(SLOW); + return true; +} +bool FavourSpeedUp(god* G) +{ + int Duration = CalcDuration(G); + ADD_MESSAGE("%s gives you the talent for speed.", G->GetName()); + PLAYER->BeginTemporaryState(HASTE, Duration); + return true; +} +bool FavourInvisible(god* G) +{ + int Duration = CalcDuration(G); + ADD_MESSAGE("%s hides you from your enemies.", G->GetName()); + PLAYER->BeginTemporaryState(INVISIBLE, Duration); + return true; +} +bool FavourInfravision(god* G) +{ + int Duration = CalcDuration(G); + ADD_MESSAGE("%s orders darkness to hinder you no more.", G->GetName()); + PLAYER->BeginTemporaryState(INFRA_VISION, Duration); + return true; +} + bool cleptia::Favour(cfestring fsWhat, int iDebit) { + if(CallFavour(FavourCureSlowness,FAVOUR_CURESLOWNESS,fsWhat,iDebit,100))return true; + if(CallFavour(FavourSpeedUp,FAVOUR_SPEEDUP,fsWhat,iDebit,150))return true; + if(CallFavour(FavourInvisible,FAVOUR_INVISIBILITY,fsWhat,iDebit,250))return true; + if(CallFavour(FavourInfravision,FAVOUR_INFRAVISION,fsWhat,iDebit,150))return true; return false; } void cleptia::PrayGoodEffect() { - int Duration = 200 * PLAYER->GetAttribute(WISDOM) + Max(Relation, 0); PLAYER->RestoreStamina(); if(PLAYER->StateIsActivated(SLOW)) { - ADD_MESSAGE("%s restores the swiftness of your movement.", GetName()); - PLAYER->DeActivateTemporaryState(SLOW); + Favour(FAVOUR_CURESLOWNESS); return; } if(!PLAYER->StateIsActivated(HASTE)) { - ADD_MESSAGE("%s gives you the talent for speed.", GetName()); - PLAYER->BeginTemporaryState(HASTE, Duration); + Favour(FAVOUR_SPEEDUP); return; } if(!PLAYER->StateIsActivated(INVISIBLE)) { - ADD_MESSAGE("%s hides you from your enemies.", GetName()); - PLAYER->BeginTemporaryState(INVISIBLE, Duration); + Favour(FAVOUR_INVISIBILITY); return; } if(!PLAYER->StateIsActivated(INFRA_VISION)) { - ADD_MESSAGE("%s orders darkness to hinder you no more.", GetName()); - PLAYER->BeginTemporaryState(INFRA_VISION, Duration); + Favour(FAVOUR_INFRAVISION); return; } @@ -1149,8 +1206,24 @@ void cleptia::PrayBadEffect() PLAYER->BeginTemporaryState(SLOW, 250); } +bool FavourEtherealMov(god* G) +{ + ADD_MESSAGE("The air suddenly feels much colder. A terrible undead voice shreds " + "the silence: \"I aM PlEaSeD By tHy sQuIrMiNg, WoRm! WaLkEtH WiTh mE " + "ThRoUgH ThE ShAdOwS As oNe oF ThE DeAd!\""); + + if(!PLAYER->StateIsActivated(ETHEREAL_MOVING)) + PLAYER->BeginTemporaryState(ETHEREAL_MOVING, PLAYER->GetAttribute(WISDOM) * 300); + else + PLAYER->EditTemporaryStateCounter(ETHEREAL_MOVING, + PLAYER->GetTemporaryStateCounter(ETHEREAL_MOVING) + (PLAYER->GetAttribute(WISDOM) * 100)); + + return true; +} + bool mortifer::Favour(cfestring fsWhat, int iDebit) { + if(CallFavour(FavourEtherealMov,FAVOUR_ETHEREALMOV,fsWhat,iDebit,350))return true; return false; } @@ -1167,15 +1240,7 @@ void mortifer::PrayGoodEffect() } else { - ADD_MESSAGE("The air suddenly feels much colder. A terrible undead voice shreds " - "the silence: \"I aM PlEaSeD By tHy sQuIrMiNg, WoRm! WaLkEtH WiTh mE " - "ThRoUgH ThE ShAdOwS As oNe oF ThE DeAd!\""); - - if(!PLAYER->StateIsActivated(ETHEREAL_MOVING)) - PLAYER->BeginTemporaryState(ETHEREAL_MOVING, PLAYER->GetAttribute(WISDOM) * 300); - else - PLAYER->EditTemporaryStateCounter(ETHEREAL_MOVING, - PLAYER->GetTemporaryStateCounter(ETHEREAL_MOVING) + (PLAYER->GetAttribute(WISDOM) * 100)); + Favour(FAVOUR_ETHEREALMOV); } } @@ -1191,8 +1256,39 @@ void mortifer::PrayBadEffect() PLAYER->CheckDeath(CONST_S("obliterated by the unholy power of ") + GetName(), 0); } +bool FavourShopping(god* G) +{ + truth Success = false; + itemvector OKItems; + + for(stackiterator i = PLAYER->GetStack()->GetBottom(); i.HasItem(); ++i) + { + if(!i->HasBetterVersion()) + continue; + + OKItems.push_back(*i); + Success = true; + } + + item* NewVersion; + + for(int c = 0; !OKItems.empty() && c < 4; ++c) + { + item* ToBeDeleted = OKItems[RAND() % OKItems.size()]; + NewVersion = ToBeDeleted->BetterVersion(); + ADD_MESSAGE("%s manages to trade %s into %s.", G->GetName(), + ToBeDeleted->CHAR_NAME(DEFINITE), NewVersion->CHAR_NAME(INDEFINITE)); + PLAYER->GetStack()->AddItem(NewVersion); + ToBeDeleted->RemoveFromSlot(); + ToBeDeleted->SendToHell(); + OKItems.erase(std::find(OKItems.begin(), OKItems.end(), ToBeDeleted)); + } + + return Success; +} bool mellis::Favour(cfestring fsWhat, int iDebit) { + if(CallFavour(FavourShopping,FAVOUR_SHOPPING,fsWhat,iDebit,250))return true; return false; } @@ -1202,30 +1298,7 @@ void mellis::PrayGoodEffect() if(!RAND_2) { - itemvector OKItems; - - for(stackiterator i = PLAYER->GetStack()->GetBottom(); i.HasItem(); ++i) - { - if(!i->HasBetterVersion()) - continue; - - OKItems.push_back(*i); - Success = true; - } - - item* NewVersion; - - for(int c = 0; !OKItems.empty() && c < 4; ++c) - { - item* ToBeDeleted = OKItems[RAND() % OKItems.size()]; - NewVersion = ToBeDeleted->BetterVersion(); - ADD_MESSAGE("%s manages to trade %s into %s.", GetName(), - ToBeDeleted->CHAR_NAME(DEFINITE), NewVersion->CHAR_NAME(INDEFINITE)); - PLAYER->GetStack()->AddItem(NewVersion); - ToBeDeleted->RemoveFromSlot(); - ToBeDeleted->SendToHell(); - OKItems.erase(std::find(OKItems.begin(), OKItems.end(), ToBeDeleted)); - } + Success = Favour(FAVOUR_SHOPPING); } if((Success && !(RAND() % 5)) || (!Success && !(RAND() % 3))) @@ -1373,20 +1446,8 @@ void infuscor::PrayBadEffect() PLAYER->LoseConsciousness(1000 + RAND_N(1000)); } -bool nefas::Favour(cfestring fsWhat, int iDebit) +bool FavourConfusion(god* G) { - return false; -} - -void nefas::PrayGoodEffect() -{ - if(PLAYER->GetNP() < HUNGER_LEVEL) - { - ADD_MESSAGE("%s breast-feeds you.", GetName()); - PLAYER->SetNP(SATIATED_LEVEL); - return; - } - rect Rect; femath::CalculateEnvironmentRectangle(Rect, game::GetCurrentLevel()->GetBorder(), PLAYER->GetPos(), 10); truth AudiencePresent = false; @@ -1420,13 +1481,32 @@ void nefas::PrayGoodEffect() && Audience->CanBeConfused() && PLAYER->GetRelation(Audience) == HOSTILE) { if(Audience->CanBeSeenByPlayer()) - ADD_MESSAGE("%s confuses %s with her sweet lies.", GetName(), Audience->CHAR_NAME(DEFINITE)); + ADD_MESSAGE("%s confuses %s with her sweet lies.", G->GetName(), Audience->CHAR_NAME(DEFINITE)); Audience->BeginTemporaryState(CONFUSED, 30 * PLAYER->GetAttribute(WISDOM) + RAND() % 500); } } } + return true; +} +bool nefas::Favour(cfestring fsWhat, int iDebit) +{ + if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; //satiated + if(CallFavour(&FavourConfusion,FAVOUR_CONFUSE,fsWhat,iDebit,200))return true; //satiated + return false; +} + +void nefas::PrayGoodEffect() +{ + if(PLAYER->GetNP() < HUNGER_LEVEL) + { + Favour(FAVOUR_FEED); + return; + } + + Favour(FAVOUR_CONFUSE); + if((GetRelation() > 200) && RAND_N(5)) { int Chief = 3000/GetRelation(); @@ -1466,8 +1546,16 @@ void nefas::PrayBadEffect() PLAYER->CheckDeath(CONST_S("killed while enjoying the company of ") + GetName(), 0); } +bool FavourDiseaseImmunity(god* G) +{ + int Duration = CalcDuration(G); + PLAYER->BeginTemporaryState(DISEASE_IMMUNITY, Duration); + ADD_MESSAGE("%s chuckles in your mind: \"No need to fall apart, my dear.\"", G->GetName()); + return true; +} bool scabies::Favour(cfestring fsWhat, int iDebit) { + if(CallFavour(&FavourDiseaseImmunity,FAVOUR_DISEASEIMMUNITY,fsWhat,iDebit,350))return true; //satiated return false; } @@ -1486,7 +1574,7 @@ void scabies::PrayGoodEffect() ADD_MESSAGE("You feel a horrible disease spreading."); } - int Duration = 300 * PLAYER->GetAttribute(WISDOM) + Relation * 5; + int Duration = CalcDuration(this); if((PLAYER->GetNP() < HUNGER_LEVEL) && (!PLAYER->StateIsActivated(FASTING) || PLAYER->GetTemporaryStateCounter(FASTING) < Duration)) @@ -1502,8 +1590,7 @@ void scabies::PrayGoodEffect() // Scabies wants followers who can spread her word, not those who just lie on thr ground, missing limbs. if(PLAYER->StateIsActivated(LEPROSY) && !PLAYER->IsImmuneToLeprosy()) { - PLAYER->BeginTemporaryState(DISEASE_IMMUNITY, Duration); - ADD_MESSAGE("%s chuckles in your mind: \"No need to fall apart, my dear.\"", GetName()); + Favour(FAVOUR_DISEASEIMMUNITY); return; } From 7cfb2b7e2ba5c8ce60fea2d43c918389f43542c4 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 31 Mar 2020 19:59:55 -0300 Subject: [PATCH 065/235] WIP-GodFavours: being in holy ground (of one god), will provide access to that god's favours independent of alignment; --- Main/Source/command.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 6c7e78fee..c1f1176ca 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1170,6 +1170,7 @@ truth commandsystem::AskFavour(character* Char) if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0)bOk=true; if(pgod->GetBasicAlignment() == NEUTRAL && game::GetPlayerAlignment() == 0)bOk=true; if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; + if(c == Char->GetLSquareUnder()->GetDivineMaster())bOk=true; if(bOk || game::WizardModeIsReallyActive()){ std::vector v = pgod->GetKnownSpells(); From 889a4bfce34b65e81807f427e27a3faf6815d47d Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 31 Mar 2020 21:44:12 -0300 Subject: [PATCH 066/235] WIP-GodFavours: fixed - favours are now saved as an integer ID, no problems on editing the description; --- Main/Include/god.h | 13 ++- Main/Include/gods.h | 30 +++--- Main/Source/command.cpp | 16 ++-- Main/Source/god.cpp | 38 +++++++- Main/Source/gods.cpp | 206 ++++++++++++++++++++++------------------ 5 files changed, 183 insertions(+), 120 deletions(-) diff --git a/Main/Include/god.h b/Main/Include/god.h index fc2be6ff5..1c90ed645 100644 --- a/Main/Include/god.h +++ b/Main/Include/god.h @@ -84,9 +84,10 @@ class god virtual int GetSex() const = 0; void SignalRandomAltarGeneration(const std::vector&); virtual truth LikesVomit() const { return false; } - virtual bool Favour(cfestring fsWhat, int iDebit=0); - const std::vector GetKnownSpells() const { return knownSpells; } - bool CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat, int iDebit, int iDbtDefault); + virtual bool Favour(int iWhat, int iDebit=0); + static festring GetFavourName(int iID); + const std::vector GetKnownSpells() const { return knownSpellsID; } + bool CallFavour(CallFavourType call, int iCallFavour, int iWhat, int iDebit, int iDbtDefault); protected: virtual void PrayGoodEffect() = 0; virtual void PrayBadEffect() = 0; @@ -94,7 +95,11 @@ class god festring fsLastKnownRelation; long Timer; truth Known; - std::vector knownSpells; + + static std::vector> vFavID; + static void AddFavourID(int i,festring fs); + static void FavourInit(); + std::vector knownSpellsID; }; #ifdef __FILE_OF_STATIC_GOD_PROTOTYPE_DEFINITIONS__ diff --git a/Main/Include/gods.h b/Main/Include/gods.h index b632d5c2d..502e87670 100644 --- a/Main/Include/gods.h +++ b/Main/Include/gods.h @@ -33,7 +33,7 @@ GOD(valpurus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(legifer, god) @@ -49,7 +49,7 @@ GOD(legifer, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(atavus, god) @@ -66,7 +66,7 @@ GOD(atavus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(dulcis, god) @@ -82,7 +82,7 @@ GOD(dulcis, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(seges, god) @@ -101,7 +101,7 @@ GOD(seges, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(sophos, god) @@ -117,7 +117,7 @@ GOD(sophos, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(silva, god) @@ -133,7 +133,7 @@ GOD(silva, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(loricatus, god) @@ -149,7 +149,7 @@ GOD(loricatus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(mellis, god) @@ -165,7 +165,7 @@ GOD(mellis, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(cleptia, god) @@ -181,7 +181,7 @@ GOD(cleptia, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(nefas, god) @@ -197,7 +197,7 @@ GOD(nefas, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(scabies, god) @@ -217,7 +217,7 @@ GOD(scabies, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(infuscor, god) @@ -233,7 +233,7 @@ GOD(infuscor, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(cruentus, god) @@ -249,7 +249,7 @@ GOD(cruentus, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; GOD(mortifer, god) @@ -266,7 +266,7 @@ GOD(mortifer, god) protected: virtual void PrayGoodEffect(); virtual void PrayBadEffect(); - virtual bool Favour(cfestring fsWhat, int iDebit=0); + virtual bool Favour(int iWhat, int iDebit=0); }; #endif diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index c1f1176ca..462fdfe60 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1162,7 +1162,7 @@ truth commandsystem::AskFavour(character* Char) { felist felSpellList(CONST_S("To Whom you want to ask a ")+game::GetVerbalPlayerAlignment()+" favour?"); - std::vector> vGS; + std::vector> vGS; for(int c = 1; c <= GODS; ++c){ god* pgod = game::GetGod(c); @@ -1173,16 +1173,16 @@ truth commandsystem::AskFavour(character* Char) if(c == Char->GetLSquareUnder()->GetDivineMaster())bOk=true; if(bOk || game::WizardModeIsReallyActive()){ - std::vector v = pgod->GetKnownSpells(); - for(auto pfsSpell = v.begin(); pfsSpell != v.end(); pfsSpell++){ + std::vector v = pgod->GetKnownSpells(); + for(auto piSpell = v.begin(); piSpell != v.end(); ++piSpell){ felSpellList.AddEntry(CONST_S("")+ game::GetAlignment(pgod->GetAlignment())+" "+ - pgod->GetName()+" may grant you a \""+*pfsSpell+"\" favour.", + pgod->GetName()+" may grant you a \""+god::GetFavourName(*piSpell)+"\" favour.", bOk ? LIGHT_GRAY : RED); - std::pair GS; + std::pair GS; GS.first = pgod; - GS.second = *pfsSpell; + GS.second = *piSpell; vGS.push_back(GS); } } @@ -1202,7 +1202,7 @@ truth commandsystem::AskFavour(character* Char) return false; god* G = vGS[Select].first; - festring fsFavour = vGS[Select].second; + int iFavour = vGS[Select].second; int iDebit=FAVOURDEBIT_AUTO; int DivineMaster = Char->GetLSquareUnder()->GetDivineMaster(); if(DivineMaster){ @@ -1212,7 +1212,7 @@ truth commandsystem::AskFavour(character* Char) iDebit=FAVOURDEBIT_AUTODOUBLE; } - if(G->Favour(fsFavour,iDebit)){ + if(G->Favour(iFavour,iDebit)){ Char->EditAP(-1000); return true; } diff --git a/Main/Source/god.cpp b/Main/Source/god.cpp index a02557aef..a414128a6 100644 --- a/Main/Source/god.cpp +++ b/Main/Source/god.cpp @@ -553,7 +553,7 @@ void god::Save(outputfile& SaveFile) const SaveFile << static_cast(GetType()); SaveFile << Relation << Timer << Known << LastPray; SaveFile << fsLastKnownRelation; - SaveFile << knownSpells; + SaveFile << knownSpellsID; } void god::Load(inputfile& SaveFile) @@ -563,7 +563,7 @@ void god::Load(inputfile& SaveFile) SaveFile >> fsLastKnownRelation; } if(game::GetCurrentSavefileVersion()>=135){ - SaveFile >> knownSpells; + SaveFile >> knownSpellsID; } } @@ -574,3 +574,37 @@ void god::ApplyDivineTick() if(LastPray > -1 && LastPray < 336000) ++LastPray; } + +std::vector> god::vFavID; + +bool god::Favour(int iWhat, int iDebit) +{ + if(Relationfirst==iID) + return FI->second; + } + + ABORT("invalid favour ID %d",iID); + return ""; //dummy +} + +void god::AddFavourID(int i,festring fs) +{ +// std::pair IDname; +// IDname.first = i; +// IDname.second = fs; +// vFavID.push_back(IDname); + vFavID.push_back(std::make_pair(i,fs)); +} diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 9b147ca31..6188da627 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -130,46 +130,70 @@ col16 mortifer::GetColor() const { return CHAOS_BASIC_COLOR; } col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } /** - * these are also used as maching IDs, - * this means that changing these texts will change what is saved on the savegame file... + * changing the order of these enums will mess importing old savegames (but wont break them) + * prefer sorting on the initialization of the strings FavourInit() */ -#define FAVOUR_CALLRAIN "Make it Rain" -#define FAVOUR_CONFUSE "Cause Confusion amongst your enemies" -#define FAVOUR_CURELEPROSY "Cure Leprosy" -#define FAVOUR_CURELYCANTHROPY "Cure Lycanthropy" -#define FAVOUR_CUREMINDWORM "Cure Mindworm" -#define FAVOUR_CUREPOISON "Cure Poison" -#define FAVOUR_CURESLOWNESS "Cure Slowness" -#define FAVOUR_CURETAPEWORM "Cure Tapeworm" -#define FAVOUR_CUREWOUNDS "Cure Wounds" -#define FAVOUR_DISEASEIMMUNITY "Gain temporary Immunity to Diseases" -#define FAVOUR_EARTHQUAKE "Invoke the rage of an Earth Quake" -#define FAVOUR_ENCHANT "Enchant Equipment" -#define FAVOUR_ETHEREALMOV "Become Ethereal" -#define FAVOUR_EXTINGUISHFIRE "Put out these Flames" //TODO consider price vs FAVOUR_HEALBURNS -#define FAVOUR_FEED "Calms your Hunger" -#define FAVOUR_FIRESTORM "Fiery Firestorm" -#define FAVOUR_FIXEQUIPMENT "Fix one broken equipped item" -#define FAVOUR_HEALBURNS "Heals your burns" -#define FAVOUR_HOLYGREN "Paladin's Holy Grenade" -#define FAVOUR_INFRAVISION "See in the Darkness" -#define FAVOUR_INVIGORATE "Invigorate" -#define FAVOUR_INVISIBILITY "Become Invisible" -#define FAVOUR_SHOPPING "Black Friday" -#define FAVOUR_SPEEDUP "Make you Fast" -#define FAVOUR_STOPFIRE "Unburn one Equipment" -#define FAVOUR_SUMMONWOLF "Summon Wolf friend(s)" -#define FAVOUR_TAME "Tame this Monster" -#define FAVOUR_TELEPORT "Teleport" - -bool god::Favour(cfestring fsWhat, int iDebit) -{ - if(Relation& ks,festring fsNew) +void AddKnownSpell(std::vector& ks,int iNew) { for(auto pfsSpell = ks.begin(); pfsSpell != ks.end(); pfsSpell++){ - if(*pfsSpell == fsNew)return; + if(*pfsSpell == iNew)return; } - ks.push_back(fsNew); + ks.push_back(iNew); } bool FavourTeleport(god* G) @@ -209,17 +233,17 @@ bool FavourTeleport(god* G) return true; } -bool god::CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat, int iDebit, int iDbtDefault) +bool god::CallFavour(CallFavourType call, int iCallFavour, int iWhat, int iDebit, int iDbtDefault) { - if(fsCallFavour!=fsWhat)return false; + if(iCallFavour!=iWhat)return false; if(iDebit==0) //came thru normal praying - AddKnownSpell(knownSpells,fsCallFavour); + AddKnownSpell(knownSpellsID,iCallFavour); iDebit=CalcDebit(this,iDebit,iDbtDefault); if(iDebit>0) - if(!god::Favour(fsWhat,iDebit)) + if(!god::Favour(iWhat,iDebit)) return false; if((*call)(this)){ @@ -237,9 +261,9 @@ bool god::CallFavour(CallFavourType call, festring fsCallFavour, festring fsWhat * @param iDebit if -1 will be automatic * @return */ -bool sophos::Favour(cfestring fsWhat, int iDebit) +bool sophos::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourTeleport,FAVOUR_TELEPORT,fsWhat,iDebit,100))return true; + if(CallFavour(&FavourTeleport,FAVOUR_TELEPORT,iWhat,iDebit,100))return true; return false; } @@ -304,9 +328,9 @@ bool FavourHolyGrenade(god* G) return true; } -bool valpurus::Favour(cfestring fsWhat, int iDebit) +bool valpurus::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourHolyGrenade,FAVOUR_HOLYGREN,fsWhat,iDebit,300))return true; + if(CallFavour(&FavourHolyGrenade,FAVOUR_HOLYGREN,iWhat,iDebit,300))return true; return false; } @@ -352,9 +376,9 @@ bool FavourFirestorm(god* G) return true; } -bool legifer::Favour(cfestring fsWhat, int iDebit) +bool legifer::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourFirestorm,FAVOUR_FIRESTORM,fsWhat,iDebit,200))return true; + if(CallFavour(&FavourFirestorm,FAVOUR_FIRESTORM,iWhat,iDebit,200))return true; return false; } @@ -424,10 +448,10 @@ bool FavourTame(god* G) return HasHelped; } -bool dulcis::Favour(cfestring fsWhat, int iDebit) +bool dulcis::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourExtinguishFire,FAVOUR_EXTINGUISHFIRE,fsWhat,iDebit,50))return true; - if(CallFavour(&FavourTame,FAVOUR_TAME,fsWhat,iDebit,250))return true; + if(CallFavour(&FavourExtinguishFire,FAVOUR_EXTINGUISHFIRE,iWhat,iDebit,50))return true; + if(CallFavour(&FavourTame,FAVOUR_TAME,iWhat,iDebit,250))return true; return false; } @@ -558,18 +582,18 @@ bool FavourFeed(god* G) return true; } -bool seges::Favour(cfestring fsWhat, int iDebit) +bool seges::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourCureWounds,FAVOUR_CUREWOUNDS,fsWhat,iDebit,150))return true; - if(CallFavour(&FavourCurePoison,FAVOUR_CUREPOISON,fsWhat,iDebit,200))return true; - if(CallFavour(&FavourCureLeprosy,FAVOUR_CURELEPROSY,fsWhat,iDebit,250))return true; - if(CallFavour(&FavourCureLycanthropy,FAVOUR_CURELYCANTHROPY,fsWhat,iDebit,300))return true; + if(CallFavour(&FavourCureWounds,FAVOUR_CUREWOUNDS,iWhat,iDebit,150))return true; + if(CallFavour(&FavourCurePoison,FAVOUR_CUREPOISON,iWhat,iDebit,200))return true; + if(CallFavour(&FavourCureLeprosy,FAVOUR_CURELEPROSY,iWhat,iDebit,250))return true; + if(CallFavour(&FavourCureLycanthropy,FAVOUR_CURELYCANTHROPY,iWhat,iDebit,300))return true; //TODO is vampirism bad in anyway? - if(CallFavour(&FavourCureTapeworm,FAVOUR_CURETAPEWORM,fsWhat,iDebit,250))return true; - if(CallFavour(&FavourCureMindworm,FAVOUR_CUREMINDWORM,fsWhat,iDebit,500))return true; - if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,300))return true; //bloats - if(CallFavour(&FavourHealBurns,FAVOUR_HEALBURNS,fsWhat,iDebit,50))return true; - if(CallFavour(&FavourInvigorate,FAVOUR_INVIGORATE,fsWhat,iDebit,250))return true; + if(CallFavour(&FavourCureTapeworm,FAVOUR_CURETAPEWORM,iWhat,iDebit,250))return true; + if(CallFavour(&FavourCureMindworm,FAVOUR_CUREMINDWORM,iWhat,iDebit,500))return true; + if(CallFavour(&FavourFeed,FAVOUR_FEED,iWhat,iDebit,300))return true; //bloats + if(CallFavour(&FavourHealBurns,FAVOUR_HEALBURNS,iWhat,iDebit,50))return true; + if(CallFavour(&FavourInvigorate,FAVOUR_INVIGORATE,iWhat,iDebit,250))return true; return false; } @@ -696,9 +720,9 @@ bool FavourEnchantEquipment(god* G) return false; } -bool atavus::Favour(cfestring fsWhat, int iDebit) +bool atavus::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourEnchantEquipment,FAVOUR_ENCHANT,fsWhat,iDebit,250))return true; + if(CallFavour(&FavourEnchantEquipment,FAVOUR_ENCHANT,iWhat,iDebit,250))return true; return false; } @@ -905,12 +929,12 @@ bool FavourSummonWolf(god* G) return true; } -bool silva::Favour(cfestring fsWhat, int iDebit) +bool silva::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; //satiated - if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,fsWhat,iDebit,75))return true; - if(CallFavour(&FavourEarthQuake,FAVOUR_EARTHQUAKE,fsWhat,iDebit,500))return true; - if(CallFavour(&FavourSummonWolf,FAVOUR_SUMMONWOLF,fsWhat,iDebit,250))return true; + if(CallFavour(&FavourFeed,FAVOUR_FEED,iWhat,iDebit,200))return true; //satiated + if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,iWhat,iDebit,75))return true; + if(CallFavour(&FavourEarthQuake,FAVOUR_EARTHQUAKE,iWhat,iDebit,500))return true; + if(CallFavour(&FavourSummonWolf,FAVOUR_SUMMONWOLF,iWhat,iDebit,250))return true; return false; } @@ -989,10 +1013,10 @@ bool FavourStopFire(god* G) return true; } -bool loricatus::Favour(cfestring fsWhat, int iDebit) +bool loricatus::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourFixEquipment,FAVOUR_FIXEQUIPMENT,fsWhat,iDebit,250))return true; - if(CallFavour(&FavourStopFire,FAVOUR_STOPFIRE,fsWhat,iDebit,50))return true; + if(CallFavour(&FavourFixEquipment,FAVOUR_FIXEQUIPMENT,iWhat,iDebit,250))return true; + if(CallFavour(&FavourStopFire,FAVOUR_STOPFIRE,iWhat,iDebit,50))return true; return false; } @@ -1155,12 +1179,12 @@ bool FavourInfravision(god* G) return true; } -bool cleptia::Favour(cfestring fsWhat, int iDebit) +bool cleptia::Favour(int iWhat, int iDebit) { - if(CallFavour(FavourCureSlowness,FAVOUR_CURESLOWNESS,fsWhat,iDebit,100))return true; - if(CallFavour(FavourSpeedUp,FAVOUR_SPEEDUP,fsWhat,iDebit,150))return true; - if(CallFavour(FavourInvisible,FAVOUR_INVISIBILITY,fsWhat,iDebit,250))return true; - if(CallFavour(FavourInfravision,FAVOUR_INFRAVISION,fsWhat,iDebit,150))return true; + if(CallFavour(FavourCureSlowness,FAVOUR_CURESLOWNESS,iWhat,iDebit,100))return true; + if(CallFavour(FavourSpeedUp,FAVOUR_SPEEDUP,iWhat,iDebit,150))return true; + if(CallFavour(FavourInvisible,FAVOUR_INVISIBILITY,iWhat,iDebit,250))return true; + if(CallFavour(FavourInfravision,FAVOUR_INFRAVISION,iWhat,iDebit,150))return true; return false; } @@ -1221,9 +1245,9 @@ bool FavourEtherealMov(god* G) return true; } -bool mortifer::Favour(cfestring fsWhat, int iDebit) +bool mortifer::Favour(int iWhat, int iDebit) { - if(CallFavour(FavourEtherealMov,FAVOUR_ETHEREALMOV,fsWhat,iDebit,350))return true; + if(CallFavour(FavourEtherealMov,FAVOUR_ETHEREALMOV,iWhat,iDebit,350))return true; return false; } @@ -1286,9 +1310,9 @@ bool FavourShopping(god* G) return Success; } -bool mellis::Favour(cfestring fsWhat, int iDebit) +bool mellis::Favour(int iWhat, int iDebit) { - if(CallFavour(FavourShopping,FAVOUR_SHOPPING,fsWhat,iDebit,250))return true; + if(CallFavour(FavourShopping,FAVOUR_SHOPPING,iWhat,iDebit,250))return true; return false; } @@ -1490,10 +1514,10 @@ bool FavourConfusion(god* G) return true; } -bool nefas::Favour(cfestring fsWhat, int iDebit) +bool nefas::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourFeed,FAVOUR_FEED,fsWhat,iDebit,200))return true; //satiated - if(CallFavour(&FavourConfusion,FAVOUR_CONFUSE,fsWhat,iDebit,200))return true; //satiated + if(CallFavour(&FavourFeed,FAVOUR_FEED,iWhat,iDebit,200))return true; //satiated + if(CallFavour(&FavourConfusion,FAVOUR_CONFUSE,iWhat,iDebit,200))return true; //satiated return false; } @@ -1553,9 +1577,9 @@ bool FavourDiseaseImmunity(god* G) ADD_MESSAGE("%s chuckles in your mind: \"No need to fall apart, my dear.\"", G->GetName()); return true; } -bool scabies::Favour(cfestring fsWhat, int iDebit) +bool scabies::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourDiseaseImmunity,FAVOUR_DISEASEIMMUNITY,fsWhat,iDebit,350))return true; //satiated + if(CallFavour(&FavourDiseaseImmunity,FAVOUR_DISEASEIMMUNITY,iWhat,iDebit,350))return true; //satiated return false; } @@ -1661,7 +1685,7 @@ void scabies::PrayBadEffect() } } -bool infuscor::Favour(cfestring fsWhat, int iDebit) +bool infuscor::Favour(int iWhat, int iDebit) { return false; } @@ -1750,7 +1774,7 @@ void infuscor::PrayGoodEffect() return; } -bool cruentus::Favour(cfestring fsWhat, int iDebit) +bool cruentus::Favour(int iWhat, int iDebit) { return false; } From 0baef44777d36838709bb9201171c6c92416b57d Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 1 Apr 2020 18:01:02 -0300 Subject: [PATCH 067/235] crafting: added basic/simple tailoring. Cutting webs drops a bit of spider silk now. Tools gets a bit damaged near forge when crafting. --- Main/Include/craft.h | 1 + Main/Source/actions.cpp | 2 + Main/Source/cmdcraft.cpp | 237 +++++++++++++++++++++++++++++---------- 3 files changed, 178 insertions(+), 62 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index 841c7e77d..3d8cdc899 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -214,6 +214,7 @@ class recipedata { v2 v2WorkbenchLocation; int iRemainingTurnsToFinish; bool bGradativeCraftOverride; + bool bTailoringMode; public: recipedata(humanoid* H=NULL,uint sel=FELIST_ERROR_BIT); diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index fa38b0a52..fdfbd52fa 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -358,6 +358,8 @@ void craft::Handle() if(rpdBkp.xplodStr>9)rpdBkp.xplodStr=9; // to limit the "fire sparks" size to one square game::GetCurrentLevel()->Explosion( ActorLocal, CONST_S("killed by the forge heat"), rpdBkp.v2XplodAt, rpdBkp.xplodStr, false, false); + if(rpdBkp.GetTool()) rpdBkp.GetTool()->ReceiveDamage( rpdBkp.rc.H(),rpdBkp.xplodStr,FIRE); + if(rpdBkp.GetTool2())rpdBkp.GetTool2()->ReceiveDamage(rpdBkp.rc.H(),rpdBkp.xplodStr,FIRE); ADD_MESSAGE("Forging sparks explode lightly."); //this will let sfx play TODO better message? the idea is to make forging a bit hazardous, } diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index c5837b152..9cba742ae 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -304,6 +304,7 @@ void recipedata::Save(outputfile& SaveFile) const << v2WorkbenchLocation << iRemainingTurnsToFinish << bGradativeCraftOverride + << bTailoringMode ; } @@ -361,6 +362,9 @@ void recipedata::Load(inputfile& SaveFile) >> bGradativeCraftOverride ; + + if(game::GetCurrentSavefileVersion() >= 135) + SaveFile >> bTailoringMode; // if(otSpawnType!=CTT_NONE) // SaveFile >> otSpawn; @@ -519,6 +523,7 @@ recipedata::recipedata(humanoid* H,uint sel) : rc(H,sel) v2WorkbenchLocation=v2(0,0); iRemainingTurnsToFinish=iBaseTurnsToFinish; bGradativeCraftOverride=false; + bTailoringMode=false; } int craftcore::CurrentDungeonLevelID(){ @@ -615,7 +620,7 @@ void recipedata::ClearRefs(){ rc.ClearRefs(); } -struct ci{ +struct ci{ //create item info/helper/data/config/param bool bMultSelect = true; int iReqCfg=0; @@ -637,6 +642,8 @@ struct ci{ int iMinMainMaterStr=0; float fUsablePercVol=1.0; + bool bMustBeTailorable=false; + bool bMixRemainingLump=true; }; struct recipe{ festring action; @@ -767,6 +774,44 @@ struct recipe{ } return itTool; } + static item* findTailoringTool(recipedata& rpd,item* itToWorkOn){ + int iCarvingStr=0; + + material* matM = itToWorkOn->GetMainMaterial(); + material* matS = itToWorkOn->GetSecondaryMaterial(); + if(!craftcore::IsMeltable(matM)) + iCarvingStr=matM->GetStrengthValue(); + if(matS!=NULL && !craftcore::IsMeltable(matS)) + iCarvingStr=Max(iCarvingStr,matS->GetStrengthValue()); + + int iMinCarvingStr = iCarvingStr/2; + + // any blanded thing bug preferably a dagger + int iMult=1; + item* itTool = FindTool(rpd, DAGGER, 0, iMinCarvingStr); //carving: tool cant be too much weaker + itTool = findCarvingToolSpecific(rpd,itTool,iMinCarvingStr,DAGGER,iMult,0); //TODO should be SCISSORS + if(itTool!=NULL){ + if(iCarvingStr>1){ + int itStr=itTool->GetMainMaterial()->GetStrengthValue(); + if(itStrGetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED)) + continue; if(vi[i]->GetStrengthValue() < CI.iMinMainMaterStr) continue; @@ -1052,7 +1100,8 @@ struct recipe{ item* lumpR = craftcore::PrepareRemains(rpd,matM,CIT_NONE,lRemainingVol); //lumpR->GetMainMaterial()->SetVolume(lRemainingVol); - lumpMix(vi,lumpR,rpd.bSpendCurrentTurn); + if(CI.bMixRemainingLump) + lumpMix(vi,lumpR,rpd.bSpendCurrentTurn); material* matS = ToUse[i]->GetSecondaryMaterial(); if(matS!=NULL && matS->GetVolume()>0) @@ -1336,6 +1385,8 @@ struct srpCutWeb : public recipe{ if(bSuccess){ rpd.bAlreadyExplained=true; + material* matSSilk=material::MakeMaterial(SPIDER_SILK); + craftcore::FinishSpawning(rpd, craftcore::PrepareRemains(rpd,matSSilk,CIT_LUMP,clock()%6+3)); }else{ bool bGetStuckOnTheWeb=false; bool bLoseWeapon=false; //TODO if has no weapon, lose one glove instead! @@ -2315,49 +2366,72 @@ struct srpForgeItem : public recipe{ } bool bIsItemContainer = dynamic_cast(itSpawn) !=NULL; - - bool bM = false; - if(!bM){ - ci CI = CIM; - CI.iReqCfg=INGOT; //meltables are 100% usable - festring fsM("as MAIN material (ingots 100%)"); - bM = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); - } - if(!bM){ - ci CI = CIM; - CI.bFirstItemMustHaveFullVolumeRequired=true; //carving: only one ingredient piece per material allowed, so it must have required volume - CI.bMultSelect=false; - CI.fUsablePercVol=0.75; - festring fsM("as MAIN material (stones "); //roundy shape loses material - fsM<<(int)(CI.fUsablePercVol*100)<<"%)"; - bM = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); - } - {//stick block - festring fsM("as MAIN material (sticks/bones "); - float fPerc=1.0; - if(!bIsItemContainer) - fPerc=0.5; + bool bIsWeapon = itSpawn->IsWeapon(rpd.rc.H()); + + bool bMainMatOk = false; + bool bMustTailor = bIsWeapon && dynamic_cast(itSpawn); + bool bCanTailor = dynamic_cast(itSpawn) && !itSpawn->IsHelmet(rpd.rc.H()); + if(bMustTailor || bCanTailor){ // tailoring + festring fsM("as MAIN material (cloth "); // only main can be cloth + float fPerc = 0.85; fsM<<(int)(fPerc*100)<<"%)"; /** - * stick shape can't provide enough to the required dimensions (this is a xtremely wild simplification btw :)) - * so, this will require twice as much sticks if not a container, to be crafted ex.: 35/0.5=70 + * cloth will be cut and sewed */ - if(!bM){ + if(!bMainMatOk){ ci CI = CIM; CI.fUsablePercVol=fPerc; - CI.bFirstItemMustHaveFullVolumeRequired=true; //carving: only one ingredient piece per material allowed, so it must have required volume - bM = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); + CI.bMustBeTailorable = rpd.bTailoringMode = true; + CI.bMixRemainingLump = false; + bMainMatOk = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); //TODO instead of should be a new item with new graphics called + } + } + if(!bMustTailor){ + // crafting with stones or ingots + if(!bMainMatOk){ + ci CI = CIM; + CI.iReqCfg=INGOT; //meltables are 100% usable + festring fsM("as MAIN material (ingots 100%)"); + bMainMatOk = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); } - if(!bM){ + + if(!bMainMatOk){ ci CI = CIM; - CI.fUsablePercVol=fPerc; - if(!bIsItemContainer) + CI.bFirstItemMustHaveFullVolumeRequired=true; //carving: only one ingredient piece per material allowed, so it must have required volume + CI.bMultSelect=false; + CI.fUsablePercVol=0.75; + festring fsM("as MAIN material (stones "); //roundy shape loses material + fsM<<(int)(CI.fUsablePercVol*100)<<"%)"; + bMainMatOk = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); + } + + // crafting with sticks and bones + if(!bMainMatOk){ + festring fsM("as MAIN material (sticks/bones "); + float fPerc = bIsItemContainer ? 1.0 : 0.5; + fsM<<(int)(fPerc*100)<<"%)"; + /** + * stick shape can't provide enough to the required dimensions (this is a xtremely wild simplification btw :)) + * so, this will require twice as much sticks if not a container, to be crafted ex.: 35/0.5=70 + */ + if(!bMainMatOk){ + ci CI = CIM; + CI.fUsablePercVol=fPerc; CI.bFirstItemMustHaveFullVolumeRequired=true; //carving: only one ingredient piece per material allowed, so it must have required volume - CI.bFirstMainMaterIsFilter=false; //wooden things are cheap (resistances, strength etc), so getting mixed into weakest will cause no trouble like losing good meltables (as they arent even), so let user chose any wood - bM = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); + bMainMatOk = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); + } + if(!bMainMatOk){ + ci CI = CIM; + CI.fUsablePercVol=fPerc; + if(!bIsItemContainer) + CI.bFirstItemMustHaveFullVolumeRequired=true; //carving: only one ingredient piece per material allowed, so it must have required volume + CI.bFirstMainMaterIsFilter=false; //wooden things are cheap (resistances, strength etc), so getting mixed into weakest will cause no trouble like losing good meltables (as they arent even), so let user chose any wood + bMainMatOk = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); + } } } - if(!bM){ + + if(!bMainMatOk){ ADD_MESSAGE("You don't have the materials to craft a %s.", Default.CStr()); rpd.bAlreadyExplained=true; craftcore::SendToHellSafely(itSpawn); @@ -2374,38 +2448,37 @@ struct srpForgeItem : public recipe{ * so preventing it would still not fix how 'metal can' works... */ - bool bIsWeapon = itSpawn->IsWeapon(rpd.rc.H()); bool bReqS = bIsWeapon; bool bAllowS = true; // if(mc)bAllowS=false; if(bContainerEmptied)bAllowS=false; if(lVolS==0)bAllowS=false; if(bAllowS){DBGLN; - bool bS = false; + bool bSecondMatOk = false; festring fsS("as Secondary material");DBGLN; - if(!bS){ + if(!bSecondMatOk){ ci CI=CIS; CI.iReqCfg=INGOT; - bS = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); + bSecondMatOk = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); } - if(!bS){ + if(!bSecondMatOk){ ci CI=CIS; //carving: only one stone per material allowed, so it must have required volume CI.bFirstItemMustHaveFullVolumeRequired=true; CI.bMultSelect=false; - bS = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); + bSecondMatOk = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); } if(bIsWeapon){DBGLN; //this is mainly to prevent "material containers" being filled with non-sense materials like a bottle fille with wood... TODO powders one day would be ok - if(!bS){ + if(!bSecondMatOk){ ci CI=CIS; - bS = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); + bSecondMatOk = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); } - if(!bS){ + if(!bSecondMatOk){ ci CI=CIS; - bS = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); + bSecondMatOk = choseIngredients(fsS,lVolS, rpd, iCfgS, CI); } } - if(!bS){ + if(!bSecondMatOk){ ADD_MESSAGE("You will craft it later..."); rpd.bAlreadyExplained=true; craftcore::SendToHellSafely(itSpawn); @@ -2455,6 +2528,13 @@ struct srpForgeItem : public recipe{ return false; } } + + if(rpd.bTailoringMode){ + if(!recipe::findOLT(rpd,WORK_BENCH)){ //must be near it //TODO should be a new bench called TAILORING_BENCH with new graphics one day... + craftcore::SendToHellSafely(itSpawn); + return false; + } + } ////////////////////////////////////////////////////////////////////////////////////////////////// applyDifficulty(rpd,itSpawn); @@ -2523,19 +2603,52 @@ struct srpForgeItem : public recipe{ } /// TOOLS /////////////////////////////////////////////////////////////////////////////////////////////// - // HAMMER like for meltables (still hot and easy to work, any hammer will do) TODO damage the hammer thru the heat of the forge + // HAMMER like for meltables (still hot and easy to work, any hammer will do) + //TODO glass should require proper tools (don't know what but sure not a hammer) bool bMissingTools=false; - if(rpd.bMeltable){ //TODO glass should require proper tools (don't know what but sure not a hammer) - rpd.itTool = FindBluntTool(rpd); + if(rpd.bTailoringMode){ + rpd.itTool = findTailoringTool(rpd,itSpawn); // only main material can be tailored if(rpd.itTool==NULL) bMissingTools=true; - } - - if(!bMissingTools){ - if(!bMeltableM || !bMeltableS){ - if(!findCarvingTool(rpd,itSpawn)) + + if(!bMissingTools){ + if(bMeltableS){ + rpd.itTool2 = FindBluntTool(rpd); + if(rpd.itTool2==NULL) + bMissingTools=true; + }else{ + rpd.itTool2 = findCarvingTool(rpd,itSpawn); + if(rpd.itTool2==NULL) + bMissingTools=true; + } + } + }else{ + if(bMeltableM){ + rpd.itTool = FindBluntTool(rpd); + if(rpd.itTool==NULL) + bMissingTools=true; + }else{ + rpd.itTool = findCarvingTool(rpd,itSpawn); + if(rpd.itTool==NULL) bMissingTools=true; } + + if(!bMissingTools){ + if(bMeltableS){ + rpd.itTool2 = FindBluntTool(rpd); + if(rpd.itTool2==NULL) + bMissingTools=true; + }else{ + rpd.itTool2 = findCarvingTool(rpd,itSpawn); + if(rpd.itTool2==NULL) + bMissingTools=true; + } + } + } + + if(!bMissingTools){ + if(rpd.itTool2==rpd.itTool) + rpd.itTool2=NULL; } DBG1(rpd.iBaseTurnsToFinish); @@ -3044,7 +3157,7 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif rpd.v2PlayerCraftingAt = Char->GetPos(); if(rpd.itTool!=NULL && rpd.itTool2!=NULL) - if(rpd.itTool==rpd.itTool2) + if(rpd.itTool==rpd.itTool2) //keep this check to fix any code bofore this. ABORT("both tools are the same item %lu:%s %lu:%s",rpd.itTool->GetID(),rpd.itTool->GetName(INDEFINITE).CStr(),rpd.itTool2->GetID(),rpd.itTool2->GetName(INDEFINITE).CStr()); if(rpd.itTool !=NULL)rpd.itToolID =rpd.itTool ->GetID(); if(rpd.itTool2!=NULL)rpd.itTool2ID=rpd.itTool2->GetID(); @@ -3259,11 +3372,11 @@ void crafthandle::CraftWorkTurn(recipedata& rpd){ DBG1(rpd.iRemainingTurnsToFini // keep this preference order! lsquare* lsqrHF=NULL; if(!lsqrHF)lsqrHF=rpd.lsqrPlaceAt; - if(!lsqrHF)lsqrHF=rpd.v2AnvilLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2AnvilLocation); + if(!lsqrHF)lsqrHF=rpd.v2AnvilLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2AnvilLocation); if(!lsqrHF)lsqrHF=rpd.v2WorkbenchLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2WorkbenchLocation); - if(!lsqrHF)lsqrHF=rpd.v2PlaceAt.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2PlaceAt); - if(!lsqrHF)lsqrHF=rpd.v2ForgeLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2ForgeLocation); - if(!lsqrHF)lsqrHF=rpd.v2XplodAt.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2XplodAt); + if(!lsqrHF)lsqrHF=rpd.v2PlaceAt.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2PlaceAt); + if(!lsqrHF)lsqrHF=rpd.v2ForgeLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2ForgeLocation); + if(!lsqrHF)lsqrHF=rpd.v2XplodAt.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2XplodAt); if(lsqrHF){ hiteffectSetup* pHitEff=new hiteffectSetup(); @@ -3272,10 +3385,10 @@ void crafthandle::CraftWorkTurn(recipedata& rpd){ DBG1(rpd.iRemainingTurnsToFini pHitEff->HitAtSquare=lsqrHF; item* itHF=NULL; //keep the below order - if(!itHF)itHF = rpd.itTool ? rpd.itTool : NULL; + if(!itHF)itHF = rpd.itTool ? rpd.itTool : NULL; if(!itHF)itHF = rpd.itTool2 ? rpd.itTool2 : NULL; if(!itHF)itHF = rpd.rc.H()->GetRightArm() ? rpd.rc.H()->GetRightArm() : NULL; - if(!itHF)itHF = rpd.rc.H()->GetLeftArm() ? rpd.rc.H()->GetLeftArm() : NULL; + if(!itHF)itHF = rpd.rc.H()->GetLeftArm() ? rpd.rc.H()->GetLeftArm() : NULL; if(itHF){ pHitEff->lItemEffectReferenceID = itHF->GetID(); lsqrHF->AddHitEffect(*pHitEff); From bebc41159e134c902f94829907af4f214f48335a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 1 Apr 2020 18:41:22 -0300 Subject: [PATCH 068/235] crafting: added a bit of spider silk as sewing thread requirement; --- Main/Source/cmdcraft.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 9cba742ae..6b4781903 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2488,7 +2488,23 @@ struct srpForgeItem : public recipe{ if(bReqS && !bAllowS) ABORT("item reqs secondary mat but doesnt allow it??? %s",itSpawn->GetName(DEFINITE).CStr()); - + + if(rpd.bTailoringMode){ + long lVolSewing = lVolM/100; + if(lVolSewing==0)lVolSewing=1; + int iSCfg=-1; + ci CISW; + CISW.bMainMaterRemainsBecomeLump=true; + CISW.bMixRemainingLump = false; + CISW.iReqMatCfgMain=SPIDER_SILK; + if(!choseIngredients(cfestring("as sewing material"),lVolSewing,rpd,iSCfg,CISW)){ //TODO instead of should be with new graphics + ADD_MESSAGE("You don't have enough sewing thread..."); + rpd.bAlreadyExplained=true; + craftcore::SendToHellSafely(itSpawn); + return false; + } + } + rpd.bHasAllIngredients=true; rpd.bCanBeBroken = itSpawn->CanBeBroken(); From ff0a7473d6a779e3f34e69df9fc38adb3409f09d Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 1 Apr 2020 19:02:16 -0300 Subject: [PATCH 069/235] crafting: cloaks now have to be tailored (no more wooden cloaks :P); --- Main/Source/cmdcraft.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 6b4781903..512a39ab7 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2369,7 +2369,7 @@ struct srpForgeItem : public recipe{ bool bIsWeapon = itSpawn->IsWeapon(rpd.rc.H()); bool bMainMatOk = false; - bool bMustTailor = bIsWeapon && dynamic_cast(itSpawn); + bool bMustTailor = (bIsWeapon && dynamic_cast(itSpawn)) || dynamic_cast(itSpawn); bool bCanTailor = dynamic_cast(itSpawn) && !itSpawn->IsHelmet(rpd.rc.H()); if(bMustTailor || bCanTailor){ // tailoring festring fsM("as MAIN material (cloth "); // only main can be cloth From 37199792b328df5a9e99df23d2d3ccc13313a400 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 1 Apr 2020 21:44:59 -0300 Subject: [PATCH 070/235] DropBeforeOffering new option; favours: all will be shown now but just aligned ones will be selectable; --- Main/Include/iconf.h | 2 ++ Main/Source/command.cpp | 27 +++++++++++++++------------ Main/Source/iconf.cpp | 5 +++++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index e87ea12e4..5d41dc63e 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -25,6 +25,7 @@ class ivanconfig static cfestring& GetSelectedBkgColor() { return SelectedBkgColor.Value; } static cfestring& GetAutoPickUpMatching() { return AutoPickUpMatching.Value; } static truth IsAllWeightIsRelevant() { return AllWeightIsRelevant.Value; } + static truth IsDropBeforeOffering() { return DropBeforeOffering.Value; } static long GetAutoSaveInterval() { return AutoSaveInterval.Value; } static long GetContrast() { return Contrast.Value; } static long GetHitIndicator() { return HitIndicator.Value; } @@ -202,6 +203,7 @@ class ivanconfig static truthoption TransparentMapLM; static truthoption WaitNeutralsMoveAway; static truthoption AllWeightIsRelevant; + static truthoption DropBeforeOffering; static truthoption ShowVolume; static truthoption EnhancedLights; diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 462fdfe60..2692632cf 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1172,18 +1172,20 @@ truth commandsystem::AskFavour(character* Char) if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; if(c == Char->GetLSquareUnder()->GetDivineMaster())bOk=true; - if(bOk || game::WizardModeIsReallyActive()){ - std::vector v = pgod->GetKnownSpells(); - for(auto piSpell = v.begin(); piSpell != v.end(); ++piSpell){ - felSpellList.AddEntry(CONST_S("")+ - game::GetAlignment(pgod->GetAlignment())+" "+ - pgod->GetName()+" may grant you a \""+god::GetFavourName(*piSpell)+"\" favour.", - bOk ? LIGHT_GRAY : RED); - - std::pair GS; - GS.first = pgod; - GS.second = *piSpell; - vGS.push_back(GS); + std::vector v = pgod->GetKnownSpells(); + for(auto piSpell = v.begin(); piSpell != v.end(); ++piSpell){ + festring fs = CONST_S("")+ + game::GetAlignment(pgod->GetAlignment())+" "+ + pgod->GetName()+" may grant you a \""+god::GetFavourName(*piSpell)+"\" favour."; + col16 col = bOk ? LIGHT_GRAY : DARK_GRAY; + if(!bOk && game::WizardModeIsReallyActive())col=RED; + felSpellList.AddEntry(fs, col, 0, NO_IMAGE, bOk || game::WizardModeIsReallyActive()); + + if(bOk || game::WizardModeIsReallyActive()){ +// std::pair GS; +// GS.first = pgod; +// GS.second = *piSpell; + vGS.push_back(std::make_pair(pgod,*piSpell)); } } } @@ -1387,6 +1389,7 @@ truth commandsystem::Offer(character* Char) if(Item) { + if(ivanconfig::IsDropBeforeOffering())Item->MoveTo(Char->GetLSquareUnder()->GetStack()); //drops before offering so non accepted will unclutter player inventory if(game::GetGod(Square->GetDivineMaster())->ReceiveOffer(Item)) { Item->RemoveFromSlot(); diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 2603b46b7..46e860d87 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -164,6 +164,10 @@ truthoption ivanconfig::AllWeightIsRelevant("AllWeightIsRelevant", "Only pile items with equal weight on lists", //clutter are useful now for crafting so their weight matters... "", false); +truthoption ivanconfig::DropBeforeOffering("DropBeforeOffering", + "Drop the item on altar before offering it.", + "Beware it may be owned floor!", + false); truthoption ivanconfig::ShowVolume( "ShowVolume", "Show item volume in cm3", "", @@ -1156,6 +1160,7 @@ void ivanconfig::Initialize() configsystem::AddOption(fsCategory,&DistLimitMagicMushrooms); configsystem::AddOption(fsCategory,&AutoPickupThrownItems); configsystem::AddOption(fsCategory,&AutoPickUpMatching); + configsystem::AddOption(fsCategory,&DropBeforeOffering); fsCategory="Game Window"; configsystem::AddOption(fsCategory,&Contrast); From c3f6f413b8321149dd75c6bba5da4100ef170824 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 1 Apr 2020 22:56:42 -0300 Subject: [PATCH 071/235] WIP-Favours: added infuscor; --- Main/Source/gods.cpp | 93 ++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 6188da627..c39c302ee 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -162,10 +162,15 @@ enum eFavours { FAVOUR_SUMMONWOLF, FAVOUR_TAME, FAVOUR_TELEPORT, + FAVOUR_BURNENEMIES, + FAVOUR_FEELENEMIES, + FAVOUR_POLYCONTROL, + FAVOUR_TELEPCONTROL, }; void god::FavourInit() //this one is better on this file { + AddFavourID(FAVOUR_BURNENEMIES,"Burn your Enemies"); AddFavourID(FAVOUR_CALLRAIN,"Make it Rain"); AddFavourID(FAVOUR_CONFUSE,"Cause Confusion amongst your enemies"); AddFavourID(FAVOUR_CURELEPROSY,"Cure Leprosy"); @@ -181,6 +186,7 @@ void god::FavourInit() //this one is better on this file AddFavourID(FAVOUR_ETHEREALMOV,"Become Ethereal"); AddFavourID(FAVOUR_EXTINGUISHFIRE,"Put out these Flames"); //TODO consider price vs FAVOUR_HEALBURNS); AddFavourID(FAVOUR_FEED,"Calms your Hunger"); + AddFavourID(FAVOUR_FEELENEMIES,"Feel your Enemies"); AddFavourID(FAVOUR_FIRESTORM,"Fiery Firestorm"); AddFavourID(FAVOUR_FIXEQUIPMENT,"Fix one broken equipped item"); AddFavourID(FAVOUR_HEALBURNS,"Heals your burns"); @@ -188,11 +194,13 @@ void god::FavourInit() //this one is better on this file AddFavourID(FAVOUR_INFRAVISION,"See in the Darkness"); AddFavourID(FAVOUR_INVIGORATE,"Invigorate"); AddFavourID(FAVOUR_INVISIBILITY,"Become Invisible"); + AddFavourID(FAVOUR_POLYCONTROL,"Control what you are"); AddFavourID(FAVOUR_SHOPPING,"Black Friday"); AddFavourID(FAVOUR_SPEEDUP,"Make you Fast"); AddFavourID(FAVOUR_STOPFIRE,"Unburn one Equipment"); AddFavourID(FAVOUR_SUMMONWOLF,"Summon Wolf friend(s)"); AddFavourID(FAVOUR_TAME,"Tame this Monster"); + AddFavourID(FAVOUR_TELEPCONTROL,"Decide where you are sent"); AddFavourID(FAVOUR_TELEPORT,"Teleport"); } @@ -1146,7 +1154,7 @@ int CalcDuration(god* G) if(dynamic_cast(G)) return 200 * PLAYER->GetAttribute(WISDOM) + Max(G->GetRelation(), 0); - if(dynamic_cast(G)) + if(dynamic_cast(G) || dynamic_cast(G)) return 300 * PLAYER->GetAttribute(WISDOM) + G->GetRelation() * 5; ABORT("invalid duration calc for god %d",G->GetName()); @@ -1685,12 +1693,7 @@ void scabies::PrayBadEffect() } } -bool infuscor::Favour(int iWhat, int iDebit) -{ - return false; -} - -void infuscor::PrayGoodEffect() +bool FavourBurnYourEnemies(god* G) { truth Success = false; @@ -1718,7 +1721,7 @@ void infuscor::PrayGoodEffect() && (BodyPart->GetMainMaterial()->GetInteractionFlags() & CAN_BURN) && !BodyPart->IsBurning()) { - if(BodyPart->TestActivationEnergy(20 + GetRelation() / 10)) + if(BodyPart->TestActivationEnergy(20 + G->GetRelation() / 10)) { Success = true; Burned = true; @@ -1726,45 +1729,77 @@ void infuscor::PrayGoodEffect() } } if(Burned) - ADD_MESSAGE("%s savagely sets fire to %s!", GetName(), Victim->CHAR_DESCRIPTION(DEFINITE)); + ADD_MESSAGE("%s savagely sets fire to %s!", G->GetName(), Victim->CHAR_DESCRIPTION(DEFINITE)); } } } + + return Success; +} + +int InfuscorFavourDuration = 0; +bool FavourFeelYourEnemies(god* G) +{ + if(!PLAYER->StateIsActivated(ESP)) + PLAYER->BeginTemporaryState(ESP, InfuscorFavourDuration); + else + PLAYER->EditTemporaryStateCounter(ESP, PLAYER->GetTemporaryStateCounter(ESP)+InfuscorFavourDuration); + ADD_MESSAGE("You feel %s whisper in your mind.", G->GetName()); + return true; +} +bool FavourControlWhatYouAre(god* G) +{ + if(!PLAYER->StateIsActivated(POLYMORPH_CONTROL)) + PLAYER->BeginTemporaryState(POLYMORPH_CONTROL, InfuscorFavourDuration); + else + PLAYER->EditTemporaryStateCounter(POLYMORPH_CONTROL, PLAYER->GetTemporaryStateCounter(POLYMORPH_CONTROL)+InfuscorFavourDuration); + ADD_MESSAGE("You feel %s gently touch your body.", G->GetName()); + return true; +} +bool FavourTeleportControl(god* G) +{ + if(!PLAYER->StateIsActivated(TELEPORT_CONTROL)) + PLAYER->BeginTemporaryState(TELEPORT_CONTROL, InfuscorFavourDuration); + else + PLAYER->EditTemporaryStateCounter(TELEPORT_CONTROL, PLAYER->GetTemporaryStateCounter(TELEPORT_CONTROL)+InfuscorFavourDuration); + ADD_MESSAGE("You feel %s gently touch your soul.", G->GetName()); + return true; +} +bool infuscor::Favour(int iWhat, int iDebit) +{ + if(CallFavour(FavourBurnYourEnemies,FAVOUR_BURNENEMIES,iWhat,iDebit,200))return true; + if(CallFavour(FavourFeelYourEnemies,FAVOUR_FEELENEMIES,iWhat,iDebit,250))return true; + if(CallFavour(FavourControlWhatYouAre,FAVOUR_POLYCONTROL,iWhat,iDebit,300))return true; + if(CallFavour(FavourTeleportControl,FAVOUR_TELEPCONTROL,iWhat,iDebit,300))return true; + return false; +} + +void infuscor::PrayGoodEffect() +{ + truth Success = Favour(FAVOUR_BURNENEMIES); if(!Success) { - int Duration = 300 * PLAYER->GetAttribute(WISDOM) + Relation * 5; - + int InfuscorFavourDuration = CalcDuration(this); + if(!PLAYER->StateIsActivated(ESP) || - PLAYER->GetTemporaryStateCounter(ESP) < Duration) + PLAYER->GetTemporaryStateCounter(ESP) < InfuscorFavourDuration) { - if(!PLAYER->StateIsActivated(ESP)) - PLAYER->BeginTemporaryState(ESP, Duration); - else - PLAYER->EditTemporaryStateCounter(ESP, PLAYER->GetTemporaryStateCounter(ESP)+Duration); - ADD_MESSAGE("You feel %s whisper in your mind.", GetName()); + Favour(FAVOUR_FEELENEMIES); return; } if(!PLAYER->StateIsActivated(POLYMORPH_CONTROL) || - PLAYER->GetTemporaryStateCounter(POLYMORPH_CONTROL) < Duration) + PLAYER->GetTemporaryStateCounter(POLYMORPH_CONTROL) < InfuscorFavourDuration) { - if(!PLAYER->StateIsActivated(POLYMORPH_CONTROL)) - PLAYER->BeginTemporaryState(POLYMORPH_CONTROL, Duration); - else - PLAYER->EditTemporaryStateCounter(POLYMORPH_CONTROL, PLAYER->GetTemporaryStateCounter(POLYMORPH_CONTROL)+Duration); - ADD_MESSAGE("You feel %s gently touch your body.", GetName()); + Favour(FAVOUR_POLYCONTROL); return; } if(!PLAYER->StateIsActivated(TELEPORT_CONTROL) || - PLAYER->GetTemporaryStateCounter(TELEPORT_CONTROL) < Duration) + PLAYER->GetTemporaryStateCounter(TELEPORT_CONTROL) < InfuscorFavourDuration) { - if(!PLAYER->StateIsActivated(TELEPORT_CONTROL)) - PLAYER->BeginTemporaryState(TELEPORT_CONTROL, Duration); - else - PLAYER->EditTemporaryStateCounter(TELEPORT_CONTROL, PLAYER->GetTemporaryStateCounter(TELEPORT_CONTROL)+Duration); - ADD_MESSAGE("You feel %s gently touch your soul.", GetName()); + Favour(FAVOUR_TELEPCONTROL); return; } From 5de69353d871697c4254475be260e7ec9cb72df1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 1 Apr 2020 23:19:16 -0300 Subject: [PATCH 072/235] WIP-Favours: added cruentus; --- Main/Source/gods.cpp | 84 +++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index c39c302ee..5029442d9 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -166,12 +166,15 @@ enum eFavours { FAVOUR_FEELENEMIES, FAVOUR_POLYCONTROL, FAVOUR_TELEPCONTROL, + FAVOUR_ENRAGE, + FAVOUR_CAUSEFEAR, }; void god::FavourInit() //this one is better on this file { AddFavourID(FAVOUR_BURNENEMIES,"Burn your Enemies"); AddFavourID(FAVOUR_CALLRAIN,"Make it Rain"); + AddFavourID(FAVOUR_CAUSEFEAR,"Your enemies will Fear you"); AddFavourID(FAVOUR_CONFUSE,"Cause Confusion amongst your enemies"); AddFavourID(FAVOUR_CURELEPROSY,"Cure Leprosy"); AddFavourID(FAVOUR_CURELYCANTHROPY,"Cure Lycanthropy"); @@ -183,6 +186,7 @@ void god::FavourInit() //this one is better on this file AddFavourID(FAVOUR_DISEASEIMMUNITY,"Gain temporary Immunity to Diseases"); AddFavourID(FAVOUR_EARTHQUAKE,"Invoke the rage of an Earth Quake"); AddFavourID(FAVOUR_ENCHANT,"Enchant Equipment"); + AddFavourID(FAVOUR_ENRAGE,"Fill you with Rage"); AddFavourID(FAVOUR_ETHEREALMOV,"Become Ethereal"); AddFavourID(FAVOUR_EXTINGUISHFIRE,"Put out these Flames"); //TODO consider price vs FAVOUR_HEALBURNS); AddFavourID(FAVOUR_FEED,"Calms your Hunger"); @@ -1809,42 +1813,16 @@ void infuscor::PrayGoodEffect() return; } -bool cruentus::Favour(int iWhat, int iDebit) +bool FavourEnrage(god* G) { - return false; + ADD_MESSAGE("%s snarls: \"Fight, you lousy coward!\"", G->GetName()); + PLAYER->DeActivateTemporaryState(PANIC); + PLAYER->BeginTemporaryState(REGENERATION, 200 * PLAYER->GetAttribute(WISDOM) + G->GetRelation() * 3); + PLAYER->BeginTemporaryState(FEARLESS, 200 * PLAYER->GetAttribute(WISDOM) + G->GetRelation() * 3); + return true; } - -void cruentus::PrayGoodEffect() +bool FavourCauseFear(god* G) { - // Blood for the god of blood! - if(!RAND_4 || Relation == 1000) - { - beamdata Beam - ( - 0, - CONST_S("drowned by the blood of ") + GetName(), - YOURSELF, - 0 - ); - lsquare* Square = PLAYER->GetLSquareUnder(); - Square->LiquidRain(Beam, BLOOD); - - if(PLAYER->HasHead()) - ADD_MESSAGE("A torrential rain of blood descends on your head."); - else - ADD_MESSAGE("A rain of blood drizzles all around you."); - } - - // A little bit of healing, but only usable when panicked. - if(PLAYER->StateIsActivated(PANIC)) - { - ADD_MESSAGE("%s snarls: \"Fight, you lousy coward!\"", GetName()); - PLAYER->DeActivateTemporaryState(PANIC); - PLAYER->BeginTemporaryState(REGENERATION, 200 * PLAYER->GetAttribute(WISDOM) + Relation * 3); - PLAYER->BeginTemporaryState(FEARLESS, 200 * PLAYER->GetAttribute(WISDOM) + Relation * 3); - return; - } - rect Rect; femath::CalculateEnvironmentRectangle(Rect, game::GetCurrentLevel()->GetBorder(), PLAYER->GetPos(), 10); truth AudiencePresent = false; @@ -1883,9 +1861,49 @@ void cruentus::PrayGoodEffect() Audience->BeginTemporaryState(PANIC, 30 * PLAYER->GetAttribute(WISDOM) + RAND() % 500); } + return true; + } + + return false; +} +bool cruentus::Favour(int iWhat, int iDebit) +{ + if(CallFavour(FavourEnrage,FAVOUR_ENRAGE,iWhat,iDebit,200))return true; + if(CallFavour(FavourCauseFear,FAVOUR_CAUSEFEAR,iWhat,iDebit,500))return true; + return false; +} + +void cruentus::PrayGoodEffect() +{ + // Blood for the god of blood! + if(!RAND_4 || Relation == 1000) + { + beamdata Beam + ( + 0, + CONST_S("drowned by the blood of ") + GetName(), + YOURSELF, + 0 + ); + lsquare* Square = PLAYER->GetLSquareUnder(); + Square->LiquidRain(Beam, BLOOD); + + if(PLAYER->HasHead()) + ADD_MESSAGE("A torrential rain of blood descends on your head."); + else + ADD_MESSAGE("A rain of blood drizzles all around you."); + } + + // A little bit of healing, but only usable when panicked. + if(PLAYER->StateIsActivated(PANIC)) + { + Favour(FAVOUR_ENRAGE); return; } + if(Favour(FAVOUR_CAUSEFEAR)) + return; + if(!RAND_2) { item* Weapon = PLAYER->GetMainWielded(); From fdb24d6b4e8047ec478174cd69a05086af365d11 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 2 Apr 2020 12:27:54 -0300 Subject: [PATCH 073/235] WIP-GodFavours: asking favours was too safe... not anymore!; --- Main/Source/command.cpp | 41 ++++++++++++++++++++++++++++------------- Main/Source/game.cpp | 9 +++++++-- Main/Source/god.cpp | 7 ++++++- Main/Source/gods.cpp | 5 ++++- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 2692632cf..d2fe40933 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1160,9 +1160,10 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng truth commandsystem::AskFavour(character* Char) { - felist felSpellList(CONST_S("To Whom you want to ask a ")+game::GetVerbalPlayerAlignment()+" favour?"); + felist felFavourList(CONST_S("To Whom you want to ask a ")+game::GetVerbalPlayerAlignment()+" favour?"); - std::vector> vGS; + int iTot=0; + std::vector> vSelectableFavours; for(int c = 1; c <= GODS; ++c){ god* pgod = game::GetGod(c); @@ -1173,38 +1174,52 @@ truth commandsystem::AskFavour(character* Char) if(c == Char->GetLSquareUnder()->GetDivineMaster())bOk=true; std::vector v = pgod->GetKnownSpells(); - for(auto piSpell = v.begin(); piSpell != v.end(); ++piSpell){ + for(auto piFavour = v.begin(); piFavour != v.end(); ++piFavour){ festring fs = CONST_S("")+ game::GetAlignment(pgod->GetAlignment())+" "+ - pgod->GetName()+" may grant you a \""+god::GetFavourName(*piSpell)+"\" favour."; + pgod->GetName()+" may grant you a \""+god::GetFavourName(*piFavour)+"\" favour."; + col16 col = bOk ? LIGHT_GRAY : DARK_GRAY; if(!bOk && game::WizardModeIsReallyActive())col=RED; - felSpellList.AddEntry(fs, col, 0, NO_IMAGE, bOk || game::WizardModeIsReallyActive()); + + felFavourList.AddEntry(fs, col, 0, NO_IMAGE, bOk || game::WizardModeIsReallyActive()); if(bOk || game::WizardModeIsReallyActive()){ // std::pair GS; // GS.first = pgod; // GS.second = *piSpell; - vGS.push_back(std::make_pair(pgod,*piSpell)); + vSelectableFavours.push_back(std::make_pair(pgod,*piFavour)); } + + iTot++; } } - game::SetStandardListAttributes(felSpellList); - felSpellList.AddFlags(SELECTABLE); - int Select = felSpellList.Draw(); + festring fsMsg; + fsMsg = fsMsg+"You don't know about any "+game::GetVerbalPlayerAlignment()+" favours..."; + if(iTot>0 && vSelectableFavours.size()==0){ + felFavourList.AddEntry(cfestring("(")+fsMsg+")", DARK_GRAY, 0, NO_IMAGE, false); + } + + int Select = LIST_WAS_EMPTY; + if(iTot>0){ + game::SetStandardListAttributes(felFavourList); + felFavourList.AddFlags(SELECTABLE); + Select = felFavourList.Draw(); + } - if(Select == LIST_WAS_EMPTY) + if(Select == LIST_WAS_EMPTY || vSelectableFavours.size()==0) { - ADD_MESSAGE("You can't feel the availability of any %s favours...", game::GetVerbalPlayerAlignment()); +// ADD_MESSAGE("You don't know about any %s favours...", game::GetVerbalPlayerAlignment()); + ADD_MESSAGE(fsMsg.CStr()); return false; } if(Select & FELIST_ERROR_BIT) return false; - god* G = vGS[Select].first; - int iFavour = vGS[Select].second; + god* G = vSelectableFavours[Select].first; + int iFavour = vSelectableFavours[Select].second; int iDebit=FAVOURDEBIT_AUTO; int DivineMaster = Char->GetLSquareUnder()->GetDivineMaster(); if(DivineMaster){ diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index c2b003686..741e0b04b 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -4062,9 +4062,14 @@ int game::GetGodAlignmentVsPlayer(god* G) case ALM: return 1; //L- /** - * Neutral has 1 less god, so 1 less source of favours. To compensate, these 3 being 0 will make it easier in favour cost for neutrally aligned players. + * There are 15 gods, 11 god alignments and 9 player alignments, so lets make it work: + * Neutrality has 1 less god, so 1 less source of favours (and no major/extreme god). + * These 3 neutral gods returning 0 will compensate by using less favour cost, to all of them, + * for a neutrally aligned player at gods.cpp/CalcDebit(). + * (the alternative would be to expand player alignments to 11 to match gods' ones, anyone up to it?) + * * Btw, these "N" matches the base Neutral alignment for these gods. - * And... it won't match very well the description at GetVerbalPlayerAlignment() tho, but let player guess it :) + * And... it won't match very well the description at GetVerbalPlayerAlignment(), but that shouldn't be a problem. */ case ANP: return 0; //N+ case AN: return 0; //N= diff --git a/Main/Source/god.cpp b/Main/Source/god.cpp index a414128a6..86d4d0f81 100644 --- a/Main/Source/god.cpp +++ b/Main/Source/god.cpp @@ -579,11 +579,16 @@ std::vector> god::vFavID; bool god::Favour(int iWhat, int iDebit) { - if(RelationGetRelation() < 0) //costy if bad relation + iDebit*=2; + // can ask more favours if very well aligned if(game::GetPlayerAlignment() == game::GetGodAlignmentVsPlayer(G)){ - iDebit/=3; + iDebit/=2; } // skilled in praying :) From 2948826c72eb9310fba3062a6082bc04d1e61542 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 3 Apr 2020 21:05:18 -0300 Subject: [PATCH 074/235] WIP-GodFavours: asking favours is now less safe; -Fix telepor favour; -Seges favour cures vampirism too now; Improved drop item on offering and its description (as the light source emitter too); --- Main/Source/command.cpp | 10 ++++++---- Main/Source/god.cpp | 8 +++++--- Main/Source/gods.cpp | 26 +++++++++++++++----------- Main/Source/iconf.cpp | 6 +++--- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index d2fe40933..b4455a371 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1160,7 +1160,7 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng truth commandsystem::AskFavour(character* Char) { - felist felFavourList(CONST_S("To Whom you want to ask a ")+game::GetVerbalPlayerAlignment()+" favour?"); + felist felFavourList(CONST_S("Ask a favour from Whom?")); int iTot=0; std::vector> vSelectableFavours; @@ -1404,16 +1404,18 @@ truth commandsystem::Offer(character* Char) if(Item) { - if(ivanconfig::IsDropBeforeOffering())Item->MoveTo(Char->GetLSquareUnder()->GetStack()); //drops before offering so non accepted will unclutter player inventory if(game::GetGod(Square->GetDivineMaster())->ReceiveOffer(Item)) { Item->RemoveFromSlot(); Item->SendToHell(); Char->DexterityAction(5); /** C **/ return true; - } - else + } else { + if(ivanconfig::IsDropBeforeOffering()) + if(!game::IsQuestItem(Item)) + Item->MoveTo(Char->GetLSquareUnder()->GetStack()); //drops before offering so non accepted will unclutter player inventory return false; + } } else return false; diff --git a/Main/Source/god.cpp b/Main/Source/god.cpp index 86d4d0f81..2fcbc94c4 100644 --- a/Main/Source/god.cpp +++ b/Main/Source/god.cpp @@ -579,14 +579,16 @@ std::vector> god::vFavID; bool god::Favour(int iWhat, int iDebit) { - if((Relation-iDebit) < -1000){ - ADD_MESSAGE("%s ignores your plea...",GetName()); + if(Relation < 0){ + ADD_MESSAGE("%s ignores your plea and makes sure you understand it...",GetName()); + PrayBadEffect(); return false; } - if(RelationGetRelation() < 0) //costy if bad relation - iDebit*=2; - // can ask more favours if very well aligned if(game::GetPlayerAlignment() == game::GetGodAlignmentVsPlayer(G)){ iDebit/=2; @@ -242,10 +241,14 @@ void AddKnownSpell(std::vector& ks,int iNew) bool FavourTeleport(god* G) { + if(!PLAYER->StateIsActivated(TELEPORT_LOCK)) + { ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement!"); game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); PLAYER->Move(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); return true; + } + return false; } bool god::CallFavour(CallFavourType call, int iCallFavour, int iWhat, int iDebit, int iDbtDefault) @@ -286,11 +289,7 @@ void sophos::PrayGoodEffect() { truth DidHelp = false; - if(!PLAYER->StateIsActivated(TELEPORT_LOCK)) - { - Favour(FAVOUR_TELEPORT); - DidHelp = true; - } + DidHelp = Favour(FAVOUR_TELEPORT); // Give a little attribute experience (Cha already given by Dulcis and not Wis, // as we want to check Wis to give the experience). @@ -596,6 +595,12 @@ bool FavourFeed(god* G) return true; } +bool FavourCureVampirism(god* G) +{ + ADD_MESSAGE("%s cures your bloodlust.", G->GetName()); + PLAYER->DeActivateTemporaryState(VAMPIRISM); + return true; +} bool seges::Favour(int iWhat, int iDebit) { @@ -603,7 +608,7 @@ bool seges::Favour(int iWhat, int iDebit) if(CallFavour(&FavourCurePoison,FAVOUR_CUREPOISON,iWhat,iDebit,200))return true; if(CallFavour(&FavourCureLeprosy,FAVOUR_CURELEPROSY,iWhat,iDebit,250))return true; if(CallFavour(&FavourCureLycanthropy,FAVOUR_CURELYCANTHROPY,iWhat,iDebit,300))return true; - //TODO is vampirism bad in anyway? + if(CallFavour(&FavourCureVampirism,FAVOUR_CUREVAMP,iWhat,iDebit,100))return true; if(CallFavour(&FavourCureTapeworm,FAVOUR_CURETAPEWORM,iWhat,iDebit,250))return true; if(CallFavour(&FavourCureMindworm,FAVOUR_CUREMINDWORM,iWhat,iDebit,500))return true; if(CallFavour(&FavourFeed,FAVOUR_FEED,iWhat,iDebit,300))return true; //bloats @@ -640,8 +645,7 @@ void seges::PrayGoodEffect() if(PLAYER->TemporaryStateIsActivated(VAMPIRISM)) { - ADD_MESSAGE("%s cures your bloodlust.", GetName()); - PLAYER->DeActivateTemporaryState(VAMPIRISM); + Favour(FAVOUR_CUREVAMP); return; } diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 46e860d87..87e694517 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -165,8 +165,8 @@ truthoption ivanconfig::AllWeightIsRelevant("AllWeightIsRelevant", "", false); truthoption ivanconfig::DropBeforeOffering("DropBeforeOffering", - "Drop the item on altar before offering it.", - "Beware it may be owned floor!", + "Drop the item on altar in case it is not accepted", + "Automatically drop offered items on an altar to prevent them from cluttering your inventory should the god not accept your gift. Beware it may be owned floor!", false); truthoption ivanconfig::ShowVolume( "ShowVolume", "Show item volume in cm3", @@ -181,7 +181,7 @@ truthoption ivanconfig::HideWeirdHitAnimationsThatLookLikeMiss("HideWeirdHitAnim "", true); truthoption ivanconfig::UseLightEmiterBasedOnVolume("UseLightEmiterBasedOnVolume", - "Small crystal rocks etc. will emit less light.", + "Small light sources emit less light", "This experimental feature still has bugs that happen when splitting rocks etc. Most are fixed after restarting the game.", false); truthoption ivanconfig::ShowFullDungeonName("ShowFullDungeonName", From 839a0a86f57391eb44c536fdddc49a101f0e0549 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 3 Apr 2020 21:14:45 -0300 Subject: [PATCH 075/235] fixed 2 LGTM alerts --- Main/Source/gods.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 50d4b4436..e9e518c1a 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -1168,7 +1168,7 @@ int CalcDuration(god* G) if(dynamic_cast(G) || dynamic_cast(G)) return 300 * PLAYER->GetAttribute(WISDOM) + G->GetRelation() * 5; - ABORT("invalid duration calc for god %d",G->GetName()); + ABORT("duration calc for god %s is not available here!",G->GetName()); } bool FavourCureSlowness(god* G) { @@ -1791,7 +1791,7 @@ void infuscor::PrayGoodEffect() if(!Success) { - int InfuscorFavourDuration = CalcDuration(this); + InfuscorFavourDuration = CalcDuration(this); if(!PLAYER->StateIsActivated(ESP) || PLAYER->GetTemporaryStateCounter(ESP) < InfuscorFavourDuration) From c68dca9c55010349e7cdab125eccb4bfa9e183a3 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 3 Apr 2020 22:22:06 -0300 Subject: [PATCH 076/235] Tailoring: fixed restrictions; --- Main/Source/cmdcraft.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 512a39ab7..c44fa073b 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2369,8 +2369,10 @@ struct srpForgeItem : public recipe{ bool bIsWeapon = itSpawn->IsWeapon(rpd.rc.H()); bool bMainMatOk = false; - bool bMustTailor = (bIsWeapon && dynamic_cast(itSpawn)) || dynamic_cast(itSpawn); - bool bCanTailor = dynamic_cast(itSpawn) && !itSpawn->IsHelmet(rpd.rc.H()); + bool bMustTailor = dynamic_cast(itSpawn) || dynamic_cast(itSpawn); + bool bCanTailor = dynamic_cast(itSpawn) && + !( dynamic_cast(itSpawn) || + dynamic_cast(itSpawn) ); if(bMustTailor || bCanTailor){ // tailoring festring fsM("as MAIN material (cloth "); // only main can be cloth float fPerc = 0.85; From 3d9c00d3f90857689f5450cc3bc12303ef7cfd40 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 4 Apr 2020 16:08:30 -0300 Subject: [PATCH 077/235] GodFavours: MANA and WISDOM now lowers favour price, increased the minimum price also; --- Main/Source/gods.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index e9e518c1a..43f9e288f 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -223,10 +223,14 @@ int CalcDebit(god* G,int iDebit,int iDefault){ iDebit/=2; } - // skilled in praying :) - iDebit -= game::GetPlayer()->GetAttribute(MANA); + // skilled in manipulative praying :) + iDebit -= ( + (game::GetPlayer()->GetAttribute(MANA)*2.0) + + + game::GetPlayer()->GetAttribute(WISDOM) + ) / 3.0; - if(iDebit<10)iDebit=10; //max of 100 vafours in the best case (master) only + if(iDebit<50)iDebit=50; //max of 20 vafours (50*20=1000) (too much?) in the best case (master) only } return iDebit; } From 6bd03a85bf84126269012b44bdff4a08ae7c61b2 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 4 Apr 2020 23:20:04 -0300 Subject: [PATCH 078/235] Crafting: new tailor bench gfx and OLTerra. You can construct a TAILORING_BENCH now. Moved some code around to make crafting work better. Fixed a bug before tailoring by letting player mix lumps before it. --- Graphics/OLTerra.png | Bin 11480 -> 11586 bytes Main/Include/confdef.h | 1 + Main/Include/craft.h | 2 + Main/Source/cmdcraft.cpp | 121 +++++++++++++++++++++++++++------------ Main/Source/game.cpp | 1 + Script/define.dat | 1 + Script/olterra.dat | 15 +++++ 7 files changed, 104 insertions(+), 37 deletions(-) diff --git a/Graphics/OLTerra.png b/Graphics/OLTerra.png index 92d639289062453fb5041ce7c20cda488f2f0bef..16a67f6970e066a9a2f94b0df02198e9c6a6282e 100644 GIT binary patch delta 10804 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 delta 10697 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 Mo9I^Eb)t;@KLq?= 135) + if(game::GetCurrentSavefileVersion() >= 135){ SaveFile >> bTailoringMode; + SaveFile >> v2TailoringWorkbenchLocation; + } // if(otSpawnType!=CTT_NONE) // SaveFile >> otSpawn; @@ -406,6 +410,7 @@ cfestring recipedata::id() const RPDINFOV2(v2AnvilLocation); RPDINFOV2(v2ForgeLocation); RPDINFOV2(v2WorkbenchLocation); + RPDINFOV2(v2TailoringWorkbenchLocation); RPDINFOV2(v2PlaceAt); RPDINFOV2(v2PlayerCraftingAt); @@ -521,6 +526,7 @@ recipedata::recipedata(humanoid* H,uint sel) : rc(H,sel) bMeltable=false; v2WorkbenchLocation=v2(0,0); + v2TailoringWorkbenchLocation=v2(0,0); iRemainingTurnsToFinish=iBaseTurnsToFinish; bGradativeCraftOverride=false; bTailoringMode=false; @@ -800,7 +806,7 @@ struct recipe{ } calcToolTurns(rpd,iMult); - if(!recipe::findOLT(rpd,WORK_BENCH)){ + if(!recipe::findOLT(rpd,TAILORING_BENCH)){ ADD_MESSAGE("As you lack a workbench, it will take a while."); //it is good to measure, hold tight, has a good height etc... rpd.iBaseTurnsToFinish *= 3; } @@ -1011,6 +1017,40 @@ struct recipe{ } } + void joinLumpsEqualTo(recipedata& rpd,material* matM){ + // multiple (compatible with 1st) lumps will be mixed in a big one again + for(int i=1;i(LumpToAdd)==NULL)continue; + + material* LumpToAddM = LumpToAdd->GetMainMaterial();DBGLN; + if(LumpToAddM->GetConfig()!=matM->GetConfig())continue; + + // join + matM->SetVolume(matM->GetVolume()+LumpToAddM->GetVolume());DBGLN; + + craftcore::SendToHellSafely(LumpToAdd); + } + } + + void joinLumpsEqualToFirst(recipedata& rpd){ + item* Lump = game::SearchItem(rpd.ingredientsIDs[0]); + material* matM=Lump->GetMainMaterial(); + joinLumpsEqualTo(rpd,matM); + } + + void askForEqualLumps(recipedata& rpd){ + ci CI; + CI.bOverridesQuestion=true; + CI.bMsgInsuficientMat=false; + CI.bInstaAddIngredients=true; + int iWeakestCfgDummy; + bool bDummy = choseIngredients( + festring("First chosen lump's material will be mixed with further ones of same material only, hit ESC to accept."), + 1000000, //just any "impossible" huge volume as "limit" + rpd, iWeakestCfgDummy, CI); // true, 0, false, true, false, true); + } + template static truth choseIngredients( cfestring fsQ, long reqVolPrecise, @@ -1223,6 +1263,9 @@ struct recipe{ case WORK_BENCH: rpd.v2WorkbenchLocation = lsqr->GetPos(); break; + case TAILORING_BENCH: + rpd.v2TailoringWorkbenchLocation = lsqr->GetPos(); + break; } } @@ -1267,6 +1310,7 @@ struct recipe{ case FORGE:fsWhat="forge";break; case ANVIL:fsWhat="anvil";break; case WORK_BENCH:fsWhat="workbench";break; + case TAILORING_BENCH:fsWhat="tailoring bench";break; } festring fsMsg="No "; @@ -1650,6 +1694,25 @@ struct srpWorkBench : public srpOltBASE{ return srpOltBASE::work(rpd); } };srpWorkBench rpWorkBench; +struct srpTWorkBench : public srpOltBASE{ + virtual bool spawnCfg(recipedata& rpd){ + rpd.otSpawnType=CTT_FURNITURE; + rpd.otSpawnCfg=TAILORING_BENCH; + return true; + } + + virtual void fillInfo(){ + init("build","a tailoring workbench"); + desc << "Build a tailoring workbench for further crafting. " << fsDescBASE; + } + + virtual bool work(recipedata& rpd){ + iReqVol=9000; + iTurns=30; + bRequiresWhere=true; + return srpOltBASE::work(rpd); + } +};srpTWorkBench rpTWorkBench; struct srpWall2 : public srpOltBASE{ virtual bool spawnCfg(recipedata& rpd){ rpd.otSpawnType=CTT_WALL; @@ -1685,40 +1748,6 @@ struct srpJoinLumps : public recipe{ desc << "Merge lumps of the same material into a single, bigger one."; } - void askForEqualLumps(recipedata& rpd){ - ci CI; - CI.bOverridesQuestion=true; - CI.bMsgInsuficientMat=false; - CI.bInstaAddIngredients=true; - int iWeakestCfgDummy; - bool bDummy = choseIngredients( - festring("First chosen lump's material will be mixed with further ones of same material only, hit ESC to accept."), - 1000000, //just any "impossible" huge volume as "limit" - rpd, iWeakestCfgDummy, CI); // true, 0, false, true, false, true); - } - - void joinLumpsEqualTo(recipedata& rpd,material* matM){ - // multiple (compatible with 1st) lumps will be mixed in a big one again - for(int i=1;i(LumpToAdd)==NULL)continue; - - material* LumpToAddM = LumpToAdd->GetMainMaterial();DBGLN; - if(LumpToAddM->GetConfig()!=matM->GetConfig())continue; - - // join - matM->SetVolume(matM->GetVolume()+LumpToAddM->GetVolume());DBGLN; - - craftcore::SendToHellSafely(LumpToAdd); - } - } - - void joinLumpsEqualToFirst(recipedata& rpd){ - item* Lump = game::SearchItem(rpd.ingredientsIDs[0]); - material* matM=Lump->GetMainMaterial(); - joinLumpsEqualTo(rpd,matM); - } - virtual bool work(recipedata& rpd){ // it is just like to put them all together, no effort, instant. askForEqualLumps(rpd); @@ -2381,6 +2410,12 @@ struct srpForgeItem : public recipe{ * cloth will be cut and sewed */ if(!bMainMatOk){ + askForEqualLumps(rpd); + if(!rpd.ingredientsIDs.empty()){ + joinLumpsEqualToFirst(rpd); + rpd.ingredientsIDs.clear(); + } + ci CI = CIM; CI.fUsablePercVol=fPerc; CI.bMustBeTailorable = rpd.bTailoringMode = true; @@ -2548,7 +2583,7 @@ struct srpForgeItem : public recipe{ } if(rpd.bTailoringMode){ - if(!recipe::findOLT(rpd,WORK_BENCH)){ //must be near it //TODO should be a new bench called TAILORING_BENCH with new graphics one day... + if(!recipe::findOLT(rpd,TAILORING_BENCH)){ //must be near it //TODO should be a new bench called TAILORING_BENCH with new graphics one day... craftcore::SendToHellSafely(itSpawn); return false; } @@ -3066,6 +3101,7 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif RP(rpForge); RP(rpWall2); RP(rpWorkBench); + RP(rpTWorkBench); if(bInitRecipes)craftRecipes.AddEntry(festring()+"Alchemy:", DARK_GRAY, 0, NO_IMAGE, false); RP(rpAcid); @@ -3392,6 +3428,7 @@ void crafthandle::CraftWorkTurn(recipedata& rpd){ DBG1(rpd.iRemainingTurnsToFini if(!lsqrHF)lsqrHF=rpd.lsqrPlaceAt; if(!lsqrHF)lsqrHF=rpd.v2AnvilLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2AnvilLocation); if(!lsqrHF)lsqrHF=rpd.v2WorkbenchLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2WorkbenchLocation); + if(!lsqrHF)lsqrHF=rpd.v2TailoringWorkbenchLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2TailoringWorkbenchLocation); if(!lsqrHF)lsqrHF=rpd.v2PlaceAt.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2PlaceAt); if(!lsqrHF)lsqrHF=rpd.v2ForgeLocation.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2ForgeLocation); if(!lsqrHF)lsqrHF=rpd.v2XplodAt.Is0() ? NULL : rpd.rc.H()->GetNearLSquare(rpd.v2XplodAt); @@ -3665,6 +3702,16 @@ void crafthandle::CheckFacilities(recipedata& rpd){ //TODO workbench should be damaged w/o explosions (that is area effect related to fire/forge) rpd.bOnlyXplodIfCriticalFumble=true; //TODO kept as WIP //explode/sparks at workbench doesnt make much sense: rpd.v2XplodAt=rpd.v2WorkbenchLocation; + }else + if(!rpd.v2TailoringWorkbenchLocation.Is0()){ + olterrain* otTWorkbench = game::GetCurrentLevel()->GetLSquare(rpd.v2TailoringWorkbenchLocation)->GetOLTerrain(); + if(otTWorkbench==NULL || otTWorkbench->GetConfig()!=TAILORING_BENCH){ + ADD_MESSAGE("The tailoring workbench was destroyed!"); + rpd.bFailedTerminateCancel=true; + } + + //TODO workbench should be damaged w/o explosions (that is area effect related to fire/forge) + rpd.bOnlyXplodIfCriticalFumble=true; //TODO kept as WIP } } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 741e0b04b..c3d1ba86a 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1438,6 +1438,7 @@ bool game::CheckAddAutoMapNote(square* sqr) olt->GetConfig() == CHAIR || olt->GetConfig() == FORGE || olt->GetConfig() == WORK_BENCH || + olt->GetConfig() == TAILORING_BENCH || false ){ olt->AddName(fs,INDEFINITE); diff --git a/Script/define.dat b/Script/define.dat index e8b7dc91f..2a6a6a458 100644 --- a/Script/define.dat +++ b/Script/define.dat @@ -1125,6 +1125,7 @@ #define BANANA_TREE 36 #define DEAD_TREE 37 #define STRANGE_TREE 38 +#define TAILORING_BENCH 39 #define SNOW_BOULDER 4 diff --git a/Script/olterra.dat b/Script/olterra.dat index 496d18b05..8a43e75d1 100644 --- a/Script/olterra.dat +++ b/Script/olterra.dat @@ -623,6 +623,21 @@ decoration Walkability = ETHEREAL; } + Config TAILORING_BENCH; + { + CanBeDestroyed = true; + DigMessage = "You break the tailoring bench."; + MainMaterialConfig == FIR_WOOD; + MaterialColorB = rgb16(96, 96, 96); + MaterialColorC = rgb16(124, 50, 16); + SitMessage = "You carefully sit on the tailoring bench."; + RestMessage = "You surprisingly manage to take a nap on the tailoring bench."; + NameSingular = "tailoring bench"; + BitmapPos = 80, 224; + HPModifier = 25; + Walkability = ETHEREAL; + } + Config TABLE; { CanBeDestroyed = true; From 5d74678b74b6358b122912a7ce77a9062f853955 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 01:17:04 -0300 Subject: [PATCH 079/235] Fixed a crash that happens when the minimap is drawn too near the top screen edge, and we hover the mouse over the top mapNote. That would draw a wide white line, but the line would be drawn outside the game gfx buffer (Y<0) and it would SEGFAULT. The proper fix would be that bitmap::DrawLine() should actually check if it is drawing outside it's boundaries... --- FeLib/Source/bitmap.cpp | 5 +++++ Main/Source/game.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/FeLib/Source/bitmap.cpp b/FeLib/Source/bitmap.cpp index 5b0c69416..4500eb52e 100644 --- a/FeLib/Source/bitmap.cpp +++ b/FeLib/Source/bitmap.cpp @@ -865,6 +865,11 @@ void bitmap::DrawLine(int FromX, int FromY, v2 To, col16 Color, truth Wide) void bitmap::DrawLine(v2 From, v2 To, col16 Color, truth Wide) { DrawLine(From.X, From.Y, To.X, To.Y, Color, Wide); } +/** + * TODO this needs a fix: + * If a line is drawn from x,1 to anywhere and it is WIDE, the game will SEGFAULT. + * Every drawn dot must be checked if is inside the bitmap boundaries... + */ void bitmap::DrawLine(int OrigFromX, int OrigFromY, int OrigToX, int OrigToY, col16 Color, truth Wide) { if(OrigFromY == OrigToY) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index c3d1ba86a..931cd325f 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1655,6 +1655,7 @@ void game::DrawMapOverlay(bitmap* buffer) static v2 v2BmpSize(0,0); static v2 v2TopLeftFinal(0,0); static v2 v2MapScrSizeFinal(0,0); + static v2 v2MinTopLeft(10,10); // this prevents a crash related to drawing lines too near top and left game gfx buffer edges static bitmap* bmpFinal; bool bTransparentMap = bPositionQuestionMode && (CursorPos != PLAYER->GetPos()) && ivanconfig::IsTransparentMapLM(); @@ -1727,8 +1728,8 @@ void game::DrawMapOverlay(bitmap* buffer) // v2 v2VisibleDungeonScrSize=v2CL*TILE_SIZE; v2Center = area::getTopLeftCorner() +v2DungeonScrSize/2; v2TopLeft = v2Center -v2MapScrSize/2; - if(v2TopLeft.X<0)v2TopLeft.X=0; - if(v2TopLeft.Y<0)v2TopLeft.Y=0; + if(v2TopLeft.X RES.X)v2TopLeftFinal.X=RES.X-v2MapScrSizeFinal.X; if((v2TopLeftFinal.Y+v2MapScrSizeFinal.Y) > RES.Y)v2TopLeftFinal.Y=RES.Y-v2MapScrSizeFinal.Y; - if(v2TopLeftFinal.X<0)v2TopLeftFinal.X=0; - if(v2TopLeftFinal.Y<0)v2TopLeftFinal.Y=0; + if(v2TopLeftFinal.X Date: Sun, 5 Apr 2020 13:17:53 -0300 Subject: [PATCH 080/235] Created independent SFX volume config option as the existing volume control was not controlling it; --- Main/Include/iconf.h | 6 ++++++ Main/Source/iconf.cpp | 45 +++++++++++++++++++++++++++++++++++++++-- Main/Source/message.cpp | 14 +++++++------ 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index 5d41dc63e..15ccb9226 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -76,6 +76,7 @@ class ivanconfig static truth IsAllowMouseOnFelist(){return AllowMouseOnFelist.Value;} static truth IsStartingOutlinedGfx() { return bStartingOutlinedGfx; } static long GetVolume() { return Volume.Value; } + static long GetSfxVolume() { return SfxVolume.Value; } static long GetMIDIOutputDevice() { return MIDIOutputDevice.Value; } #ifndef __DJGPP__ @@ -142,8 +143,11 @@ class ivanconfig static void MemorizeEquipmentModeDisplayer(const cycleoption* O, festring& Entry); static void MIDIOutputDeviceDisplayer(const cycleoption*, festring&); static void VolumeDisplayer(const numberoption*, festring&); + static void SfxVolumeDisplayer(const numberoption*, festring&); static truth VolumeChangeInterface(numberoption*); + static truth SfxVolumeChangeInterface(numberoption*); static void VolumeChanger(numberoption*, long); + static void SfxVolumeChanger(numberoption*, long); static void AltSilhouetteDisplayer(const cycleoption* O, festring& Entry); static void AllowMouseOnFelistChanger(truthoption*, truth); @@ -171,6 +175,7 @@ class ivanconfig static void SetupCustomKeysChanger(truthoption*, truth); static void ContrastHandler(long); static void VolumeHandler(long); + static void SfxVolumeHandler(long); static void BackGroundDrawer(); static stringoption DefaultName; @@ -244,6 +249,7 @@ class ivanconfig static truthoption SmartOpenCloseApply; static truthoption BeNice; static scrollbaroption Volume; + static scrollbaroption SfxVolume; static cycleoption MIDIOutputDevice; #ifndef __DJGPP__ diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 87e694517..06fb5496d 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -315,13 +315,22 @@ numberoption ivanconfig::AltListItemWidth("AltListItemWidth", &AltListItemWidthChangeInterface, &AltListItemWidthChanger); scrollbaroption ivanconfig::Volume( "Volume", - "Volume", - "Select volume for sound effects and game music.", + "Music Volume", + "Select volume for game MIDI music", 127, &VolumeDisplayer, &VolumeChangeInterface, &VolumeChanger, &VolumeHandler); +scrollbaroption ivanconfig::SfxVolume( "SfxVolume", + "Soud Effects (SFX) Volume", + "Select volume for sound effects", + 127, + &SfxVolumeDisplayer, + &SfxVolumeChangeInterface, + &SfxVolumeChanger, + &SfxVolumeHandler); + cycleoption ivanconfig::MIDIOutputDevice( "MIDIOutputDevice", "Use MIDI soundtrack", "Select an output device for the game music, or disable soundtrack.", @@ -529,6 +538,10 @@ void ivanconfig::VolumeDisplayer(const numberoption* O, festring& Entry) { Entry << O->Value << "/127"; } +void ivanconfig::SfxVolumeDisplayer(const numberoption* O, festring& Entry) +{ + Entry << O->Value << "/127"; +} void ivanconfig::AltSilhouetteDisplayer(const cycleoption* O, festring& Entry) { @@ -839,6 +852,17 @@ truth ivanconfig::VolumeChangeInterface(numberoption* O) return false; } +truth ivanconfig::SfxVolumeChangeInterface(numberoption* O) +{ + iosystem::ScrollBarQuestion(CONST_S("Set new SFX volume value (0-127, '<' and '>' move the slider):"), + GetQuestionPos(), O->Value, 5, 0, 127, O->Value, WHITE, LIGHT_GRAY, DARK_GRAY, + game::GetMoveCommandKey(KEY_LEFT_INDEX), game::GetMoveCommandKey(KEY_RIGHT_INDEX), + !game::IsRunning(), static_cast(O)->BarHandler); + + clearToBackgroundAfterChangeInterface(); + + return false; +} void ivanconfig::XBRZSquaresAroundPlayerChanger(numberoption* O, long What) { @@ -961,6 +985,12 @@ void ivanconfig::VolumeChanger(numberoption* O, long What) audio::SetVolumeLevel(What); } +void ivanconfig::SfxVolumeChanger(numberoption* O, long What) +{ + if(What < 0) What = 0; + if(What > 127) What = 127; + O->Value = What; +} #ifndef __DJGPP__ @@ -1106,6 +1136,16 @@ void ivanconfig::VolumeHandler(long Value) game::DrawEverythingNoBlit(); } } +void ivanconfig::SfxVolumeHandler(long Value) +{ + SfxVolumeChanger(&SfxVolume, Value); + + if(game::IsRunning()) + { + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverythingNoBlit(); + } +} #ifndef __DJGPP__ @@ -1208,6 +1248,7 @@ void ivanconfig::Initialize() configsystem::AddOption(fsCategory,&MIDIOutputDevice); configsystem::AddOption(fsCategory,&Volume); + configsystem::AddOption(fsCategory,&SfxVolume); fsCategory="Input and Interface"; configsystem::AddOption(fsCategory,&DirectionKeyMap); diff --git a/Main/Source/message.cpp b/Main/Source/message.cpp index c54bf14c7..2e1e4392d 100644 --- a/Main/Source/message.cpp +++ b/Main/Source/message.cpp @@ -328,6 +328,7 @@ festring getstr(FILE *f, truth word) } } +FILE *debf = NULL; void soundsystem::initSound() { const char *error; @@ -336,7 +337,7 @@ void soundsystem::initSound() if(SoundState == 0) { festring fsSndDbgFile = game::GetUserDataDir() + "ivanSndDebug.txt"; - FILE *debf = fopen(fsSndDbgFile.CStr(), "wt"); //"a"); + debf = fopen(fsSndDbgFile.CStr(), "wt"); //"a"); if(debf)fprintf(debf, "This file can be used to diagnose problems with sound.\n"); if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 8000) != 0) @@ -499,13 +500,14 @@ void soundsystem::playSound(festring Buffer) if(*sf->chunk) { - for(int i=0; i<16; i++) + for(int iChannel=0; iChannel<16; iChannel++) { - if(!Mix_Playing(i)) + if(!Mix_Playing(iChannel)) { - Mix_PlayChannel(i, *sf->chunk, 0); -// fprintf(debf, "Mix_PlayChannel(%d, \"%s\", 0);\n", i, sf->filename.CStr()); - // Mix_SetPosition(i, angle, dist); + Mix_Volume(iChannel, ivanconfig::GetSfxVolume()); + Mix_PlayChannel(iChannel, *sf->chunk, 0); + //TODO why this causes SEGFAULT?!! -> //DBGEXEC( fprintf(debf, "Mix_PlayChannel(%d, \"%s\", 0);\n", iChannel, sf->filename.CStr()) ); + //TODO? Mix_SetPosition(i, angle, dist); return; } } From 30eded77865f15cda24fbd3a392517be2f3e4e15 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 14:50:52 -0300 Subject: [PATCH 081/235] WIP-fix MIDI transition (initial tests are ok): some music transitions leave MIDI in wrong state (keeps playing some single instrument note until next music is played)fix to some music transitions that leave MIDI in wrong state (keeps playing some single instrument note until next music is played). praying/favour: updated fsLastKnownRelation for Pray() and Favour() commands too. updated my devsPrefs; --- .../nbproject/configurations.xml | 62 +++++++++++++++++++ Main/Source/dungeon.cpp | 7 +-- Main/Source/god.cpp | 9 ++- Main/Source/gods.cpp | 8 ++- audio/audio.cpp | 1 + 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 47597a70f..491efccfb 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -2,12 +2,27 @@ + MIDICodes.h + MIDIDebug.cpp + MIDIDebug.h + MIDIUtils.cpp + MIDIUtils.h + RtMidi.cpp + audio.cpp + audio_stack.cpp + linkedlist.cpp + midiparser.cpp + midiplayback.cpp + example.cc + namegen.cc + dungeon.h + bitmap.h felist.h feloops.h femath.h @@ -94,6 +109,13 @@ wterras.cpp + + config.h + xbrz.cpp + xbrz.h + + libxbrzscale.cpp + libxbrzscale.h ${CMAKE} -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${IDE_CC} -DCMAKE_CXX_COMPILER=${IDE_CXX} -DCMAKE_C_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_CXX_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . + + @@ -213,6 +237,8 @@ + + @@ -327,6 +353,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp index c1b4869fe..f53c5b24f 100644 --- a/Main/Source/dungeon.cpp +++ b/Main/Source/dungeon.cpp @@ -150,7 +150,7 @@ void dungeon::PrepareMusic(int Index) if( hasCurrentTrack == true ) { - audio::ClearMIDIPlaylist(CurrentTrack); + audio::ClearMIDIPlaylist(CurrentTrack); //keep current track for( int i = 0; i < LevelScript->GetAudioPlayList()->Size; ++i ) { festring Music = LevelScript->GetAudioPlayList()->Data[i]; @@ -167,7 +167,7 @@ void dungeon::PrepareMusic(int Index) if( hasCurrentTrack == false ) { audio::SetPlaybackStatus(audio::STOPPED); - audio::ClearMIDIPlaylist(); + audio::ClearMIDIPlaylist(); //clear it all for( int i = 0; i < LevelScript->GetAudioPlayList()->Size; ++i ) { festring Music = LevelScript->GetAudioPlayList()->Data[i]; @@ -176,9 +176,6 @@ void dungeon::PrepareMusic(int Index) audio::SetPlaybackStatus(audio::PLAYING); } - - - } void dungeon::SaveLevel(cfestring& SaveName, int Number, truth DeleteAfterwards) diff --git a/Main/Source/god.cpp b/Main/Source/god.cpp index 2fcbc94c4..e4c7b9220 100644 --- a/Main/Source/god.cpp +++ b/Main/Source/god.cpp @@ -21,7 +21,7 @@ int god::GetBasicAlignment() const { return NEUTRAL; } void god::Pray() { LastPray = 0; - if(!Timer) + if(!Timer){ if(Relation >= -RAND_N(500)) { ADD_MESSAGE("You feel %s is pleased.", GetName()); @@ -79,7 +79,7 @@ void god::Pray() game::ApplyDivineAlignmentBonuses(this, 10, false); PLAYER->EditExperience(WISDOM, -50, 1 << 10); } - else + }else{ if(Relation > RAND_N(500) && Timer < RAND_N(500000)) { ADD_MESSAGE("You feel %s is displeased, but tries to help you anyway.", GetName()); @@ -109,6 +109,9 @@ void god::Pray() ADD_MESSAGE("%s seems to be hostile.", Angel->CHAR_DESCRIPTION(DEFINITE)); } } + } + + fsLastKnownRelation = PrintRelation(); } festring god::GetCompleteDescription() const @@ -356,6 +359,7 @@ truth god::ReceiveOffer(item* Sacrifice) ADD_MESSAGE("%s seems not to appreciate your gift at all.", GetName()); fsLastKnownRelation = PrintRelation(); + int RandModifier = Sacrifice->GetAttachedGod() == GetType() ? 50 : 100; if(OfferValue > 0 && Relation > 250 && !(RAND() % RandModifier)) @@ -582,6 +586,7 @@ bool god::Favour(int iWhat, int iDebit) if(Relation < 0){ ADD_MESSAGE("%s ignores your plea and makes sure you understand it...",GetName()); PrayBadEffect(); + fsLastKnownRelation = PrintRelation(); return false; } diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 43f9e288f..47faeec3d 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -267,14 +267,16 @@ bool god::CallFavour(CallFavourType call, int iCallFavour, int iWhat, int iDebit if(iDebit>0) if(!god::Favour(iWhat,iDebit)) return false; - + + bool bWorked = false; if((*call)(this)){ if(iDebit>0) Relation-=iDebit; - return true; + bWorked = true; } - return false; + fsLastKnownRelation = PrintRelation(); + return bWorked; } /** diff --git a/audio/audio.cpp b/audio/audio.cpp index 4e278a012..efb3efd18 100644 --- a/audio/audio.cpp +++ b/audio/audio.cpp @@ -158,6 +158,7 @@ int audio::Loop(void *ptr) festring MusFile = MusDir + CurrentTrack; + MPB_ResetMIDI(); // fix to some music transitions that leave MIDI in wrong state (keeps playing some single instrument note until next music is played) PlayMIDIFile(MusFile, 1); } isTrackPlaying = false; From 3c6023128598c633d9bd55d5947382f0d3c114e3 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 17:22:41 -0300 Subject: [PATCH 082/235] Favours: optional show relation on favours list too; WizAIautoPlay: improving navigation when AI can't decide what to do anymore; --- Main/Source/char.cpp | 32 +++++++++++++++++++++++++++----- Main/Source/command.cpp | 1 + 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 6048856f0..7ab6ad6e0 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3113,6 +3113,10 @@ int character::AutoPlayAIFindWalkDist(v2 v2To) return iDist>0?iDist:iMoreThanMaxDist; } +const int iDesperateResetCountDownDefault=5; +const int iDesperateEarthQuakeCountDownDefault=iDesperateResetCountDownDefault*2; +const int iAutoPlayAIResetCountDownDefault = iDesperateEarthQuakeCountDownDefault*2; +int iAutoPlayAIResetCountDown = iAutoPlayAIResetCountDownDefault; truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) { /** @@ -3311,8 +3315,12 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) iWanderTurns=100+clock()%300; DBG2("WanderALotOnFullyExploredLevel",iWanderTurns); //just move around a lot, some NPC may spawn }else{ // travel between dungeons if current fully explored - v2 v2Try = v2Exits[clock()%v2Exits.size()]; - if(AutoPlayAITestValidPathTo(v2Try)) + v2 v2Try; + for(int i=0;i<10;i++){ + v2Try = v2Exits[clock()%v2Exits.size()]; + if(v2Try!=GetPos())break; + } + if(AutoPlayAITestValidPathTo(v2Try) || iAutoPlayAIResetCountDown==0) v2NewKGTo = v2TravelingToAnotherDungeon = v2Try; DBGSV2(v2TravelingToAnotherDungeon); } }else{ @@ -3557,7 +3565,18 @@ truth character::AutoPlayAICommand(int& rKey) /** * travel between dungeons */ - if(!v2TravelingToAnotherDungeon.Is0() && GetPos() == v2TravelingToAnotherDungeon){ + bool bTBD = false; + if(!v2TravelingToAnotherDungeon.Is0()){ + if(GetPos() == v2TravelingToAnotherDungeon) + bTBD=true; + else + if(iAutoPlayAIResetCountDown==0){ + Move(v2TravelingToAnotherDungeon,true); + iAutoPlayAIResetCountDown=iAutoPlayAIResetCountDownDefault; + bTBD=true; + } + } + if(bTBD){ bool bTravel=false; lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(v2TravelingToAnotherDungeon); // square* sqr = Area->GetSquare(v2TravelingToAnotherDungeon); @@ -3581,8 +3600,6 @@ truth character::AutoPlayAICommand(int& rKey) } } - static const int iDesperateResetCountDownDefault=10; - static const int iDesperateEarthQuakeCountDownDefault=iDesperateResetCountDownDefault*5; static int iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; if(AutoPlayAINavigateDungeon(bPlayerHasLantern)){ iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; @@ -3590,6 +3607,10 @@ truth character::AutoPlayAICommand(int& rKey) }else{ if(iDesperateEarthQuakeCountDown==0){ iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; + /** + * this changes the dungeon level paths, + * so applying pickaxe or using fireballs etc are not required! + */ scrollofearthquake::Spawn()->FinishReading(this); DBG1("UsingTerribleEarthquakeSolution"); // xD }else{ @@ -3609,6 +3630,7 @@ truth character::AutoPlayAICommand(int& rKey) iDesperateResetCountDown=iDesperateResetCountDownDefault; AutoPlayAIReset(true); + iAutoPlayAIResetCountDown--; // AFTER THE RESET!!! iWanderTurns=iMaxWanderTurns; DBG2("DesperateResetToSeeIfAIWorksAgain",iWanderTurns); diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index b4455a371..9ee5c414e 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1178,6 +1178,7 @@ truth commandsystem::AskFavour(character* Char) festring fs = CONST_S("")+ game::GetAlignment(pgod->GetAlignment())+" "+ pgod->GetName()+" may grant you a \""+god::GetFavourName(*piFavour)+"\" favour."; + if(ivanconfig::IsShowGodInfo())fs << " " << game::GetGod(c)->GetLastKnownRelation(); col16 col = bOk ? LIGHT_GRAY : DARK_GRAY; if(!bOk && game::WizardModeIsReallyActive())col=RED; From f6a587bf83ec3b657bf0421c19ee11737cabe7a0 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 18:38:16 -0300 Subject: [PATCH 083/235] Favours list looks better now (icons and god sections); character::TakeHit() quite rare (?) SIGFPE workaround; --- Main/Source/char.cpp | 17 ++++++++++++++--- Main/Source/command.cpp | 16 +++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 7ab6ad6e0..56c64a9c1 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -769,9 +769,20 @@ int character::TakeHit(character* Enemy, item* Weapon, /* Effectively, the average chance to hit is 100% / (DV/THV + 1). */ - if(RAND() % int(100 + ToHitValue / DodgeValue * (100 + Success)) < 100 - && !Critical && !ForceHit) - { + /** + * SIGFPE happened once when: + * ToHitValue = -2.1331964070645735 (why < 0 ???); + * DodgeValue = 2.3094010767585029; + * Success = 8; + * result thru linux `bc <<< "scale=16;100 + -2.1331964070645735/2.3094010767585029 * (100+8)"` + * was = .2402768919010060 + */ + int a = int(100 + ToHitValue / DodgeValue * (100 + Success)); + if( + ((a>=-1 && a<=1) || ((RAND() % a) < 100)) && + !Critical && + !ForceHit + ){ Enemy->AddMissMessage(this); EditExperience(AGILITY, 150, 1 << 7); EditExperience(PERCEPTION, 75, 1 << 7); diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 9ee5c414e..53ca8c209 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1161,6 +1161,7 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng truth commandsystem::AskFavour(character* Char) { felist felFavourList(CONST_S("Ask a favour from Whom?")); + felFavourList.SetEntryDrawer(game::GodEntryDrawer); int iTot=0; std::vector> vSelectableFavours; @@ -1173,17 +1174,22 @@ truth commandsystem::AskFavour(character* Char) if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; if(c == Char->GetLSquareUnder()->GetDivineMaster())bOk=true; + bool bGodSectionEntry=true; std::vector v = pgod->GetKnownSpells(); for(auto piFavour = v.begin(); piFavour != v.end(); ++piFavour){ - festring fs = CONST_S("")+ - game::GetAlignment(pgod->GetAlignment())+" "+ - pgod->GetName()+" may grant you a \""+god::GetFavourName(*piFavour)+"\" favour."; - if(ivanconfig::IsShowGodInfo())fs << " " << game::GetGod(c)->GetLastKnownRelation(); + festring fsFavour = CONST_S("") + god::GetFavourName(*piFavour); col16 col = bOk ? LIGHT_GRAY : DARK_GRAY; if(!bOk && game::WizardModeIsReallyActive())col=RED; - felFavourList.AddEntry(fs, col, 0, NO_IMAGE, bOk || game::WizardModeIsReallyActive()); + if(bGodSectionEntry){ + festring fsGodEntry = CONST_S("") + game::GetAlignment(pgod->GetAlignment()) + " " + + pgod->GetName()+" may grant you a favour."; + if(ivanconfig::IsShowGodInfo())fsGodEntry << " " << game::GetGod(c)->GetLastKnownRelation(); + felFavourList.AddEntry(fsGodEntry, DARK_GRAY, 20, c, false); + bGodSectionEntry=false; + } + felFavourList.AddEntry(fsFavour, col, 0, NO_IMAGE, bOk || game::WizardModeIsReallyActive()); if(bOk || game::WizardModeIsReallyActive()){ // std::pair GS; From e9e8f6bbaab99f2e18cbb5977cb2c1f56d58dee8 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 19:05:52 -0300 Subject: [PATCH 084/235] Favours: hides unknown gods; scrollofearthquake: fixed: at Spider Nest could fail to find any wall and crash; --- Main/Source/command.cpp | 1 + Main/Source/miscitem.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 53ca8c209..49677f6b0 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1167,6 +1167,7 @@ truth commandsystem::AskFavour(character* Char) std::vector> vSelectableFavours; for(int c = 1; c <= GODS; ++c){ god* pgod = game::GetGod(c); + if(!pgod->IsKnown())continue; bool bOk=false; if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0)bOk=true; diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index 72e88b3b8..60d2d1e4d 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -202,10 +202,12 @@ void scrollofearthquake::FinishReading(character* Reader) int ToEmpty = 10 + RAND() % 11; - for(c = 0; c < ToEmpty; ++c) + for(c = 0; c < ToEmpty; ++c){ for(int i = 0; i < 50; ++i) { v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE); + if(Pos == ERROR_V2)continue; //this may probably happen at Spider Nest dungeon level + truth Correct = false; for(int d = 0; d < 8; ++d) @@ -225,6 +227,7 @@ void scrollofearthquake::FinishReading(character* Reader) break; } } + } int ToGround = 20 + RAND() % 21; From d637c02864cab619ef652fa3d55264bf2a1526ba Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 20:15:55 -0300 Subject: [PATCH 085/235] rework DEVCMDMSG to cope with LGTM; --- Main/Include/devcons.h | 7 ++++++- Main/Source/bugworkaround.cpp | 8 ++++---- Main/Source/devcons.cpp | 14 ++++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Main/Include/devcons.h b/Main/Include/devcons.h index e62c73539..6fb90b013 100644 --- a/Main/Include/devcons.h +++ b/Main/Include/devcons.h @@ -17,7 +17,12 @@ #include "festring.h" -#define DEVCMDMSG(fmt,x,...) ADD_MESSAGE(" > " fmt,x); +// this causes warnings with LGTM checks: #define DEVCMDMSG(fmt,x,...) ADD_MESSAGE(" > " fmt,x); +#define DEVCMDMSG(fmt,x) ADD_MESSAGE(" > " fmt,x); +#define DEVCMDMSG2(fmt,x,y) ADD_MESSAGE(" > " fmt,x,y); +#define DEVCMDMSG3(fmt,x,y,z) ADD_MESSAGE(" > " fmt,x,y,z); +#define DEVCMDMSG4(fmt,x,y,z,a) ADD_MESSAGE(" > " fmt,x,y,z,a); +#define DEVCMDMSG15(fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) ADD_MESSAGE(" > " fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o); typedef void (*callcmd)(festring); diff --git a/Main/Source/bugworkaround.cpp b/Main/Source/bugworkaround.cpp index 491073b45..5846370ba 100644 --- a/Main/Source/bugworkaround.cpp +++ b/Main/Source/bugworkaround.cpp @@ -387,7 +387,7 @@ void bugfixdp::DupPlayerFix(character* DupPlayer) DEVCMDMSG("fixed bodyparts %d",iFixedCount); //BEWARE!!! this leads to crash: DupPlayer->Remove(); - DEVCMDMSG("fixed dup player '%s' id=%d/%d 0x%X",DupPlayer->GetName(DEFINITE).CStr(),idOld,DupPlayer->GetID(),DupPlayer); + DEVCMDMSG4("fixed dup player '%s' id=%d/%d 0x%X",DupPlayer->GetName(DEFINITE).CStr(),idOld,DupPlayer->GetID(),DupPlayer); DBGCHAR(DupPlayer,"CharFix:CharToBeLost"); } @@ -575,7 +575,7 @@ character* bugfixdp::FindByPlayerID1(v2 ReqPosL,bool bAndFixIt) CharPlayerOk = CharID1; DEVCMDMSG("%s","ID1 will be ok now"); if(bAndFixIt && CharID1->GetSquareUnder()==NULL){ - DEVCMDMSG("placing the character ID1 at %d,%d",ReqPosL.X,ReqPosL.Y); + DEVCMDMSG2("placing the character ID1 at %d,%d",ReqPosL.X,ReqPosL.Y); CharID1->PutToOrNear(ReqPosL); //place he where expected } // } @@ -640,9 +640,9 @@ character* bugfixdp::BugWorkaroundDupPlayer(){ // last thing is grant player's stuff is consistent int iFixedCount=0; iFixedCount=CharEquipmentsWork(CharPlayerOk,true,false);DBGLN; - DEVCMDMSG("fixed player '%s' equipments %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); + DEVCMDMSG2("fixed player '%s' equipments %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); iFixedCount=CharInventoryWork (CharPlayerOk,true,false);DBGLN; - DEVCMDMSG("fixed player '%s' inventory %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); + DEVCMDMSG2("fixed player '%s' inventory %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); iFixedCount=TrapsWork(); DEVCMDMSG("fixed traps %d",iFixedCount); diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index be7110bac..bc4a61ae0 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -100,7 +100,7 @@ void ListItems(festring fsParams){ } } - DEVCMDMSG("params: %d %d",idFilter,idCharFilter); + DEVCMDMSG2("params: %d %d",idFilter,idCharFilter); itemidmap map = game::GetItemIDMapCopy(); for(itemidmap::iterator itr = map.begin();itr!=map.end();itr++){ @@ -118,7 +118,7 @@ void ListItems(festring fsParams){ it->GetSquaresUnder()>100 || //something improbable, could be just 8 I guess... false ){ - DEVCMDMSG("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened? + DEVCMDMSG2("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened? } if(idFilter!=0 && idFilter!=it->GetID()) @@ -181,7 +181,7 @@ void ListItems(festring fsParams){ fsPos<GetPos().X<<","<GetPos().Y; } - DEVCMDMSG("%sid=%d (%s) '%s' owner '%d/%s' '%d/%s' (%s%s%s).", + DEVCMDMSG15("%sid=%d (%s) '%s' owner '%d/%s' '%d/%s' (%s%s%s).", bPlayerStuff?"@":" ", it->GetID(), @@ -197,7 +197,9 @@ void ListItems(festring fsParams){ fsType.CStr(), fsPB.CStr(), - fsDec.CStr() + fsDec.CStr(), + + 0,0,0,0 //dummy ); } } @@ -332,7 +334,7 @@ void devcons::Help(festring fsFilter) fsWM=""; } } - DEVCMDMSG("%s - %s%s",vCmd[j].fsCmd.CStr(),fsWM.CStr(),vCmd[j].fsHelp.CStr()); + DEVCMDMSG3("%s - %s%s",vCmd[j].fsCmd.CStr(),fsWM.CStr(),vCmd[j].fsHelp.CStr()); } // ADD_MESSAGE("%sPs.: main commands are case insensitive.",cPrompt); DEVCMDMSG("%s","Ps.: main commands are case insensitive."); @@ -367,7 +369,7 @@ void devcons::runCommand(festring fsFullCmd) } // ADD_MESSAGE("%sTrying to run: %s ('%s' '%s')",cPrompt,strFullCmd.c_str(),strCmd.c_str(),strParams.c_str()); - DEVCMDMSG("Trying to run: %s ('%s' '%s')", + DEVCMDMSG3("Trying to run: %s ('%s' '%s')", festring(strFullCmd.c_str()).CStr(), festring(strCmd.c_str()).CStr(), festring(strParams.c_str()).CStr() From ed7f7ab114bf9b8f5be5e910bef3d9bcd4eb81ad Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 21:52:19 -0300 Subject: [PATCH 086/235] WizAutoPlay: Improving AI; --- Main/Source/char.cpp | 28 ++++++++++++++++++++++++---- Main/Source/human.cpp | 20 ++++++++++++++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 56c64a9c1..001b473a9 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3046,8 +3046,8 @@ bool character::IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern) if(!it->IsPickable(this))return false; if(it->GetSquaresUnder()!=1)return false; //avoid big corpses 2x2 - if(!bPlayerHasLantern && it->IsOnFire(this)){ - //ok + if(!bPlayerHasLantern && (it->IsOnFire(this) || it->GetEmitation()>0)){ + //pickup priority }else{ if(it->IsBroken())return false; if(it->GetTruePrice()<=iMaxValueless)return false; //mainly to avoid all rocks from broken walls @@ -3134,6 +3134,7 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) * navigate the unknown dungeon */ std::vector v2Exits; + std::vector v2Altars; if(v2KeepGoingTo.Is0()){ DBG1("TryNewMoveTarget"); // target undiscovered squares to explore v2 v2PreferedTarget(0,0); @@ -3161,6 +3162,12 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) if(olt && (olt->GetConfig() == STAIRS_UP || olt->GetConfig() == STAIRS_DOWN)){ v2Exits.push_back(v2(lsqr->GetPos())); DBGSV2(v2Exits[v2Exits.size()-1]); } + + altar* Altar = dynamic_cast(olt); + if(olt && Altar){ + if(!Altar->GetMasterGod()->IsKnown()) + v2Altars.push_back(v2(lsqr->GetPos())); + } stack* stkSqr = lsqr->GetStack(); static itemvector vit;vit.clear();stkSqr->FillItemVector(vit); @@ -3221,8 +3228,10 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) vit[n]->IsWeapon (this) || vit[n]->IsArmor (this) || vit[n]->IsAmulet (this) || - vit[n]->IsZappable(this) || + vit[n]->IsZappable(this) || //wands vit[n]->IsRing (this) || + vit[n]->IsReadable(this) || //books and scrolls + vit[n]->IsDrinkable(this)|| //potions and vials bIsLanternOnFloor ) if(IsAutoplayAICanPickup(vit[n],bPlayerHasLantern)) @@ -3321,6 +3330,17 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) } if(v2NewKGTo.Is0()){ //no new destination: fully explored + if(v2Altars.size()>0){ + for(int i=0;i0){ if(game::GetCurrentDungeonTurnsCount()==0){ DBG1("Dungeon:FullyExplored:FirstTurn"); iWanderTurns=100+clock()%300; DBG2("WanderALotOnFullyExploredLevel",iWanderTurns); //just move around a lot, some NPC may spawn @@ -3522,7 +3542,7 @@ truth character::AutoPlayAICommand(int& rKey) truth bPlayerHasLantern=false; static itemvector vit;vit.clear();GetStack()->FillItemVector(vit); for(uint i=0;i(vit[i])!=NULL || vit[i]->IsOnFire(this)){ + if(dynamic_cast(vit[i])!=NULL || vit[i]->IsOnFire(this) || vit[i]->GetEmitation()>0){ bPlayerHasLantern=true; //will keep only the 1st lantern break; } diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index a6d1fa215..8f8310943 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -3795,7 +3795,7 @@ truth humanoid::AutoPlayAIequip() } } - //////////////////////////////// read book + //////////////////////////////// read books and scrolls static int iLastReadTurn=-1; if(game::GetTurn()>(iLastReadTurn+50)){ DBG2(game::GetTurn(),iLastReadTurn); //every X turns try to use stuff from inv iLastReadTurn=game::GetTurn(); @@ -3803,10 +3803,22 @@ truth humanoid::AutoPlayAIequip() static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); for(uint c = 0; c < vitEqW.size(); ++c){ if(!vitEqW[c]->IsReadable(this))continue; + static holybook* hb;hb = dynamic_cast(vitEqW[c]); - if(hb==NULL)continue; - - if(vitEqW[c]->Read(this)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs + if(hb){ + if(vitEqW[c]->Read(this)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs + return true; + } + } + + static scroll* Scroll; Scroll = dynamic_cast(vitEqW[c]); + if( //some are simple (just read to work imediately) + dynamic_cast(Scroll) || + dynamic_cast(Scroll) || + dynamic_cast(Scroll) || + false //dummy + ){ + Scroll->Read(this); return true; } } From 04a0d93fe7b1bcc095d46a48230c912f5027aa5f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 5 Apr 2020 23:34:42 -0300 Subject: [PATCH 087/235] CPUwiseAI() improvements: magic mushroom clouds animations (or it's other calculations) are the real source of the lag. Inproved help info about magic clouds lag so player knows what to do to fix the lag. The AI limit now considers levitating (mainly) rooted magic mushrooms too, to grant performance. --- Main/Include/iconf.h | 2 +- Main/Source/iconf.cpp | 17 ++++++----------- Main/Source/nonhuman.cpp | 24 ++++++++++++++++++------ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index 15ccb9226..f2ef7d9e3 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -47,7 +47,7 @@ class ivanconfig static truth IsWaitNeutralsMoveAway() { return WaitNeutralsMoveAway.Value; } static truth IsEnhancedLights() { return EnhancedLights.Value; } static int GetMemorizeEquipmentMode() { return 2; /*MemorizeEquipmentMode.Value;*/ } - static int GetDistLimitMagicMushrooms() { return DistLimitMagicMushrooms.Value * 4; } + static int GetDistLimitMagicMushrooms() { return DistLimitMagicMushrooms.Value; } static truth IsShowFullDungeonName() { return ShowFullDungeonName.Value; } static truth IsCenterOnPlayerAfterLook(){ return CenterOnPlayerAfterLook.Value; } static truth IsShowGodInfo(){ return ShowGodInfo.Value; } diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 06fb5496d..2d063cb97 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -255,7 +255,7 @@ cycleoption ivanconfig::FontGfx( "FontGfx", &FontGfxChanger); cycleoption ivanconfig::DistLimitMagicMushrooms("DistLimitMagicMushrooms", "Breeders' active range", - "Select the maximum distance where breeding monsters will spawn more of their own. This option can be used to prevent lag from high number of creatures on slow computers, but may impact the intended game balance negatively.", + "Select the maximum distance where breeding monsters will spawn more of their own. This option can be used to prevent lag from high number of creatures on slow computers, but may impact the intended game balance negatively. After you lower this option value to the minimum, you have to wait for all magic clouds (from magic mushrooms) to disappear as they are the main source of lag.", 0, 5, &DistLimitMagicMushroomsDisplayer); cycleoption ivanconfig::SaveGameSortMode( "SaveGameSortMode", @@ -394,16 +394,11 @@ void ivanconfig::FrameSkipDisplayer(const numberoption* O, festring& Entry) void ivanconfig::DistLimitMagicMushroomsDisplayer(const cycleoption* O, festring& Entry) { - if(O->Value == 0) - Entry << "everywhere"; - else if(O->Value == 1) - Entry << "close"; - else if(O->Value == 2) - Entry << "near"; - else if(O->Value == 3) - Entry << "medium"; - else if(O->Value == 4) - Entry << "far"; + if (O->Value == 0) Entry << "everywhere"; + else if(O->Value == 1) Entry << "close"; + else if(O->Value == 2) Entry << "near"; + else if(O->Value == 3) Entry << "medium"; + else if(O->Value == 4) Entry << "far"; else Entry << O->Value; } diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp index 326d97d51..538f1d115 100644 --- a/Main/Source/nonhuman.cpp +++ b/Main/Source/nonhuman.cpp @@ -1219,14 +1219,19 @@ void mushroom::PostConstruct() * This is not gameplay wise as far AI events will not happen, * but will allow the game to still be playable at least... * Use this on any NPC class that may encumber the CPU too much. + * + * TODO Confirm if the main lag problem is related to magic clouds *animations* + * or other magic cloud calculations? If the problem is about animations, + * non visible (or simply far away) ones could just be skipped w/o problem! */ bool CPUwiseAI(nonhumanoid* nh) { if(!nh->IsRooted())return true; //only NPCs that can't move - if(nh->StateIsActivated(LEVITATION))return true; //this keeps levitating ones still active what may be good TODO add user option to deny them? int iDist = ivanconfig::GetDistLimitMagicMushrooms(); - if(iDist==0)return true; + if(iDist==0)return true; //everywhere allowed + + iDist*=4; // this gives min AI = 64, max = 1024 but the lag seems related to magic clouds (non visible animations?) from magic mushrooms, as soon the clouds vanish, lag goes away too int iSqDist = nh->GetDistanceSquareFrom(PLAYER); int iSqLim = iDist*iDist; @@ -1234,16 +1239,23 @@ bool CPUwiseAI(nonhumanoid* nh) static int iPreviousTurnActivatedAIs=0; static int iTurnChkAI = 0; - if(iTurnChkAI != game::GetTurn()){ DBG4(iTurnChkAI,iPreviousTurnActivatedAIs,iSqLim,iMaxActiveAI); + if(iTurnChkAI != game::GetTurn()){ DBG6(iTurnChkAI,iPreviousTurnActivatedAIs,iDist,iSqDist,iSqLim,iMaxActiveAI); iPreviousTurnActivatedAIs=0; iTurnChkAI = game::GetTurn(); } bool bActivated = false; if(iTurnChkAI==game::GetTurn()){ - if(iPreviousTurnActivatedAIsStateIsActivated(LEVITATION)){ + iPreviousTurnActivatedAIs++; + bActivated=true; + } } } From 730c6ee63f04882dfb803c96ff46b5067631c095 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 6 Apr 2020 00:08:52 -0300 Subject: [PATCH 088/235] God's earth quake magic and related scroll magic were a copy/paste code, unified them to grant the fix in one place. --- Main/Include/miscitem.h | 1 + Main/Source/gods.cpp | 109 +-------------------------------------- Main/Source/miscitem.cpp | 10 ++-- 3 files changed, 10 insertions(+), 110 deletions(-) diff --git a/Main/Include/miscitem.h b/Main/Include/miscitem.h index 1b62ab8cf..5c87cd068 100644 --- a/Main/Include/miscitem.h +++ b/Main/Include/miscitem.h @@ -203,6 +203,7 @@ ITEM(scrollofearthquake, scroll) { public: virtual void FinishReading(character*); + static void EarthQuakeMagic(); }; ITEM(scrollofbodyswitch, scroll) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 47faeec3d..0cf697796 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -813,113 +813,8 @@ bool FavourCallRain(god* G) bool FavourEarthQuake(god* G) { - ADD_MESSAGE("Suddenly a horrible earthquake shakes the level."); - int c, Tunnels = 2 + RAND() % 3; - if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) - Tunnels = 0; - - for(c = 0; c < Tunnels; ++c) - game::GetCurrentLevel()->AttachPos(game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE)); - - int ToEmpty = 10 + RAND() % 11; - - for(c = 0; c < ToEmpty; ++c) - for(int i = 0; i < 50; ++i) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE); - truth Correct = false; - - for(int d = 0; d < 8; ++d) - { - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); - - if(Square && Square->IsFlyable()) - { - Correct = true; - break; - } - } - - if(Correct) - { - game::GetCurrentLevel()->GetLSquare(Pos)->ChangeOLTerrainAndUpdateLights(0); - break; - } - } - - int ToGround = 20 + RAND() % 21; - - for(c = 0; c < ToGround; ++c) - for(int i = 0; i < 50; ++i) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, RAND() & 1 ? 0 : HAS_CHARACTER); - - if(Pos == ERROR_V2) - continue; - - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); - character* Char = Square->GetCharacter(); - - if(Square->GetOLTerrain() || (Char && (Char->IsPlayer() || PLAYER->GetRelation(Char) != HOSTILE))) - continue; - - int Walkables = 0; - - for(int d = 0; d < 8; ++d) - { - lsquare* NearSquare = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); - - if(NearSquare && NearSquare->IsFlyable()) - ++Walkables; - } - - if(Walkables > 6) - { - Square->ChangeOLTerrainAndUpdateLights(earth::Spawn()); - - if(Char) - { - if(Char->CanBeSeenByPlayer()) - ADD_MESSAGE("%s is hit by a rock falling from the ceiling!", Char->CHAR_NAME(DEFINITE)); - - Char->ReceiveDamage(0, 20 + RAND() % 21, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); - Char->CheckDeath(CONST_S("killed by an earthquake"), 0); - } - - Square->KickAnyoneStandingHereAway(); - Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 41, PHYSICAL_DAMAGE); - break; - } - } - - // Generate a few boulders in the level - - int BoulderNumber = 10 + RAND() % 10; - - for(c = 0; c < BoulderNumber; ++c) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(); - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); - character* MonsterHere = Square->GetCharacter(); - - if(!Square->GetOLTerrain() && (!MonsterHere || MonsterHere->GetRelation(PLAYER) == HOSTILE)) - { - Square->ChangeOLTerrainAndUpdateLights(boulder::Spawn(1 + (RAND() & 1))); - - if(MonsterHere) - MonsterHere->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); - - Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE); - } - } - - // Damage to items in the level - - for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) - for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) - game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); - - return true; + scrollofearthquake::EarthQuakeMagic(); + return true; } bool FavourSummonWolf(god* G) diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index 60d2d1e4d..588534a91 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -188,10 +188,8 @@ void scrolloffireballs::FinishReading(character* Reader) Square->FireBall(Beam); } -void scrollofearthquake::FinishReading(character* Reader) +void scrollofearthquake::EarthQuakeMagic() { - if(!game::GetCurrentLevel()->IsOnGround()) - { ADD_MESSAGE("Suddenly a horrible earthquake shakes the level!"); int c, Tunnels = 2 + RAND() % 3; if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) @@ -300,6 +298,12 @@ void scrollofearthquake::FinishReading(character* Reader) for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); +} +void scrollofearthquake::FinishReading(character* Reader) +{ + if(!game::GetCurrentLevel()->IsOnGround()) + { + EarthQuakeMagic(); } else { From 81e3b22bb46bf2c660901da7b6e59ea867e98dd5 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 6 Apr 2020 00:28:20 -0300 Subject: [PATCH 089/235] Improved user help about customising every movement and command keys. It's usage is still a bit difficult tho. --- Main/Source/iconf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 2d063cb97..7f1dffe2b 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -288,8 +288,8 @@ cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", DIR_NORM, 3, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SetupCustomKeys( "SetupCustomKeys", - "Movement control custom keys", //TODO all keys one day, and let it work on main menu - "Let you assign all 8 movement keys to any available key of your preference. All other command keys will remain as assigned by the scheme above. This global configuration won't work at main menu, load/start some game.", + "* Movement control custom keys", //TODO all keys one day, and let it work on main menu + "Let you assign all 8 movement keys to any available key of your preference. All other command keys will remain as assigned by the scheme above until you change them externally on the newly generated config file and restart the game. This global configuration won't work at main menu, load/start some game.", false, &configsystem::NormalTruthDisplayer, &configsystem::NormalTruthChangeInterface, From a11b08493d732804472a376e2cdb1266a60d1bb2 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 6 Apr 2020 00:42:08 -0300 Subject: [PATCH 090/235] WizAutoPlay: improved, a chance to learn about all gods if none is known; --- Main/Source/char.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 001b473a9..4b473139b 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3496,15 +3496,20 @@ truth character::AutoPlayAIPray() aiKGodsP[iKGTotP++] = c; } } - if(iKGTot==0)return false; + if(iKGTot==0){ + if(clock()%10==0) + for(int c = 1; c <= GODS; ++c) + game::LearnAbout(game::GetGod(c)); + return false; + } // if(bSPO && iKGTotP==0)return false; // chose and pray to one god god* g = NULL; if(iKGTotP>0 && (bSPO || clock()%10!=0)) - g = game::GetGod(aiKGodsP[clock()%iKGTotP]); + g = game::GetGod(aiKGodsP[clock()%iKGTotP]); //only good effect else - g = game::GetGod(aiKGods[clock()%iKGTot]); + g = game::GetGod(aiKGods[clock()%iKGTot]); //can have bad effect too if(bSPO || clock()%10!=0){ //it may not recover some times to let pray unsafely int iRecover=0; From f6f0b07134e8440f9dba7c0154c546b85cfb806e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 6 Apr 2020 01:24:53 -0300 Subject: [PATCH 091/235] CPUwiseAI() a dungeon filled with normal mushroom* breeders will also lag, so extended that limit to them; --- Main/Source/nonhuman.cpp | 99 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp index 538f1d115..7635f78ee 100644 --- a/Main/Source/nonhuman.cpp +++ b/Main/Source/nonhuman.cpp @@ -1137,6 +1137,53 @@ truth eddy::Hit(character* Enemy, v2, int, int) return true; } +/** + * This is not gameplay wise as far AI events will not happen, + * but will allow the game to still be playable at least... + * Use this on any NPC class that may encumber the CPU too much. + * + * TODO Confirm if the main lag problem is related to magic clouds *animations* + * or other magic cloud calculations? If the problem is about animations, + * non visible (or simply far away) ones could just be skipped w/o problem! + */ +bool CPUwiseAI(nonhumanoid* nh) +{ + if(!nh->IsRooted())return true; //only NPCs that can't move + + int iDist = ivanconfig::GetDistLimitMagicMushrooms(); + if(iDist==0)return true; //everywhere allowed + + iDist*=4; // this gives min AI = 64, max = 1024 but the lag seems related to magic clouds (non visible animations?) from magic mushrooms, as soon the clouds vanish, lag goes away too + + int iSqDist = nh->GetDistanceSquareFrom(PLAYER); + int iSqLim = iDist*iDist; + int iMaxActiveAI = iDist*2 * iDist*2; + + static int iPreviousTurnActivatedAIs=0; + static int iTurnChkAI = 0; + if(iTurnChkAI != game::GetTurn()){ DBG6(iTurnChkAI,iPreviousTurnActivatedAIs,iDist,iSqDist,iSqLim,iMaxActiveAI); + iPreviousTurnActivatedAIs=0; + iTurnChkAI = game::GetTurn(); + } + + bool bActivated = false; + if(iTurnChkAI==game::GetTurn()){ + if(iPreviousTurnActivatedAIsStateIsActivated(LEVITATION)){ + iPreviousTurnActivatedAIs++; + bActivated=true; + } + } + } + + return bActivated; +} + void mushroom::Save(outputfile& SaveFile) const { nonhumanoid::Save(SaveFile); @@ -1151,6 +1198,8 @@ void mushroom::Load(inputfile& SaveFile) void mushroom::GetAICommand() { + if(!CPUwiseAI(this))return; + SeekLeader(GetLeader()); if(FollowLeader(GetLeader())) @@ -1215,57 +1264,9 @@ void mushroom::PostConstruct() } } -/** - * This is not gameplay wise as far AI events will not happen, - * but will allow the game to still be playable at least... - * Use this on any NPC class that may encumber the CPU too much. - * - * TODO Confirm if the main lag problem is related to magic clouds *animations* - * or other magic cloud calculations? If the problem is about animations, - * non visible (or simply far away) ones could just be skipped w/o problem! - */ -bool CPUwiseAI(nonhumanoid* nh) -{ - if(!nh->IsRooted())return true; //only NPCs that can't move - - int iDist = ivanconfig::GetDistLimitMagicMushrooms(); - if(iDist==0)return true; //everywhere allowed - - iDist*=4; // this gives min AI = 64, max = 1024 but the lag seems related to magic clouds (non visible animations?) from magic mushrooms, as soon the clouds vanish, lag goes away too - - int iSqDist = nh->GetDistanceSquareFrom(PLAYER); - int iSqLim = iDist*iDist; - int iMaxActiveAI = iDist*2 * iDist*2; - - static int iPreviousTurnActivatedAIs=0; - static int iTurnChkAI = 0; - if(iTurnChkAI != game::GetTurn()){ DBG6(iTurnChkAI,iPreviousTurnActivatedAIs,iDist,iSqDist,iSqLim,iMaxActiveAI); - iPreviousTurnActivatedAIs=0; - iTurnChkAI = game::GetTurn(); - } - - bool bActivated = false; - if(iTurnChkAI==game::GetTurn()){ - if(iPreviousTurnActivatedAIsStateIsActivated(LEVITATION)){ - iPreviousTurnActivatedAIs++; - bActivated=true; - } - } - } - - return bActivated; -} - void magicmushroom::GetAICommand() { - if(!CPUwiseAI(this)) - return; + if(!CPUwiseAI(this))return; if(!(RAND() % 750)) { From 2f3c31e7cbd0909c2fd6f33a264c52b0d73312b2 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 6 Apr 2020 19:04:27 -0300 Subject: [PATCH 092/235] unified all(?) earthquake copied codes --- Main/Include/miscitem.h | 2 +- Main/Source/human.cpp | 106 +-------------------------- Main/Source/miscitem.cpp | 153 ++++++++++++++++++++------------------- 3 files changed, 79 insertions(+), 182 deletions(-) diff --git a/Main/Include/miscitem.h b/Main/Include/miscitem.h index 5c87cd068..3a7c8451e 100644 --- a/Main/Include/miscitem.h +++ b/Main/Include/miscitem.h @@ -203,7 +203,7 @@ ITEM(scrollofearthquake, scroll) { public: virtual void FinishReading(character*); - static void EarthQuakeMagic(); + static void EarthQuakeMagic(festring fsMsgHitNPC = cfestring()); }; ITEM(scrollofbodyswitch, scroll) diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 8f8310943..3572b8ace 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -6742,112 +6742,8 @@ void xinrochghost::CreateCorpse(lsquare* Square) /*This needs to be a function someday*/ if(!game::GetCurrentLevel()->IsOnGround()) { - ADD_MESSAGE("Suddenly a horrible earthquake shakes the level."); + scrollofearthquake::EarthQuakeMagic("%s is hit by a brick of earth falling from the roof!"); ADD_MESSAGE("You hear an eerie scream: \"Ahh! Free at last! FREE TO BE REBORN!\""); - int c, Tunnels = 2 + RAND() % 3; - if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) - Tunnels = 0; - - for(c = 0; c < Tunnels; ++c) - game::GetCurrentLevel()->AttachPos(game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE)); - - int ToEmpty = 10 + RAND() % 11; - - for(c = 0; c < ToEmpty; ++c) - for(int i = 0; i < 50; ++i) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE); - truth Correct = false; - - for(int d = 0; d < 8; ++d) - { - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); - - if(Square && Square->IsFlyable()) - { - Correct = true; - break; - } - } - - if(Correct) - { - game::GetCurrentLevel()->GetLSquare(Pos)->ChangeOLTerrainAndUpdateLights(0); - break; - } - } - - int ToGround = 20 + RAND() % 21; - - for(c = 0; c < ToGround; ++c) - for(int i = 0; i < 50; ++i) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, RAND() & 1 ? 0 : HAS_CHARACTER); - - if(Pos == ERROR_V2) - continue; - - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); - character* Char = Square->GetCharacter(); - - if(Square->GetOLTerrain() || (Char && (Char->IsPlayer() || PLAYER->GetRelation(Char) != HOSTILE))) - continue; - - int Walkables = 0; - - for(int d = 0; d < 8; ++d) - { - lsquare* NearSquare = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); - - if(NearSquare && NearSquare->IsFlyable()) - ++Walkables; - } - - if(Walkables > 6) - { - Square->ChangeOLTerrainAndUpdateLights(earth::Spawn()); - - if(Char) - { - if(Char->CanBeSeenByPlayer()) - ADD_MESSAGE("%s is hit by a brick of earth falling from the roof!", Char->CHAR_NAME(DEFINITE)); - - Char->ReceiveDamage(0, 20 + RAND() % 21, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); - Char->CheckDeath(CONST_S("killed by an earthquake"), 0); - } - - Square->KickAnyoneStandingHereAway(); - Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 41, PHYSICAL_DAMAGE); - break; - } - } - - // Generate a few boulders in the level - - int BoulderNumber = 10 + RAND() % 10; - - for(c = 0; c < BoulderNumber; ++c) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(); - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); - character* MonsterHere = Square->GetCharacter(); - - if(!Square->GetOLTerrain() && (!MonsterHere || MonsterHere->GetRelation(PLAYER) == HOSTILE)) - { - Square->ChangeOLTerrainAndUpdateLights(boulder::Spawn(1 + (RAND() & 1))); - - if(MonsterHere) - MonsterHere->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); - - Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE); - } - } - - // Damage to items in the level - - for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) - for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) - game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); } } diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index 588534a91..1c19c1bfe 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -188,116 +188,117 @@ void scrolloffireballs::FinishReading(character* Reader) Square->FireBall(Beam); } -void scrollofearthquake::EarthQuakeMagic() +void scrollofearthquake::EarthQuakeMagic(festring fsMsgHitNPC) { - ADD_MESSAGE("Suddenly a horrible earthquake shakes the level!"); - int c, Tunnels = 2 + RAND() % 3; - if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) - Tunnels = 0; + ADD_MESSAGE("Suddenly a horrible earthquake shakes the level!"); + int c, Tunnels = 2 + RAND() % 3; + if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) + Tunnels = 0; - for(c = 0; c < Tunnels; ++c) - game::GetCurrentLevel()->AttachPos(game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE)); + for(c = 0; c < Tunnels; ++c) + game::GetCurrentLevel()->AttachPos(game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE)); - int ToEmpty = 10 + RAND() % 11; + int ToEmpty = 10 + RAND() % 11; - for(c = 0; c < ToEmpty; ++c){ - for(int i = 0; i < 50; ++i) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE); - if(Pos == ERROR_V2)continue; //this may probably happen at Spider Nest dungeon level - - truth Correct = false; + for(c = 0; c < ToEmpty; ++c){ + for(int i = 0; i < 50; ++i) + { + v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE); + if(Pos == ERROR_V2)continue; //this may probably happen at Spider Nest dungeon level - for(int d = 0; d < 8; ++d) - { - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); + truth Correct = false; - if(Square && Square->IsFlyable()) - { - Correct = true; - break; - } - } + for(int d = 0; d < 8; ++d) + { + lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); - if(Correct) + if(Square && Square->IsFlyable()) { - game::GetCurrentLevel()->GetLSquare(Pos)->ChangeOLTerrainAndUpdateLights(0); + Correct = true; break; } } - } - int ToGround = 20 + RAND() % 21; - - for(c = 0; c < ToGround; ++c) - for(int i = 0; i < 50; ++i) + if(Correct) { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, RAND() & 1 ? 0 : HAS_CHARACTER); + game::GetCurrentLevel()->GetLSquare(Pos)->ChangeOLTerrainAndUpdateLights(0); + break; + } + } + } - if(Pos == ERROR_V2) - continue; + int ToGround = 20 + RAND() % 21; - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); - character* Char = Square->GetCharacter(); + for(c = 0; c < ToGround; ++c) + for(int i = 0; i < 50; ++i) + { + v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, RAND() & 1 ? 0 : HAS_CHARACTER); - if(Square->GetOLTerrain() || (Char && (Char->IsPlayer() || PLAYER->GetRelation(Char) != HOSTILE))) - continue; + if(Pos == ERROR_V2) + continue; - int Walkables = 0; + lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); + character* Char = Square->GetCharacter(); - for(int d = 0; d < 8; ++d) - { - lsquare* NearSquare = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); + if(Square->GetOLTerrain() || (Char && (Char->IsPlayer() || PLAYER->GetRelation(Char) != HOSTILE))) + continue; - if(NearSquare && NearSquare->IsFlyable()) - ++Walkables; - } + int Walkables = 0; - if(Walkables > 6) - { - Square->ChangeOLTerrainAndUpdateLights(earth::Spawn()); + for(int d = 0; d < 8; ++d) + { + lsquare* NearSquare = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); - if(Char) - { - if(Char->CanBeSeenByPlayer()) - ADD_MESSAGE("%s is hit by a rock falling from the ceiling!", Char->CHAR_NAME(DEFINITE)); + if(NearSquare && NearSquare->IsFlyable()) + ++Walkables; + } - Char->ReceiveDamage(0, 20 + RAND() % 21, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); - Char->CheckDeath(CONST_S("killed by an earthquake"), 0); - } + if(Walkables > 6) + { + Square->ChangeOLTerrainAndUpdateLights(earth::Spawn()); - Square->KickAnyoneStandingHereAway(); - Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 41, PHYSICAL_DAMAGE); - break; + if(Char) + { + if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE(fsMsgHitNPC.IsEmpty()?"%s is hit by a rock falling from the ceiling!":fsMsgHitNPC.CStr(), + Char->CHAR_NAME(DEFINITE)); + + Char->ReceiveDamage(0, 20 + RAND() % 21, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); + Char->CheckDeath(CONST_S("killed by an earthquake"), 0); } + + Square->KickAnyoneStandingHereAway(); + Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 41, PHYSICAL_DAMAGE); + break; } + } - // Generate a few boulders in the level + // Generate a few boulders in the level - int BoulderNumber = 10 + RAND() % 10; + int BoulderNumber = 10 + RAND() % 10; - for(c = 0; c < BoulderNumber; ++c) - { - v2 Pos = game::GetCurrentLevel()->GetRandomSquare(); - lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); - character* MonsterHere = Square->GetCharacter(); + for(c = 0; c < BoulderNumber; ++c) + { + v2 Pos = game::GetCurrentLevel()->GetRandomSquare(); + lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); + character* MonsterHere = Square->GetCharacter(); - if(!Square->GetOLTerrain() && (!MonsterHere || MonsterHere->GetRelation(PLAYER) == HOSTILE)) - { - Square->ChangeOLTerrainAndUpdateLights(boulder::Spawn(1 + (RAND() & 1))); + if(!Square->GetOLTerrain() && (!MonsterHere || MonsterHere->GetRelation(PLAYER) == HOSTILE)) + { + Square->ChangeOLTerrainAndUpdateLights(boulder::Spawn(1 + (RAND() & 1))); - if(MonsterHere) - MonsterHere->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); + if(MonsterHere) + MonsterHere->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); - Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE); - } + Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE); } + } - // Damage to items in the level + // Damage to items in the level - for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) - for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) - game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); + for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) + for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) + game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); } void scrollofearthquake::FinishReading(character* Reader) { From f9fe073a7b87afe844cef9ea445de290ae3a97c6 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 6 Apr 2020 19:46:34 -0300 Subject: [PATCH 093/235] WizAutoPlay: improved eating to play faster; --- Main/Source/human.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 3572b8ace..4feea455b 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -3755,6 +3755,7 @@ truth humanoid::AutoPlayAIequip() if( ConsumeMaterial!=NULL && vitEqW[c]->IsConsumable() && + !HasHadBodyPart(vitEqW[c]) && //this avoids a slow interactive question ConsumeItem(vitEqW[c], vitEqW[c]->GetConsumeMaterial(this)->GetConsumeVerb()) ){ DBG2("AutoPlayConsumed",vitEqW[c]->GetNameSingular().CStr()); From 197bc7925edaf7246a3e517a8c43c4344f8e89a0 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 7 Apr 2020 21:36:15 -0300 Subject: [PATCH 094/235] Offer auto drop (if on) will add dropped tag (that prevents auto pickup if on); Praying at a master god's ground will show relation too (if opt is on). --- Main/Include/game.h | 1 + Main/Source/command.cpp | 17 ++++++++--------- Main/Source/game.cpp | 10 ++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Main/Include/game.h b/Main/Include/game.h index 4ab24a0ad..9a894f14c 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -182,6 +182,7 @@ class game static void UpdateSRegionsXBRZ(bool bIsXBRZScale); static void RegionSilhouetteEnable(bool b); static void RegionListItemEnable(bool b); + static void SetDropTag(item* it); static void UpdatePosAroundForXBRZ(v2 ScreenPos); static void SRegionAroundDisable(); static void SRegionAroundAllow(); diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 49677f6b0..e268e8589 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -531,12 +531,7 @@ truth commandsystem::Drop(character* Char) for(uint c = 0; c < ToDrop.size(); ++c) { ToDrop[c]->MoveTo(Char->GetStackUnder()); - if(ivanconfig::IsAutoPickupThrownItems()){ - ToDrop[c]->ClearTag('t'); //throw: to avoid auto-pickup - if(game::IsAutoPickupMatch(ToDrop[c]->GetName(DEFINITE))){ - ToDrop[c]->SetTag('d'); //intentionally dropped: this will let user decide specific items to NOT auto-pickup regex matching - } - } + game::SetDropTag(ToDrop[c]); } Success = true; } @@ -1259,9 +1254,9 @@ truth commandsystem::Pray(character* Char) return false; } + festring desc; if(!DivineMaster) { - festring desc; for(int c = 1; c <= GODS; ++c) if(game::GetGod(c)->IsKnown()) { @@ -1276,7 +1271,9 @@ truth commandsystem::Pray(character* Char) else if(game::GetGod(DivineMaster)->IsKnown()) { - Panthenon.AddEntry(game::GetGod(DivineMaster)->GetCompleteDescription(), LIGHT_GRAY, 20, DivineMaster); + desc << game::GetGod(DivineMaster)->GetCompleteDescription(); + if(ivanconfig::IsShowGodInfo())desc << " " << game::GetGod(DivineMaster)->GetLastKnownRelation(); + Panthenon.AddEntry(desc, LIGHT_GRAY, 20, DivineMaster); Panthenon.SetLastEntryHelp(festring() << game::GetGod(DivineMaster)->GetName() << ", the " << game::GetGod(DivineMaster)->GetDescription()); Known[0] = DivineMaster; } @@ -1420,8 +1417,10 @@ truth commandsystem::Offer(character* Char) return true; } else { if(ivanconfig::IsDropBeforeOffering()) - if(!game::IsQuestItem(Item)) + if(!game::IsQuestItem(Item)){ Item->MoveTo(Char->GetLSquareUnder()->GetStack()); //drops before offering so non accepted will unclutter player inventory + game::SetDropTag(Item); + } return false; } } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 931cd325f..de665e078 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1324,6 +1324,16 @@ int game::RotateMapNotes() return iMapNotesRotation; } +void game::SetDropTag(item* it) +{ + if(ivanconfig::IsAutoPickupThrownItems()){ + it->ClearTag('t'); //throw: to avoid auto-pickup + if(game::IsAutoPickupMatch(it->GetName(DEFINITE))){ + it->SetTag('d'); //intentionally dropped: this will let user decide specific items to NOT auto-pickup regex matching + } + } +} + std::vector afsAutoPickupMatch; pcre *reAutoPickup=NULL; void game::UpdateAutoPickUpMatching() //simple matching syntax From efd1bb59c3d4ae062d45bc44b025dd6a47c4816a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 8 Apr 2020 00:03:13 -0300 Subject: [PATCH 095/235] Fixed a crash when tearing a web down from an NPC. --- Main/Source/char.cpp | 3 +++ Main/Source/cmdcraft.cpp | 2 +- Main/Source/traps.cpp | 21 ++++++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 4b473139b..e651524ff 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -12110,6 +12110,9 @@ struct trapidcomparer void character::RemoveTrap(ulong ID) { + if(!TrapData) //everywhere calling this must be sure it can be called! + ABORT("can't remove a non existing trap..."); + trapdata*& T = ListFind(TrapData, trapidcomparer(ID)); trapdata* ToDel = T; T = T->Next; diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 4e9355953..f60c60aa4 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -1415,7 +1415,7 @@ struct srpCutWeb : public recipe{ if(ra && la) tot+=2; //means something like using both arms to destroy a single web spot if(bSelfPos) - tot *= 3; //to make it worther than just trying to move, and to compensate for not moving too as player won't insta flee from attacks + tot *= 3; //to make it worthier than just trying to move, and to compensate for not moving too as player won't insta flee from attacks if(rpd.itTool!=NULL) //float multiplier last thing! tot *= 1 + craftcore::CraftSkill(h)/10.0; DBG1(tot); diff --git a/Main/Source/traps.cpp b/Main/Source/traps.cpp index 4f21e195a..e64c53e25 100644 --- a/Main/Source/traps.cpp +++ b/Main/Source/traps.cpp @@ -42,6 +42,12 @@ int web::GetRemoveTrapModifier(character* C) + C->GetAttribute(ARM_STRENGTH), 1); } +/** + * + * @param Actor who tears down the web (from self or another character*) + * @param Modifier + * @return + */ truth web::TryToTearDown(character* Actor,int Modifier) { if(Modifier==-1) @@ -50,16 +56,21 @@ truth web::TryToTearDown(character* Actor,int Modifier) if(!RAND_N(Max(Modifier << 1, 2))) { //if(GetLSquareUnder()->GetPos()==Actor->GetPos())C->RemoveTrap(GetTrapID());else - if(GetLSquareUnder()->GetCharacter()) - GetLSquareUnder()->GetCharacter()->RemoveTrap(GetTrapID()); + character* C = GetLSquareUnder()->GetCharacter(); + if(C && C->IsStuckToTrap(GetTrapID())) + C->RemoveTrap(GetTrapID()); TrapData.VictimID = 0; GetLSquareUnder()->RemoveTrap(this); SendToHell(); - + + festring fsOther; + if(C && Actor!=C) + fsOther=CONST_S(" from ")+C->CHAR_NAME(DEFINITE); + if(Actor->IsPlayer()) - ADD_MESSAGE("You tear the web down."); + ADD_MESSAGE("You tear the web down%s.",fsOther.CStr()); else if(Actor->CanBeSeenByPlayer()) - ADD_MESSAGE("%s tears the web down.", Actor->CHAR_NAME(DEFINITE)); + ADD_MESSAGE("%s tears the web down%s.", Actor->CHAR_NAME(DEFINITE),fsOther.CStr()); Actor->EditAP(-500); return true; From c850ca9adae7acb7f0fb20f8d2e112030eb0cb12 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 10 Apr 2020 02:24:01 -0300 Subject: [PATCH 096/235] When generating a new level, if the problem is "Undefined configuration sought!", it will be retried up to 10 times trying to avoid an ABORT(). fixed SEGFAULT when looking items on a square that has only an adjacent square with a wall lantern. --- Main/Include/database.h | 2 +- Main/Include/game.h | 4 ++ Main/Source/database.cpp | 9 +++- Main/Source/dungeon.cpp | 94 ++++++++++++++++++++++++---------------- Main/Source/game.cpp | 8 ++-- 5 files changed, 73 insertions(+), 44 deletions(-) diff --git a/Main/Include/database.h b/Main/Include/database.h index 987c8ea53..0c2fa6253 100644 --- a/Main/Include/database.h +++ b/Main/Include/database.h @@ -34,8 +34,8 @@ template class databasecreator typedef std::map*> databasemembermap; static void ReadFrom(inputfile&); static void FindDataBase(const database*&, const prototype*, int); - static truth InstallDataBaseIfPossible(type*, int, int); static void InstallDataBase(type*, int); + static truth InstallDataBaseIfPossible(type*, int, int); static void CreateDataBaseMemberMap(); static int CreateDivineConfigurations(const prototype*, database**, int); private: diff --git a/Main/Include/game.h b/Main/Include/game.h index 9a894f14c..a90107906 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -165,6 +165,7 @@ typedef std::vector charactervector; class quitrequest { }; class areachangerequest { }; +class undefinedConfigurationSoughtException { }; typedef void (*dbgdrawoverlay)(); @@ -509,6 +510,8 @@ class game static int GetSaveFileVersionHardcoded(); static void ValidateCommandKeys(char Key1,char Key2,char Key3); static void LoadCustomCommandKeys(); + static truth IsGenNewLvl(){return bGeneratingNewDungeonLevel;} + static truth ToggleGenNewLvl(){return bGeneratingNewDungeonLevel = !bGeneratingNewDungeonLevel;return bGeneratingNewDungeonLevel;} private: static void UpdateCameraCoordinate(int&, int, int, int); static cchar* const Alignment[]; @@ -631,6 +634,7 @@ class game const static int iListWidth = 652; static std::vector vDbgDrawOverlayFunctions; static int iCurrentDungeonTurn; + static truth bGeneratingNewDungeonLevel; }; inline void game::CombineLights(col24& L1, col24 L2) diff --git a/Main/Source/database.cpp b/Main/Source/database.cpp index bf98984d3..edcd70027 100644 --- a/Main/Source/database.cpp +++ b/Main/Source/database.cpp @@ -13,6 +13,7 @@ /* Compiled through dataset.cpp */ #include +#include "game.h" int CreateConfigTable(databasebase*** ConfigTable, databasebase*** TempTable, databasebase** ConfigArray, long* TempTableInfo, int Type, int Configs, int TempTables) @@ -949,8 +950,12 @@ template void databasecreator::InstallDataBase(type* Instance const prototype* Proto = Instance->FindProtoType(); FindDataBase(Instance->DataBase, Proto, Config); - if(!Instance->DataBase) - ABORT("Undefined %s configuration #%d sought!", const_cast(Proto->GetClassID()), Config); + if(!Instance->DataBase){ + if(game::IsGenNewLvl()) + throw undefinedConfigurationSoughtException(); + else + ABORT("Undefined %s configuration #%d sought!", const_cast(Proto->GetClassID()), Config); + } } template truth databasecreator::InstallDataBaseIfPossible(type* Instance, int Config, int OldConfig) diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp index f53c5b24f..1b7a4112c 100644 --- a/Main/Source/dungeon.cpp +++ b/Main/Source/dungeon.cpp @@ -21,6 +21,7 @@ #include "graphics.h" #include "message.h" #include "audio.h" +#include "database.h" dungeon::dungeon(int Index) : Index(Index) { @@ -88,47 +89,64 @@ truth dungeon::PrepareLevel(int Index, truth Visual) } else { - level* NewLevel = Level[Index] = new level; - NewLevel->SetDungeon(this); - NewLevel->SetIndex(Index); - const levelscript* LevelScript = GetLevelScript(Index); - NewLevel->SetLevelScript(LevelScript); + int iRetryMax=10; + level* NewLevel=NULL; + cbitmap* EnterImage=NULL; + if(!game::ToggleGenNewLvl())ABORT("expecting gen lvl to be true"); + for(int i=0;iSetDungeon(this); + NewLevel->SetIndex(Index); + const levelscript* LevelScript = GetLevelScript(Index); + NewLevel->SetLevelScript(LevelScript); + + if(Visual) + { + if(LevelScript->GetEnterImage()) + { + EnterImage = new bitmap(game::GetDataDir() + "Graphics/" + *LevelScript->GetEnterImage()); + game::SetEnterImage(EnterImage); + v2 Displacement = *LevelScript->GetEnterTextDisplacement(); + game::SetEnterTextDisplacement(Displacement); + game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + + CONST_S("...\n\nThis may take some time, please wait."), + Displacement, WHITE, false, true, &game::BusyAnimation); + game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + + CONST_S("...\n\nPress any key to continue."), + Displacement, WHITE, true, false, &game::BusyAnimation); + game::SetEnterImage(0); + delete EnterImage; + EnterImage=NULL; + } + else + { + game::SetEnterTextDisplacement(ZERO_V2); + game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + + CONST_S("...\n\nThis may take some time, please wait."), + ZERO_V2, WHITE, false, true, &game::BusyAnimation); + } + } - if(Visual) - { - if(LevelScript->GetEnterImage()) - { - cbitmap* EnterImage = new bitmap(game::GetDataDir() + "Graphics/" + *LevelScript->GetEnterImage()); - game::SetEnterImage(EnterImage); - v2 Displacement = *LevelScript->GetEnterTextDisplacement(); - game::SetEnterTextDisplacement(Displacement); - game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) - + CONST_S("...\n\nThis may take some time, please wait."), - Displacement, WHITE, false, true, &game::BusyAnimation); - game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) - + CONST_S("...\n\nPress any key to continue."), - Displacement, WHITE, true, false, &game::BusyAnimation); - game::SetEnterImage(0); - delete EnterImage; - } - else - { - game::SetEnterTextDisplacement(ZERO_V2); - game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) - + CONST_S("...\n\nThis may take some time, please wait."), - ZERO_V2, WHITE, false, true, &game::BusyAnimation); + NewLevel->Generate(Index); + game::SetCurrentLSquareMap(NewLevel->GetMap()); + Generated[Index] = true; + game::BusyAnimation(); + + if(*NewLevel->GetLevelScript()->GenerateMonsters()) + NewLevel->GenerateNewMonsters(NewLevel->GetIdealPopulation(), false); + + if(game::ToggleGenNewLvl())ABORT("expecting gen lvl to be false"); + return false; // new level is ok + }catch(undefinedConfigurationSoughtException){ + // cleanup + if(NewLevel)delete NewLevel; //TODO is this enough to clean it in a whole? + if(EnterImage)delete EnterImage; + + //retry } } - - NewLevel->Generate(Index); - game::SetCurrentLSquareMap(NewLevel->GetMap()); - Generated[Index] = true; - game::BusyAnimation(); - - if(*NewLevel->GetLevelScript()->GenerateMonsters()) - NewLevel->GenerateNewMonsters(NewLevel->GetIdealPopulation(), false); - - return false; + ABORT("Generating new level failed after %d retries, aborting...",iRetryMax); } } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index de665e078..55ea2df3e 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -4542,8 +4542,8 @@ v2 game::LookKeyHandler(v2 CursorPos, int Key) if(LSquare->IsTransparent() && Stack->GetVisibleItems(Player)) Stack->DrawContents(Player, "Items here", - NO_SELECT| - (GetSeeWholeMapCheatMode() || Stack->GetItem(0)->GetRoom() || Player->GetLSquareUnder()==LSquare ? + NO_SELECT| // that square may have an adjacent square with a wall lantern, checking for Stack->GetItem(0) prevents a SEGFAULT + (GetSeeWholeMapCheatMode() || (Stack->GetItem(0) && Stack->GetItem(0)->GetRoom()) || Player->GetLSquareUnder()==LSquare ? 0 : NO_SPECIAL_INFO) ); else @@ -4820,8 +4820,10 @@ truth game::LeaveArea(charactervector& Group, truth AllowHostiles, truth AlliesF return true; } -/* Used always when the player enters an area. */ +static truth bGeneratingNewDungeonLevel; +truth game::bGeneratingNewDungeonLevel=false; +/* Used always when the player enters an area. */ void game::EnterArea(charactervector& Group, int Area, int EntryIndex) { if(Area != WORLD_MAP) From 1403a92a9e02391abd778f1a10198ab8c2805e0a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 10 Apr 2020 13:29:42 -0300 Subject: [PATCH 097/235] undefinedConfigurationSoughtException: reorganized related code to avoid messing the hierarchy; --- FeLib/Include/error.h | 9 +++++++++ FeLib/Source/error.cpp | 2 ++ Main/Include/game.h | 4 ---- Main/Source/database.cpp | 4 ++-- Main/Source/dungeon.cpp | 4 ++-- Main/Source/game.cpp | 3 --- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/FeLib/Include/error.h b/FeLib/Include/error.h index 1e81a612b..90e5f3d56 100644 --- a/FeLib/Include/error.h +++ b/FeLib/Include/error.h @@ -21,6 +21,15 @@ #define SIGNALS 8 #endif +class undefinedConfigurationSoughtException +{ + public: + static truth IsGenNewLvl(){return bGeneratingNewDungeonLevel;} + static truth ToggleGenNewLvl(){return bGeneratingNewDungeonLevel = !bGeneratingNewDungeonLevel;return bGeneratingNewDungeonLevel;} + private: + static truth bGeneratingNewDungeonLevel; +}; + class globalerrorhandler { public: diff --git a/FeLib/Source/error.cpp b/FeLib/Source/error.cpp index 5f6235201..6e93d88b4 100644 --- a/FeLib/Source/error.cpp +++ b/FeLib/Source/error.cpp @@ -218,3 +218,5 @@ void globalerrorhandler::SignalHandler(int Signal) } #endif + +truth undefinedConfigurationSoughtException::bGeneratingNewDungeonLevel=false; diff --git a/Main/Include/game.h b/Main/Include/game.h index a90107906..9a894f14c 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -165,7 +165,6 @@ typedef std::vector charactervector; class quitrequest { }; class areachangerequest { }; -class undefinedConfigurationSoughtException { }; typedef void (*dbgdrawoverlay)(); @@ -510,8 +509,6 @@ class game static int GetSaveFileVersionHardcoded(); static void ValidateCommandKeys(char Key1,char Key2,char Key3); static void LoadCustomCommandKeys(); - static truth IsGenNewLvl(){return bGeneratingNewDungeonLevel;} - static truth ToggleGenNewLvl(){return bGeneratingNewDungeonLevel = !bGeneratingNewDungeonLevel;return bGeneratingNewDungeonLevel;} private: static void UpdateCameraCoordinate(int&, int, int, int); static cchar* const Alignment[]; @@ -634,7 +631,6 @@ class game const static int iListWidth = 652; static std::vector vDbgDrawOverlayFunctions; static int iCurrentDungeonTurn; - static truth bGeneratingNewDungeonLevel; }; inline void game::CombineLights(col24& L1, col24 L2) diff --git a/Main/Source/database.cpp b/Main/Source/database.cpp index edcd70027..053d3b192 100644 --- a/Main/Source/database.cpp +++ b/Main/Source/database.cpp @@ -13,7 +13,7 @@ /* Compiled through dataset.cpp */ #include -#include "game.h" +#include "error.h" int CreateConfigTable(databasebase*** ConfigTable, databasebase*** TempTable, databasebase** ConfigArray, long* TempTableInfo, int Type, int Configs, int TempTables) @@ -951,7 +951,7 @@ template void databasecreator::InstallDataBase(type* Instance FindDataBase(Instance->DataBase, Proto, Config); if(!Instance->DataBase){ - if(game::IsGenNewLvl()) + if(undefinedConfigurationSoughtException::IsGenNewLvl()) throw undefinedConfigurationSoughtException(); else ABORT("Undefined %s configuration #%d sought!", const_cast(Proto->GetClassID()), Config); diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp index 1b7a4112c..402087848 100644 --- a/Main/Source/dungeon.cpp +++ b/Main/Source/dungeon.cpp @@ -92,7 +92,7 @@ truth dungeon::PrepareLevel(int Index, truth Visual) int iRetryMax=10; level* NewLevel=NULL; cbitmap* EnterImage=NULL; - if(!game::ToggleGenNewLvl())ABORT("expecting gen lvl to be true"); + if(!undefinedConfigurationSoughtException::ToggleGenNewLvl())ABORT("expecting gen lvl to be true"); for(int i=0;iGetLevelScript()->GenerateMonsters()) NewLevel->GenerateNewMonsters(NewLevel->GetIdealPopulation(), false); - if(game::ToggleGenNewLvl())ABORT("expecting gen lvl to be false"); + if(undefinedConfigurationSoughtException::ToggleGenNewLvl())ABORT("expecting gen lvl to be false"); return false; // new level is ok }catch(undefinedConfigurationSoughtException){ // cleanup diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 55ea2df3e..4ea92deec 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -4820,9 +4820,6 @@ truth game::LeaveArea(charactervector& Group, truth AllowHostiles, truth AlliesF return true; } -static truth bGeneratingNewDungeonLevel; -truth game::bGeneratingNewDungeonLevel=false; - /* Used always when the player enters an area. */ void game::EnterArea(charactervector& Group, int Area, int EntryIndex) { From ed044c67b8774413e6f8d64199b12eefe0572786 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 10 Apr 2020 14:35:51 -0300 Subject: [PATCH 098/235] Documented at --help command line option, all useful environment variables; Added debug info (a list of all characters, items and trap IDs) to track a crashes (mainly when a TrapID is not found); --- FeLib/Source/graphics.cpp | 2 +- Main/Source/char.cpp | 5 ++++- Main/Source/game.cpp | 3 +++ Main/Source/main.cpp | 6 ++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/FeLib/Source/graphics.cpp b/FeLib/Source/graphics.cpp index ea97b53ce..8f6425c28 100644 --- a/FeLib/Source/graphics.cpp +++ b/FeLib/Source/graphics.cpp @@ -634,7 +634,7 @@ void graphics::AddDrawAboveAll(drawabove da, int iPriority, const char* desc) vDrawaboveTmp.erase(vDrawaboveTmp.begin()+iIndexLP); } - char* c = std::getenv("IVAN_LISTDRAWABOVE"); //to help on development, so all priorities can be adjusted easily + char* c = std::getenv("IVAN_LISTDRAWABOVE"); //output to terminal to help on development, so all priorities can be adjusted easily bool bCOut = c!=NULL && strcmp(c,"true")==0; festring fsDrawAbovePriority; for(int i=0;iGetVictimID() == GetID() && Trap->TryToUnStick(this, Dir)) break; } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 4ea92deec..f7d260caf 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -296,6 +296,7 @@ int game::GetScreenYSize() { //actually dugeon visible height in tiles count void game::AddCharacterID(character* Char, ulong ID) { + DBG3(ID,Char->GetID(),Char->GetName(INDEFINITE).CStr()); CharacterIDMap.insert(std::make_pair(ID, Char)); } void game::RemoveCharacterID(ulong ID) @@ -309,6 +310,7 @@ void game::RemoveCharacterID(ulong ID) } void game::AddItemID(item* Item, ulong ID) { + DBG3(ID,Item->GetID(),Item->GetName(INDEFINITE).CStr()); ItemIDMap.insert(std::make_pair(ID, Item)); } @@ -339,6 +341,7 @@ void game::UpdateItemID(item* Item, ulong ID) } void game::AddTrapID(entity* Trap, ulong ID) { + DBG3(ID,Trap->GetTrapID(),Trap->GetTrapType()); if(ID) TrapIDMap.insert(std::make_pair(ID, Trap)); } void game::RemoveTrapID(ulong ID) diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index 7f0b39524..d375472af 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -129,10 +129,16 @@ int main(int argc, char** argv) if(argc > 1 && festring(argv[1]) == "--help") { + std::cout << "Command line options:" << std::endl; std::cout << "--defgen Generate defines validator source file. " << std::endl; std::cout << "--defval Validate defines. " << std::endl; // std::cout << "--genmvkeys Generate custom move keys cfg file. " << std::endl; std::cout << "--version Show current game version. " << std::endl; + std::cout << std::endl; + std::cout << "Environment Variables (only work if set to 'true'):" << std::endl; + std::cout << "IVAN_SHOWFPS=true # show FPS at top right" << std::endl; + std::cout << "IVAN_DebugShowTinyDungeon=true #DEBUG always show tiny dungeon above stretched one" << std::endl; + std::cout << "IVAN_LISTDRAWABOVE=true #DEBUG output the draw above priority list to text console terminal" << std::endl; return 0; } From af5f9f024542ab79eb2f2f50b4fcde38922da424 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 10 Apr 2020 15:08:27 -0300 Subject: [PATCH 099/235] Added autoplay defines to clarify usages; Wont ask keypress on new special dungeon if autoplay is on; Fixed debug messages (if enabled) causing crash at AddCharacterID(), AddItemID(), AddTrapID(); --- Main/Include/game.h | 10 ++++++++-- Main/Source/char.cpp | 6 +++--- Main/Source/dungeon.cpp | 2 +- Main/Source/game.cpp | 26 ++++++++++++++------------ 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Main/Include/game.h b/Main/Include/game.h index 9a894f14c..48c5fd161 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -168,6 +168,12 @@ class areachangerequest { }; typedef void (*dbgdrawoverlay)(); +#define AUTOPLAYMODE_DISABLED 0 +#define AUTOPLAYMODE_NOTIMEOUT 1 +#define AUTOPLAYMODE_SLOW 2 +#define AUTOPLAYMODE_FAST 3 +#define AUTOPLAYMODE_FRENZY 4 + #define AUTOSAVE_SUFFIX ".AutoSave" #define CUSTOM_KEYS_FILENAME "CustomCommandKeys.cfg" class game @@ -407,7 +413,7 @@ class game static void IncAutoPlayMode(); static int GetAutoPlayMode() { return AutoPlayMode; } static void AutoPlayModeApply(); - static void DisableAutoPlayMode() {AutoPlayMode=0;AutoPlayModeApply();} + static void DisableAutoPlayMode() {AutoPlayMode=AUTOPLAYMODE_DISABLED;AutoPlayModeApply();} static void SeeWholeMap(); static int GetSeeWholeMapCheatMode() { return SeeWholeMapCheatMode; } static truth GoThroughWallsCheatIsActive() { return GoThroughWallsCheat; } @@ -416,7 +422,7 @@ class game static truth WizardModeIsActive() { return false; } static int GetSeeWholeMapCheatMode() { return 0; } static truth GoThroughWallsCheatIsActive() { return false; } - static int GetAutoPlayMode() { return 0; } + static int GetAutoPlayMode() { return AUTOPLAYMODE_DISABLED; } #endif static truth WizardModeIsReallyActive() { return WizardMode; } diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index a77ce56c0..fd1e72b53 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3580,7 +3580,7 @@ truth character::AutoPlayAICommand(int& rKey) return true; if(iWanderTurns>0){ - if(!IsPlayer() || game::GetAutoPlayMode()==0 || !IsPlayerAutoPlay()){ //redundancy: yep + if(!IsPlayer() || game::GetAutoPlayMode()==AUTOPLAYMODE_DISABLED || !IsPlayerAutoPlay()){ //redundancy: yep DBG9(this,GetNameSingular().CStr(),IsPolymorphed(),IsHuman(),IsHumanoid(),IsPolymorphable(),IsPlayerKind(),IsTemporary(),IsPet()); DBG5(IsHeadless(),IsPlayer(),game::GetAutoPlayMode(),IsPlayerAutoPlay(),GetName(DEFINITE).CStr()); ABORT("autoplay is inconsistent %d %d %d %d %d %s %d %s %d %d %d %d %d", @@ -3724,7 +3724,7 @@ void character::GetPlayerCommand() #ifdef WIZARD if(IsPlayerAutoPlay()){ bool bForceStop = false; - if(game::GetAutoPlayMode()>=2) + if(game::GetAutoPlayMode()>=AUTOPLAYMODE_SLOW) bForceStop = globalwindowhandler::IsKeyPressed(SDL_SCANCODE_ESCAPE); if(!bForceStop && Key=='.'){ // pressed or simulated @@ -3739,7 +3739,7 @@ void character::GetPlayerCommand() * if the user hits any key during the autoplay mode that runs by itself, it will be disabled. * at non auto mode, can be moved around but cannot rest or will move by itself */ - if(game::GetAutoPlayMode()>=2 && (Key!='~' || bForceStop)){ + if(game::GetAutoPlayMode()>=AUTOPLAYMODE_SLOW && (Key!='~' || bForceStop)){ game::DisableAutoPlayMode(); AutoPlayAIReset(true); // this will help on re-randomizing things, mainly paths } diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp index 402087848..101bf8ba7 100644 --- a/Main/Source/dungeon.cpp +++ b/Main/Source/dungeon.cpp @@ -114,7 +114,7 @@ truth dungeon::PrepareLevel(int Index, truth Visual) Displacement, WHITE, false, true, &game::BusyAnimation); game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + CONST_S("...\n\nPress any key to continue."), - Displacement, WHITE, true, false, &game::BusyAnimation); + Displacement, WHITE, game::GetAutoPlayMode()GetID(),Char->GetName(INDEFINITE).CStr()); + DBG2(ID,Char->GetID()); // do not use GetName() here, will crash!: ,Char->GetName(INDEFINITE).CStr()); CharacterIDMap.insert(std::make_pair(ID, Char)); } void game::RemoveCharacterID(ulong ID) @@ -310,7 +310,7 @@ void game::RemoveCharacterID(ulong ID) } void game::AddItemID(item* Item, ulong ID) { - DBG3(ID,Item->GetID(),Item->GetName(INDEFINITE).CStr()); + DBG2(ID,Item->GetID());// do not use GetName() here, will crash!: ,Item->GetName(INDEFINITE).CStr()); ItemIDMap.insert(std::make_pair(ID, Item)); } @@ -341,8 +341,10 @@ void game::UpdateItemID(item* Item, ulong ID) } void game::AddTrapID(entity* Trap, ulong ID) { - DBG3(ID,Trap->GetTrapID(),Trap->GetTrapType()); - if(ID) TrapIDMap.insert(std::make_pair(ID, Trap)); + if(ID){ + DBG2(ID,Trap->GetTrapID()); // do not use GetTrapType() here, will crash!: ,Trap->GetTrapType()); + TrapIDMap.insert(std::make_pair(ID, Trap)); + } } void game::RemoveTrapID(ulong ID) { @@ -4246,7 +4248,7 @@ int game::AskForKeyPress(cfestring& Topic) int Key = GET_KEY(); #ifdef FELIST_WAITKEYUP //not actually felist here but is the waitkeyup event - if(game::GetAutoPlayMode()==0) + if(game::GetAutoPlayMode()==AUTOPLAYMODE_DISABLED) for(;;){if(WAIT_FOR_KEY_UP())break;}; #endif @@ -5594,25 +5596,25 @@ void game::AutoPlayModeApply(){ const char* msg; switch(game::AutoPlayMode){ - case 0: + case AUTOPLAYMODE_DISABLED: // disabled msg="%s says \"I can rest now.\""; break; - case 1: + case AUTOPLAYMODE_NOTIMEOUT: // no timeout, user needs to hit '.' to it autoplay once, the behavior is controled by AutoPlayMode AND the timeout delay that if 0 will have no timeout but will still autoplay. msg="%s says \"I won't rest!\""; break; - case 2: // TIMEOUTs key press from here to below + case AUTOPLAYMODE_SLOW: // TIMEOUTs key press from here to below msg="%s says \"I can't wait anymore!\""; iTimeout=(1000); bPlayInBackground=true; break; - case 3: + case AUTOPLAYMODE_FAST: msg="%s says \"I am in a hurry!\""; iTimeout=(1000/2); bPlayInBackground=true; break; - case 4: + case AUTOPLAYMODE_FRENZY: msg="%s says \"I... *frenzy* yeah! Try to follow me now! Hahaha!\""; iTimeout=10;//min possible to be fastest //(1000/10); // like 10 FPS, so user has 100ms chance to disable it bPlayInBackground=true; @@ -5645,7 +5647,7 @@ void game::IncAutoPlayMode() { // } ++AutoPlayMode; - if(AutoPlayMode>4)AutoPlayMode=0; + if(AutoPlayMode>AUTOPLAYMODE_FRENZY)AutoPlayMode=AUTOPLAYMODE_DISABLED; AutoPlayModeApply(); } From 8361a385b13143d0dd8f1d7474f2740b741196b1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 10 Apr 2020 17:01:27 -0300 Subject: [PATCH 100/235] Properly using dbgmsgproj.h; Now using genericException instead of undefinedConfigurationSoughtException; --- FeLib/Include/dbgmsgproj.h | 4 +++- FeLib/Include/error.h | 5 ++++- FeLib/Include/graphics.h | 2 +- FeLib/Source/error.cpp | 10 +++++++++- Main/Source/char.cpp | 2 +- Main/Source/database.cpp | 4 ++-- Main/Source/dungeon.cpp | 9 ++++++--- 7 files changed, 26 insertions(+), 10 deletions(-) diff --git a/FeLib/Include/dbgmsgproj.h b/FeLib/Include/dbgmsgproj.h index 77963dea8..59b830dc3 100644 --- a/FeLib/Include/dbgmsgproj.h +++ b/FeLib/Include/dbgmsgproj.h @@ -2,7 +2,9 @@ * !!!WARNING!!! * This file cannot be included in other .h files TODO why? * These macros cannot be used at inline methods declared in .h files TODO why? - * If you do this, it may SEGFAULT only when being run. Compiler and linker will warn nothing about the problem! + * If you do this, it may SEGFAULT only when being run. + * Compiler and linker will warn nothing about the problem! + * On linux this check can be added prior to compile: `egrep "dbgmsgproj.h" * -rnI --include "*.h"` */ #ifndef INCLUDE_DBGMSGPROJ_H_ diff --git a/FeLib/Include/error.h b/FeLib/Include/error.h index 90e5f3d56..6cfccc946 100644 --- a/FeLib/Include/error.h +++ b/FeLib/Include/error.h @@ -21,12 +21,15 @@ #define SIGNALS 8 #endif -class undefinedConfigurationSoughtException +class genericException { public: + genericException(cchar* pc); + cchar* GetMsg(){return pcMsg;}; static truth IsGenNewLvl(){return bGeneratingNewDungeonLevel;} static truth ToggleGenNewLvl(){return bGeneratingNewDungeonLevel = !bGeneratingNewDungeonLevel;return bGeneratingNewDungeonLevel;} private: + cchar* pcMsg; static truth bGeneratingNewDungeonLevel; }; diff --git a/FeLib/Include/graphics.h b/FeLib/Include/graphics.h index 68ca890c8..ff17b6897 100644 --- a/FeLib/Include/graphics.h +++ b/FeLib/Include/graphics.h @@ -167,4 +167,4 @@ class graphics static rawbitmap* DefaultFont; }; -#endif +#endif //__GRAPHICS_H__ diff --git a/FeLib/Source/error.cpp b/FeLib/Source/error.cpp index 6e93d88b4..8c42a2350 100644 --- a/FeLib/Source/error.cpp +++ b/FeLib/Source/error.cpp @@ -77,6 +77,14 @@ void globalerrorhandler::DumpStackTraceToStdErr(int Signal){ } #endif + +genericException::genericException(cchar* pc) +{ + pcMsg=pc; + DBG1(pc); + DBGBREAKPOINT; +} + void globalerrorhandler::Install() { static truth AlreadyInstalled = false; @@ -219,4 +227,4 @@ void globalerrorhandler::SignalHandler(int Signal) #endif -truth undefinedConfigurationSoughtException::bGeneratingNewDungeonLevel=false; +truth genericException::bGeneratingNewDungeonLevel=false; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index fd1e72b53..e734992c5 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -12095,7 +12095,7 @@ truth character::TryToUnStickTraps(v2 Dir) entity* Trap = game::SearchTrap(TrapVector[c].TrapID); if(!Trap) - ABORT("Trap ID %d not found, character name: %s, x=%d y=%d",TrapVector[c].TrapID,GetName(INDEFINITE).CStr(),GetPos().X,GetPos().Y); + ABORT("Trap ID %lu not found, character name: %s, x=%d y=%d",TrapVector[c].TrapID,GetName(INDEFINITE).CStr(),GetPos().X,GetPos().Y); if(Trap->GetVictimID() == GetID() && Trap->TryToUnStick(this, Dir)) break; diff --git a/Main/Source/database.cpp b/Main/Source/database.cpp index 053d3b192..3d49698cc 100644 --- a/Main/Source/database.cpp +++ b/Main/Source/database.cpp @@ -951,8 +951,8 @@ template void databasecreator::InstallDataBase(type* Instance FindDataBase(Instance->DataBase, Proto, Config); if(!Instance->DataBase){ - if(undefinedConfigurationSoughtException::IsGenNewLvl()) - throw undefinedConfigurationSoughtException(); + if(genericException::IsGenNewLvl()) + throw genericException([Proto,Config]{festring fsE;fsE << "UndefinedConfigurationSought,\"" << const_cast(Proto->GetClassID()) << "\","<(Proto->GetClassID()), Config); } diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp index 101bf8ba7..74bd7d8da 100644 --- a/Main/Source/dungeon.cpp +++ b/Main/Source/dungeon.cpp @@ -22,6 +22,7 @@ #include "message.h" #include "audio.h" #include "database.h" +#include "dbgmsgproj.h" dungeon::dungeon(int Index) : Index(Index) { @@ -89,10 +90,11 @@ truth dungeon::PrepareLevel(int Index, truth Visual) } else { + DBG2("GeneratingDungeonLevel",Index); int iRetryMax=10; level* NewLevel=NULL; cbitmap* EnterImage=NULL; - if(!undefinedConfigurationSoughtException::ToggleGenNewLvl())ABORT("expecting gen lvl to be true"); + if(!genericException::ToggleGenNewLvl())ABORT("expecting gen lvl to be true"); for(int i=0;iSetIndex(Index); const levelscript* LevelScript = GetLevelScript(Index); NewLevel->SetLevelScript(LevelScript); + DBG3("GeneratingDungeonLevel",Index,NewLevel->GetDungeon()->GetLevelDescription(Index, true).CStr()); if(Visual) { @@ -136,9 +139,9 @@ truth dungeon::PrepareLevel(int Index, truth Visual) if(*NewLevel->GetLevelScript()->GenerateMonsters()) NewLevel->GenerateNewMonsters(NewLevel->GetIdealPopulation(), false); - if(undefinedConfigurationSoughtException::ToggleGenNewLvl())ABORT("expecting gen lvl to be false"); + if(genericException::ToggleGenNewLvl())ABORT("expecting gen lvl to be false"); return false; // new level is ok - }catch(undefinedConfigurationSoughtException){ + }catch(const genericException& e){ // cleanup if(NewLevel)delete NewLevel; //TODO is this enough to clean it in a whole? if(EnterImage)delete EnterImage; From 055c4ccadea10646f0a8e3c647844ba53d112316 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 10 Apr 2020 20:59:42 -0300 Subject: [PATCH 101/235] new env vars: IVAN_DebugStayOnDungeonLevel for auto play. IVAN_DebugGenDungeonLevelLoopID and IVAN_DebugGenDungeonLevelLoopMax to test dungeon generation; --- FeLib/Include/dbgmsgproj.h | 2 +- Main/Source/char.cpp | 5 ++++- Main/Source/dungeon.cpp | 34 +++++++++++++++++++++++----------- Main/Source/main.cpp | 11 +++++++---- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/FeLib/Include/dbgmsgproj.h b/FeLib/Include/dbgmsgproj.h index 59b830dc3..38f831dbd 100644 --- a/FeLib/Include/dbgmsgproj.h +++ b/FeLib/Include/dbgmsgproj.h @@ -25,7 +25,7 @@ #include "dbgmsg.h" - /******************************************************************************************** + /************************************************************ * CUSTOM / PROJECT SPECIFIC, modify at will ************************************************************/ #ifndef DBGMSG_OBJ //do NOT define DBGMSG_OBJ this in your project cpp files! diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index e734992c5..e494a9432 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3133,6 +3133,8 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) /** * navigate the unknown dungeon */ + festring fsDL;fsDL<GetIndex()<GetIndex(); + festring fsStayOnDL;{const char* pc = std::getenv("IVAN_DebugStayOnDungeonLevel");if(pc!=NULL)fsStayOnDL< v2Exits; std::vector v2Altars; if(v2KeepGoingTo.Is0()){ DBG1("TryNewMoveTarget"); @@ -3160,7 +3162,8 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) olterrain* olt = lsqr->GetOLTerrain(); if(olt && (olt->GetConfig() == STAIRS_UP || olt->GetConfig() == STAIRS_DOWN)){ - v2Exits.push_back(v2(lsqr->GetPos())); DBGSV2(v2Exits[v2Exits.size()-1]); + if(fsDL!=fsStayOnDL) + v2Exits.push_back(v2(lsqr->GetPos())); DBGSV2(v2Exits[v2Exits.size()-1]); } altar* Altar = dynamic_cast(olt); diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp index 74bd7d8da..63d68ab1c 100644 --- a/Main/Source/dungeon.cpp +++ b/Main/Source/dungeon.cpp @@ -90,19 +90,26 @@ truth dungeon::PrepareLevel(int Index, truth Visual) } else { - DBG2("GeneratingDungeonLevel",Index); - int iRetryMax=10; + festring fsGenLoopDL;{const char* pc = std::getenv("IVAN_DebugGenDungeonLevelLoopID");if(pc!=NULL)fsGenLoopDL<SetDungeon(this); NewLevel->SetIndex(Index); const levelscript* LevelScript = GetLevelScript(Index); NewLevel->SetLevelScript(LevelScript); - DBG3("GeneratingDungeonLevel",Index,NewLevel->GetDungeon()->GetLevelDescription(Index, true).CStr()); + DBG7("GeneratingDungeonLevel",NewLevel->GetDungeon()->GetIndex(),Index,fsDL.CStr(),fsGenLoopDL.CStr(),i,NewLevel->GetDungeon()->GetLevelDescription(Index, true).CStr()); if(Visual) { @@ -114,10 +121,12 @@ truth dungeon::PrepareLevel(int Index, truth Visual) game::SetEnterTextDisplacement(Displacement); game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + CONST_S("...\n\nThis may take some time, please wait."), - Displacement, WHITE, false, true, &game::BusyAnimation); + Displacement, WHITE, false, + true, &game::BusyAnimation); game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + CONST_S("...\n\nPress any key to continue."), - Displacement, WHITE, game::GetAutoPlayMode()GetLevelScript()->GenerateMonsters()) NewLevel->GenerateNewMonsters(NewLevel->GetIdealPopulation(), false); - if(genericException::ToggleGenNewLvl())ABORT("expecting gen lvl to be false"); - return false; // new level is ok + if(genericException::ToggleGenNewLvl())ABORT("expecting gen lvl to become: false"); + if(fsGenLoopDL!=fsDL) + return false; // new level is ok }catch(const genericException& e){ // cleanup - if(NewLevel)delete NewLevel; //TODO is this enough to clean it in a whole? - if(EnterImage)delete EnterImage; + //TODO it is not working well, memory usage keeps increasing... + if(NewLevel ){delete NewLevel ;NewLevel=NULL;} + if(EnterImage){delete EnterImage;EnterImage=NULL;} //retry } - } + } //for() + if(fsGenLoopDL==fsDL)ABORT("Generating dungeon loop test completed."); ABORT("Generating new level failed after %d retries, aborting...",iRetryMax); } } diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index d375472af..9344b9320 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -135,10 +135,13 @@ int main(int argc, char** argv) // std::cout << "--genmvkeys Generate custom move keys cfg file. " << std::endl; std::cout << "--version Show current game version. " << std::endl; std::cout << std::endl; - std::cout << "Environment Variables (only work if set to 'true'):" << std::endl; - std::cout << "IVAN_SHOWFPS=true # show FPS at top right" << std::endl; - std::cout << "IVAN_DebugShowTinyDungeon=true #DEBUG always show tiny dungeon above stretched one" << std::endl; - std::cout << "IVAN_LISTDRAWABOVE=true #DEBUG output the draw above priority list to text console terminal" << std::endl; + std::cout << "Environment Variables:" << std::endl; + std::cout << "IVAN_SHOWFPS=[true] # show FPS at top right" << std::endl; + std::cout << "IVAN_DebugShowTinyDungeon=[true] #DEBUG always show tiny dungeon above stretched one" << std::endl; + std::cout << "IVAN_LISTDRAWABOVE=[true] #DEBUG output the draw above priority list to text console terminal" << std::endl; + std::cout << "IVAN_DebugGenDungeonLevelLoopID=[DungeonLevelIndex] #DEBUG DungeonLevelIndex must be an integer matching a some dungeon level" << std::endl; + std::cout << "IVAN_DebugGenDungeonLevelLoopMax=[integer] #DEBUG generate the dungeon level how many times" << std::endl; + std::cout << "IVAN_DebugStayOnDungeonLevel=[DungeonLevelIndex] #DEBUG auto play AI will not leave that Dungeon Level after entering it" << std::endl; return 0; } From d25f5073c0cca4e4ffe6dca7e41052f64d233531 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 10 Apr 2020 21:25:53 -0300 Subject: [PATCH 102/235] fix for IVAN_DebugStayOnDungeonLevel with dbgmsg enabled; --- Main/Source/char.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index e494a9432..04ba6ea6a 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3162,8 +3162,10 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) olterrain* olt = lsqr->GetOLTerrain(); if(olt && (olt->GetConfig() == STAIRS_UP || olt->GetConfig() == STAIRS_DOWN)){ - if(fsDL!=fsStayOnDL) - v2Exits.push_back(v2(lsqr->GetPos())); DBGSV2(v2Exits[v2Exits.size()-1]); + if(fsDL!=fsStayOnDL){ + v2Exits.push_back(v2(lsqr->GetPos())); + DBGSV2(v2Exits[v2Exits.size()-1]); + } } altar* Altar = dynamic_cast(olt); From 6e0183aaf11d30e0dfe9e613c4eb8c820977ee40 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 11 Apr 2020 00:23:37 -0300 Subject: [PATCH 103/235] Fixed a crash where an earthquake could destroy an alchemy wand dropped on the ground that could have valuable items nearby but no character on it's square. By having no beam effect owner (no one zapped or applied it), it would crash. --- Main/Source/game.cpp | 4 +++- Main/Source/item.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 619c56830..bda236824 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -353,8 +353,10 @@ void game::RemoveTrapID(ulong ID) if(itr == TrapIDMap.end() || itr->second == NULL){ if(!bugfixdp::IsFixing()) ABORT("AlreadyErased:TrapID %lu",ID); - }else + }else{ + DBG1(ID); TrapIDMap.erase(itr); + } } } void game::UpdateTrapID(entity* Trap, ulong ID) diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp index e79464304..0458b4b1c 100644 --- a/Main/Source/item.cpp +++ b/Main/Source/item.cpp @@ -404,7 +404,7 @@ truth item::Alchemize(character* Midas, stack* CurrentStack) long Price = GetTruePrice(); - if(Price) + if(Midas && Price) { Price /= 4; /* slightly lower than with 10 Cha */ ADD_MESSAGE("Gold pieces clatter on the floor."); From f487229146632c1b41317bfd60198c533c6baa03 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 11 Apr 2020 02:39:25 -0300 Subject: [PATCH 104/235] character::TrapData consistency validation applied/revised/reworked everywhere necessary; --- Main/Include/char.h | 1 + Main/Source/bodypart.cpp | 1 + Main/Source/char.cpp | 100 +++++++++++++++++++++++---------------- Main/Source/game.cpp | 3 ++ Main/Source/human.cpp | 1 + Main/Source/level.cpp | 8 +++- Main/Source/miscitem.cpp | 1 + Main/Source/nonhuman.cpp | 2 + Main/Source/traps.cpp | 1 + 9 files changed, 76 insertions(+), 42 deletions(-) diff --git a/Main/Include/char.h b/Main/Include/char.h index 732cc0d34..1711c9feb 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -1125,6 +1125,7 @@ class character : public entity, public id festring GetTrapDescription() const; truth TryToUnStickTraps(v2); void RemoveTrap(ulong); + void ValidateTrapData(); void AddTrap(ulong, ulong); truth IsStuckToTrap(ulong) const; void RemoveTraps(); diff --git a/Main/Source/bodypart.cpp b/Main/Source/bodypart.cpp index 7ddf08815..4f2a56833 100644 --- a/Main/Source/bodypart.cpp +++ b/Main/Source/bodypart.cpp @@ -3537,6 +3537,7 @@ void bodypart::UpdateFlags() else Flags &= ~BADLY_HURT; + Master->ValidateTrapData(); if(Master->BodyPartIsStuck(GetBodyPartIndex())) Flags |= STUCK; else diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 04ba6ea6a..e6c862442 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1566,6 +1566,7 @@ truth character::TryMove(v2 MoveVector, truth Important, truth Run, truth* pbWai } else { + ValidateTrapData(); if(IsPlayer() && !IsStuck() && GetLevel()->IsOnGround() && game::TruthQuestion(CONST_S("Do you want to leave ") + game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex()) @@ -2602,6 +2603,7 @@ truth character::CheckDeath(cfestring& Msg, ccharacter* Murderer, ulong DeathFla ++SpecifierParts; } + ValidateTrapData(); if(!(DeathFlags & IGNORE_TRAPS) && IsStuck()) { if(SpecifierParts++) @@ -3043,6 +3045,7 @@ truth character::AutoPlayAIDropThings() bool character::IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern) { if(!it->CanBeSeenBy(this))return false; + ValidateTrapData(); if(!it->IsPickable(this))return false; if(it->GetSquaresUnder()!=1)return false; //avoid big corpses 2x2 @@ -3940,6 +3943,7 @@ void character::BeKicked(character* Kicker, item* Boot, bodypart* Leg, v2 HitPos truth character::CheckBalance(double KickDamage) { + ValidateTrapData(); return !CanMove() || IsStuck() || !KickDamage @@ -4241,7 +4245,8 @@ truth character::CheckForUsefulItemsOnGround(truth CheckFood) itemvector ItemVector; GetStackUnder()->FillItemVector(ItemVector); - for(uint c = 0; c < ItemVector.size(); ++c) + ValidateTrapData(); + for(uint c = 0; c < ItemVector.size(); ++c){ if(ItemVector[c]->CanBeSeenBy(this) && ItemVector[c]->IsPickable(this)) { if(!(CommandFlags & DONT_CHANGE_EQUIPMENT) @@ -4252,6 +4257,7 @@ truth character::CheckForUsefulItemsOnGround(truth CheckFood) && TryToConsume(ItemVector[c])) return true; } + } return false; } @@ -4384,6 +4390,8 @@ truth character::Displace(character* Who, truth Forced) else Danger /= 1 << -PriorityDifference; + ValidateTrapData(); + Who->ValidateTrapData(); if(IsSmall() && Who->IsSmall() && (Forced || Danger > 1. || !(Who->IsPlayer() || Who->IsBadPath(GetPos()))) && !IsStuck() && !Who->IsStuck() @@ -10052,8 +10060,9 @@ truth character::CheckForFoodInSquare(v2 Pos) lsquare* Square = Level->GetLSquare(Pos); stack* Stack = Square->GetStack(); - if(Stack->GetItems()) - for(stackiterator i = Stack->GetBottom(); i.HasItem(); ++i) + if(Stack->GetItems()){ + ValidateTrapData(); + for(stackiterator i = Stack->GetBottom(); i.HasItem(); ++i){ if(i->IsPickable(this) && i->CanBeSeenBy(this) && i->CanBeEatenByAI(this) @@ -10063,6 +10072,8 @@ truth character::CheckForFoodInSquare(v2 Pos) SetGoingTo(Pos); return MoveTowardsTarget(false); } + } + } } return false; @@ -12086,26 +12097,16 @@ truth character::IsUsingWeaponOfCategory(int Category) const truth character::TryToUnStickTraps(v2 Dir) { - if(!TrapData) - return true; - - std::vector TrapVector; - - for(const trapdata* T = TrapData; T; T = T->Next) - TrapVector.push_back(*TrapData); - - for(uint c = 0; c < TrapVector.size(); ++c) + ValidateTrapData(); + for(trapdata* T = TrapData; T; T = T->Next) if(IsEnabled()) { - entity* Trap = game::SearchTrap(TrapVector[c].TrapID); - - if(!Trap) - ABORT("Trap ID %lu not found, character name: %s, x=%d y=%d",TrapVector[c].TrapID,GetName(INDEFINITE).CStr(),GetPos().X,GetPos().Y); - - if(Trap->GetVictimID() == GetID() && Trap->TryToUnStick(this, Dir)) + entity* Trap = game::SearchTrap(T->TrapID); + if(Trap && Trap->GetVictimID() == GetID() && Trap->TryToUnStick(this, Dir)) break; } + ValidateTrapData(); return !TrapData && IsEnabled(); } @@ -12116,20 +12117,37 @@ struct trapidcomparer ulong ID; }; +void character::ValidateTrapData() +{ + for(trapdata* T = TrapData; T;) + { + if(!game::SearchTrap(T->TrapID)){ + trapdata* ToDel = T; + if(TrapData==ToDel) + TrapData=T->Next; + T = T->Next; + delete ToDel; + }else{ + T = T->Next; + } + } +} + void character::RemoveTrap(ulong ID) { - if(!TrapData) //everywhere calling this must be sure it can be called! - ABORT("can't remove a non existing trap..."); - + ValidateTrapData(); trapdata*& T = ListFind(TrapData, trapidcomparer(ID)); - trapdata* ToDel = T; - T = T->Next; - delete ToDel; + if(T){ + trapdata* ToDel = T; + T = T->Next; + delete ToDel; + } doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange); } void character::AddTrap(ulong ID, ulong BodyParts) { + ValidateTrapData(); trapdata*& T = ListFind(TrapData, trapidcomparer(ID)); if(T) @@ -12231,25 +12249,27 @@ festring character::GetTrapDescription() const } } - if(Index <= 3) - { - TrapStack[0].first->AddTrapName(Desc, TrapStack[0].second); - - if(Index == 2) + if(Index > 0){ + if(Index <= 3) { - Desc << " and "; - TrapStack[1].first->AddTrapName(Desc, TrapStack[1].second); - } - else if(Index == 3) - { - Desc << ", "; - TrapStack[1].first->AddTrapName(Desc, TrapStack[1].second); - Desc << " and "; - TrapStack[2].first->AddTrapName(Desc, TrapStack[2].second); + TrapStack[0].first->AddTrapName(Desc, TrapStack[0].second); + + if(Index == 2) + { + Desc << " and "; + TrapStack[1].first->AddTrapName(Desc, TrapStack[1].second); + } + else if(Index == 3) + { + Desc << ", "; + TrapStack[1].first->AddTrapName(Desc, TrapStack[1].second); + Desc << " and "; + TrapStack[2].first->AddTrapName(Desc, TrapStack[2].second); + } } + else + Desc << "lots of traps"; } - else - Desc << "lots of traps"; return Desc; } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index bda236824..ab584c473 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -6760,6 +6760,7 @@ double game::GetGameSituationDanger() { double SituationDanger = 0; character* Player = GetPlayer(); + Player->ValidateTrapData(); truth PlayerStuck = Player->IsStuck(); v2 PlayerPos = Player->GetPos(); character* TruePlayer = Player; @@ -6774,6 +6775,7 @@ double game::GetGameSituationDanger() if(Enemy->IsEnabled() && Enemy->CanAttack() && (Enemy->CanMove() || Enemy->GetPos().IsAdjacent(PlayerPos))) { + Enemy->ValidateTrapData(); truth EnemyStuck = Enemy->IsStuck(); v2 EnemyPos = Enemy->GetPos(); truth Sees = TruePlayer->CanBeSeenBy(Enemy); @@ -6794,6 +6796,7 @@ double game::GetGameSituationDanger() v2 FriendPos = Friend->GetPos(); truth Sees = TrueEnemy->CanBeSeenBy(Friend); + Friend->ValidateTrapData(); if(Friend->IsStuck()) { Friend = Friend->Duplicate(IGNORE_PROHIBITIONS); diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 4feea455b..4e40ce058 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -2657,6 +2657,7 @@ truth humanoid::CanWield() const truth humanoid::CheckBalance(double KickDamage) { + ValidateTrapData(); return !CanMove() || IsStuck() || !KickDamage diff --git a/Main/Source/level.cpp b/Main/Source/level.cpp index 1b99b16d4..84979f00b 100644 --- a/Main/Source/level.cpp +++ b/Main/Source/level.cpp @@ -1254,13 +1254,15 @@ truth level::CollectCreatures(charactervector& CharacterArray, character* Leader if(!AllowHostiles) for(c = 0; c < game::GetTeams(); ++c) if(Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) - for(character* p : game::GetTeam(c)->GetMember()) + for(character* p : game::GetTeam(c)->GetMember()){ + p->ValidateTrapData(); if(p->IsEnabled() && Leader->CanBeSeenBy(p) && Leader->SquareUnderCanBeSeenBy(p, true) && p->CanFollow()) { ADD_MESSAGE("You can't escape when there are hostile creatures nearby."); return false; } + } truth TakeAll = true; @@ -1274,7 +1276,8 @@ truth level::CollectCreatures(charactervector& CharacterArray, character* Leader for(c = 0; c < game::GetTeams(); ++c) if(game::GetTeam(c) == Leader->GetTeam() || Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) - for(character* p : game::GetTeam(c)->GetMember()) + for(character* p : game::GetTeam(c)->GetMember()){ + p->ValidateTrapData(); if(p->IsEnabled() && p != Leader && (TakeAll || (Leader->CanBeSeenBy(p) @@ -1292,6 +1295,7 @@ truth level::CollectCreatures(charactervector& CharacterArray, character* Leader p->Remove(); } } + } return true; } diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index 1c19c1bfe..d9752a4d3 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -1598,6 +1598,7 @@ void beartrap::StepOnEffect(character* Stepper) truth beartrap::CheckPickUpEffect(character* Picker) { + Picker->ValidateTrapData(); if(Picker->IsStuckToTrap(GetTrapID())) { if(Picker->IsPlayer()) diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp index 7635f78ee..98450b755 100644 --- a/Main/Source/nonhuman.cpp +++ b/Main/Source/nonhuman.cpp @@ -2480,6 +2480,7 @@ void spider::GetAICommand() if(NearestChar) { + NearestChar->ValidateTrapData(); if(NearestChar->IsStuck() || GetConfig() == ARANEA || (GetConfig() == PHASE && !CanBeSeenBy(NearestChar))) SetGoingTo(NearestChar->GetPos()); @@ -2719,6 +2720,7 @@ void lobhse::GetAICommand() if(NearestChar) { + NearestChar->ValidateTrapData(); if(NearestChar->IsStuck()) SetGoingTo(NearestChar->GetPos()); else diff --git a/Main/Source/traps.cpp b/Main/Source/traps.cpp index e64c53e25..d870f3c91 100644 --- a/Main/Source/traps.cpp +++ b/Main/Source/traps.cpp @@ -57,6 +57,7 @@ truth web::TryToTearDown(character* Actor,int Modifier) { //if(GetLSquareUnder()->GetPos()==Actor->GetPos())C->RemoveTrap(GetTrapID());else character* C = GetLSquareUnder()->GetCharacter(); + if(C)C->ValidateTrapData(); if(C && C->IsStuckToTrap(GetTrapID())) C->RemoveTrap(GetTrapID()); TrapData.VictimID = 0; From 507006c0dd7ea608cdbb368db6e7dbaa1c0659b1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 11 Apr 2020 19:20:36 -0300 Subject: [PATCH 105/235] AutoPlay: make it use items more often and added Apply capability; item::SendNewDrawAndMemorizedUpdateRequest() workaround for a bug when a item is nowhere to be found; --- Main/Include/human.h | 2 +- Main/Source/char.cpp | 45 ++++++++++++----- Main/Source/gear.cpp | 1 + Main/Source/human.cpp | 106 ++++++++++++++++++++++++++++----------- Main/Source/item.cpp | 8 ++- Main/Source/miscitem.cpp | 15 +++--- 6 files changed, 124 insertions(+), 53 deletions(-) diff --git a/Main/Include/human.h b/Main/Include/human.h index 29e49806b..a9302f067 100644 --- a/Main/Include/human.h +++ b/Main/Include/human.h @@ -172,7 +172,7 @@ CHARACTER(humanoid, character) truth HasSadistWeapon() const; truth CheckAIZapOpportunity(); virtual truth HasSadistAttackMode() const; - truth AutoPlayAIequip(); + truth AutoPlayAIequipConsumeZapReadApply(); static v2 GetSilhouetteWhere(){return SilhouetteWhere;} static v2 GetSilhouetteWhereDefault(){return SilhouetteWhereDefault;} static void SetSilhouetteWhere(v2 pos){SilhouetteWhere=pos;} diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index e6c862442..147e54703 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3015,21 +3015,36 @@ truth character::AutoPlayAIDropThings() if(iDirOk==-1){iDirOk=clock()%8;DBG2("RandomDir",iDirOk);}DBGLN; //TODO should just drop may be? unless hitting w/e is there could help - if(iDirOk>-1){DBG2("KickOrThrow",iDirOk); - static itemcontainer* itc;itc = dynamic_cast(dropMe);DBGLN; - static humanoid* h;h = dynamic_cast(this);DBGLN; - DBG8("CanKickLockedChest",lsqrDropAt,itc,itc?itc->IsLocked():-1,CanKick(),h,h?h->GetLeftLeg():0,h?h->GetRightLeg():0); - if(lsqrDropAt && itc && itc->IsLocked() && CanKick() && h && h->GetLeftLeg() && h->GetRightLeg()){DBGLN; - dropMe->MoveTo(lsqrDropAt->GetStack());DBGLN; //drop in front.. - Kick(lsqrDropAt,iDirOk,true);DBGLN; // ..to kick it - }else{DBGLN; - ThrowItem(iDirOk, dropMe); DBG5("DropThrow",iDirOk,dropMe->GetName(DEFINITE).CStr(),dropMe->GetTruePrice(),dropMe->GetWeight()); + bool bApplyDropped=false; //or vanished + if(dropMe->IsAppliable(this) && dropMe->Apply(this)){ + static itemvector ivChkDrop;ivChkDrop.clear(); + GetStack()->FillItemVector(ivChkDrop); + bApplyDropped=true; + for(int i6=0;i6MoveTo(GetLSquareUnder()->GetStack());DBGLN; //just drop } + + if(!bApplyDropped){ + if(iDirOk>-1){DBG2("KickOrThrow",iDirOk); + static itemcontainer* itc;itc = dynamic_cast(dropMe);DBGLN; + static humanoid* h;h = dynamic_cast(this);DBGLN; + DBG8("CanKickLockedChest",lsqrDropAt,itc,itc?itc->IsLocked():-1,CanKick(),h,h?h->GetLeftLeg():0,h?h->GetRightLeg():0); + if(lsqrDropAt && itc && itc->IsLocked() && CanKick() && h && h->GetLeftLeg() && h->GetRightLeg()){DBGLN; + dropMe->MoveTo(lsqrDropAt->GetStack());DBGLN; //drop in front.. + Kick(lsqrDropAt,iDirOk,true);DBGLN; // ..to kick it + }else{DBGLN; + ThrowItem(iDirOk, dropMe); DBG5("DropThrow",iDirOk,dropMe->GetName(DEFINITE).CStr(),dropMe->GetTruePrice(),dropMe->GetWeight()); + } + }else{DBGLN; + dropMe->MoveTo(GetLSquareUnder()->GetStack());DBGLN; //just drop + } - v2LastDropPlayerWasAt=GetPos();DBGSV2(v2LastDropPlayerWasAt); + v2LastDropPlayerWasAt=GetPos();DBGSV2(v2LastDropPlayerWasAt); + } return true; } @@ -3064,8 +3079,11 @@ truth character::AutoPlayAIEquipAndPickup(bool bPlayerHasLantern) { static humanoid* h;h = dynamic_cast(this); if(h==NULL)return false; + // other invalid equippers + if(dynamic_cast(this) != NULL)return false; + if(dynamic_cast(this) != NULL)return false; - if(h->AutoPlayAIequip()) + if(h->AutoPlayAIequipConsumeZapReadApply()) return true; if(GetBurdenState()!=OVER_LOADED){ DBG4(CommandFlags&DONT_CHANGE_EQUIPMENT,this,GetNameSingular().CStr(),GetSquareUnder()); @@ -3237,6 +3255,7 @@ truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) vit[n]->IsArmor (this) || vit[n]->IsAmulet (this) || vit[n]->IsZappable(this) || //wands + vit[n]->IsAppliable(this) || //mines, beartraps etc vit[n]->IsRing (this) || vit[n]->IsReadable(this) || //books and scrolls vit[n]->IsDrinkable(this)|| //potions and vials diff --git a/Main/Source/gear.cpp b/Main/Source/gear.cpp index 48642ae6a..612ad18bb 100644 --- a/Main/Source/gear.cpp +++ b/Main/Source/gear.cpp @@ -170,6 +170,7 @@ truth pickaxe::Apply(character* User) } int Dir = game::DirectionQuestion(CONST_S("What direction do you want to dig? [press a direction key]"), false); + if(User->IsPlayerAutoPlay())Dir = clock()%8; if(Dir == DIR_ERROR) return false; diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 4e40ce058..a1706f568 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -3657,24 +3657,29 @@ long skeleton::GetBodyPartVolume(int I) const return 0; } -truth humanoid::AutoPlayAIequip() +truth humanoid::AutoPlayAIequipConsumeZapReadApply() { + bool bDidSomething=false; + + ///////////////////////////////// WIELD + item* iL = GetEquipment(LEFT_WIELDED_INDEX); item* iR = GetEquipment(RIGHT_WIELDED_INDEX); //every X turns remove all equipments bool bTryWieldNow=false; static int iLastReEquipAllTurn=-1; - if(game::GetTurn()>(iLastReEquipAllTurn+150)){ DBG2(game::GetTurn(),iLastReEquipAllTurn); + if(game::GetTurn()>(iLastReEquipAllTurn+100)){ DBG2(game::GetTurn(),iLastReEquipAllTurn); iLastReEquipAllTurn=game::GetTurn(); + DBG1("UnequipAll"); for(int i=0;iMoveTo(GetStack());SetEquipment(i,NULL);} //eq is moved to end of stack! if(iL==eq)iL=NULL; if(iR==eq)iR=NULL; } -// if(iL!=NULL){iL->MoveTo(GetStack());iL=NULL;SetEquipment(LEFT_WIELDED_INDEX ,NULL);DBGLN;} -// if(iR!=NULL){iR->MoveTo(GetStack());iR=NULL;SetEquipment(RIGHT_WIELDED_INDEX,NULL);DBGLN;} +// if(iL!=NULL){iL->MoveTo(GetStack());iL=NULL;SetEquipment(LEFT_WIELDED_INDEX ,NULL);} +// if(iR!=NULL){iR->MoveTo(GetStack());iR=NULL;SetEquipment(RIGHT_WIELDED_INDEX,NULL);} bTryWieldNow=true; } @@ -3698,6 +3703,7 @@ truth humanoid::AutoPlayAIequip() for(uint c = 0; c < vitEqW.size(); ++c){ if(vitEqW[c]->IsWeapon(this) && vitEqW[c]->IsTwoHanded()){ DBG1(vitEqW[c]->GetNameSingular().CStr()); vitEqW[c]->RemoveFromSlot(); + DBG2("Wield2hd",vitEqW[c]->GetNameSingular().CStr()); SetEquipment(clock()%2==0 ? LEFT_WIELDED_INDEX : RIGHT_WIELDED_INDEX, vitEqW[c]); //DBG3("Wield",iEqIndex,vitEqW[c]->GetName(DEFINITE).CStr()); bDoneLR=true; break; @@ -3727,7 +3733,8 @@ truth humanoid::AutoPlayAIequip() (vitEqW[c]->IsWeapon(this) && !vitEqW[c]->IsTwoHanded()) || vitEqW[c]->IsShield(this) - ){ DBG1(vitEqW[c]->GetNameSingular().CStr()); + ){ + DBG2("WieldDual",vitEqW[c]->GetNameSingular().CStr()); vitEqW[c]->RemoveFromSlot(); SetEquipment(iChk, vitEqW[c]); bDoneLR=true; @@ -3742,14 +3749,15 @@ truth humanoid::AutoPlayAIequip() //every X turns try to use stuff from inv static int iLastTryToUseInvTurn=-1; - if(game::GetTurn()>(iLastTryToUseInvTurn+5)){ DBG2(game::GetTurn(),iLastTryToUseInvTurn); + if(game::GetTurn()>(iLastTryToUseInvTurn+5)){ + DBG2(game::GetTurn(),iLastTryToUseInvTurn); iLastTryToUseInvTurn=game::GetTurn(); //////////////////////////////// consume food/drink { //TODO let this happen for non-human too? - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);DBGLN; - for(uint c = 0; c < vitEqW.size(); ++c){DBGLN; - if(clock()%3!=0 && GetHungerState() >= BLOATED)break;DBGLN; //randomly let it vomit and activate all related flows *eew* xD + static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(clock()%3!=0 && GetHungerState() >= BLOATED)break; //randomly let it vomit and activate all related flows *eew* xD //if(TryToConsume(vitEqW[c])) material* ConsumeMaterial = vitEqW[c]->GetConsumeMaterial(this); @@ -3760,18 +3768,21 @@ truth humanoid::AutoPlayAIequip() ConsumeItem(vitEqW[c], vitEqW[c]->GetConsumeMaterial(this)->GetConsumeVerb()) ){ DBG2("AutoPlayConsumed",vitEqW[c]->GetNameSingular().CStr()); - return true; - }DBGLN; + bDidSomething=true; + break; + } } } - //////////////////////////////// equip - {DBGLN; - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);DBGLN; - for(uint c = 0; c < vitEqW.size(); ++c){DBGLN; - if(TryToEquip(vitEqW[c],true)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); - return true; - }else{DBGLN; + //////////////////////////////// equip armor ring amulet etc + { + static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(TryToEquip(vitEqW[c],true)){ + DBG2("EquipItem",vitEqW[c]->GetNameSingular().CStr()); + bDidSomething=true; + break; + }else{ vitEqW[c]->MoveTo(GetStack()); //was dropped, get back, will be in the end of the stack! :) } } @@ -3779,7 +3790,8 @@ truth humanoid::AutoPlayAIequip() //////////////////////////////// zap static int iLastZapTurn=-1; - if(game::GetTurn()>(iLastZapTurn+100)){ DBG2(game::GetTurn(),iLastZapTurn); //every X turns try to use stuff from inv + if(game::GetTurn()>(iLastZapTurn+30)){ + DBG2(game::GetTurn(),iLastZapTurn); //every X turns try to use stuff from inv iLastZapTurn=game::GetTurn(); int iDir=clock()%(8+1); // index 8 is the macro YOURSELF already... if(iDir==8)iDir=YOURSELF; @@ -3787,19 +3799,25 @@ truth humanoid::AutoPlayAIequip() for(uint c = 0; c < vitEqW.size(); ++c){ if(!vitEqW[c]->IsZappable(this))continue; - if(vitEqW[c]->Zap(this, GetPos(), iDir)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs - return true; - } - - if(vitEqW[c]->Apply(this)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); - return true; + if(vitEqW[c]->IsZapWorthy(this)){ + if(vitEqW[c]->Zap(this, GetPos(), iDir)){ + DBG2(iLastZapTurn,vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs + bDidSomething=true; + break; + } + }else{ + if(vitEqW[c]->Apply(this)){ + DBG2(iLastZapTurn,vitEqW[c]->GetNameSingular().CStr()); + bDidSomething=true; + break; + } } } } //////////////////////////////// read books and scrolls static int iLastReadTurn=-1; - if(game::GetTurn()>(iLastReadTurn+50)){ DBG2(game::GetTurn(),iLastReadTurn); //every X turns try to use stuff from inv + if(game::GetTurn()>(iLastReadTurn+15)){ DBG2(game::GetTurn(),iLastReadTurn); //every X turns try to use stuff from inv iLastReadTurn=game::GetTurn(); static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); @@ -3809,7 +3827,9 @@ truth humanoid::AutoPlayAIequip() static holybook* hb;hb = dynamic_cast(vitEqW[c]); if(hb){ if(vitEqW[c]->Read(this)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs - return true; + DBG2(iLastReadTurn,vitEqW[c]->GetNameSingular().CStr()); + bDidSomething=true; + break; } } @@ -3820,14 +3840,42 @@ truth humanoid::AutoPlayAIequip() dynamic_cast(Scroll) || false //dummy ){ + DBG2(iLastReadTurn,vitEqW[c]->GetNameSingular().CStr()); Scroll->Read(this); - return true; + bDidSomething=true; + break; } } } + + //////////////////////////////// apply things + static int iLastApplyTurn=-1; + if(game::GetTurn()>(iLastApplyTurn+40)){ + DBG2(game::GetTurn(),iLastApplyTurn); //every X turns try to use stuff from inv + iLastApplyTurn=game::GetTurn(); + + static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); + static itemvector vitA;vitA.clear(); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(!vitEqW[c]->IsAppliable(this))continue; + if(vitEqW[c]->IsZappable(this))continue; //not here, see zap section + if(dynamic_cast(vitEqW[c]))continue; // too complex to make it auto work + if(dynamic_cast(vitEqW[c]))continue; // too complex to make it auto work + + vitA.push_back(vitEqW[c]); + } + + if(vitA.size()){ + item* itA = vitA[clock()%vitA.size()]; + DBG2(iLastApplyTurn,itA->GetNameSingular().CStr()); + itA->Apply(this); + bDidSomething=true; + } + } + } - return false; + return bDidSomething; } truth humanoid::CheckIfEquipmentIsNotUsable(int I) const diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp index 0458b4b1c..3798b74e1 100644 --- a/Main/Source/item.cpp +++ b/Main/Source/item.cpp @@ -1704,8 +1704,12 @@ void item::SendNewDrawAndMemorizedUpdateRequest() const if(Slot[c]) { lsquare* Square = GetLSquareUnder(c); - Square->SendNewDrawRequest(); - Square->SendMemorizedUpdateRequest(); + if(Square){ //TODO is this fix ok? let it crash elsewhere better to track the problem... + Square->SendNewDrawRequest(); + Square->SendMemorizedUpdateRequest(); + }else{ + DBG4("Is nowhere to be found, how!?",Square,GetNameSingular().CStr(),SquaresUnder); + } } } diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index d9752a4d3..a979f6a76 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -1178,7 +1178,7 @@ truth key::Apply(character* User) } v2 DirVect = game::GetDirectionVectorForKey(Key); - + if(DirVect != ERROR_V2 && User->GetArea()->IsValidPos(User->GetPos() + DirVect)) return GetLevel()->GetLSquare(User->GetPos() + DirVect)->TryKey(this, User); } @@ -1633,7 +1633,6 @@ truth stethoscope::Apply(character* Doctor) ABORT("Doctor is not here, man, but these pills taste just as good anyway."); int Dir = game::DirectionQuestion(CONST_S("What do you want to inspect? [press a direction key]"), false, true); - if(Dir == DIR_ERROR) return false; @@ -1669,8 +1668,8 @@ truth itemcontainer::ContentsCanBeSeenBy(ccharacter* Viewer) const truth mine::Apply(character* User) { - if(User->IsPlayer() && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") - + GetName(DEFINITE) + "? [y/N]")) + if(User->IsPlayer() && (!User->IsPlayerAutoPlay()) && + !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) return false; room* Room = GetRoom(); @@ -1704,8 +1703,8 @@ truth beartrap::Apply(character* User) return false; } - if(User->IsPlayer() - && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) + if(User->IsPlayer() && (!User->IsPlayerAutoPlay()) && + !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) return false; room* Room = GetRoom(); @@ -4066,8 +4065,8 @@ truth gastrap::Apply(character* User) return false; } - if(User->IsPlayer() - && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) + if(User->IsPlayer() && (!User->IsPlayerAutoPlay()) && + !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) return false; room* Room = GetRoom(); From a323ebb55a7fb1beb52f6a26fe6c76ba37a3b9c5 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 13 Apr 2020 22:08:13 -0300 Subject: [PATCH 106/235] All keys can be easily customized in-game now. --- Main/Include/command.h | 2 +- Main/Include/game.h | 3 + Main/Source/command.cpp | 24 ++--- Main/Source/game.cpp | 197 ++++++++++++++++++++++++++++++++++------ Main/Source/iconf.cpp | 77 +--------------- 5 files changed, 189 insertions(+), 114 deletions(-) diff --git a/Main/Include/command.h b/Main/Include/command.h index 61431fe50..48ff9f978 100644 --- a/Main/Include/command.h +++ b/Main/Include/command.h @@ -23,7 +23,7 @@ class command command(truth (*)(character*), cchar*, char, char, char, truth, truth = false); truth (*GetLinkedFunction() const)(character*) { return LinkedFunction; } cchar* GetDescription() const { return Description; } - char GetKey() const; + int GetKey() const; truth IsUsableInWilderness() const { return UsableInWilderness; } truth IsWizardModeFunction() const { return WizardModeFunction; } int SetCustomKey(int iKey){ int iKeyBkp=Key4; Key4 = iKey; return iKeyBkp; } diff --git a/Main/Include/game.h b/Main/Include/game.h index 48c5fd161..c9786dfbf 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -514,6 +514,9 @@ class game static int GetCurrentDungeonTurnsCount(){return iCurrentDungeonTurn;} static int GetSaveFileVersionHardcoded(); static void ValidateCommandKeys(char Key1,char Key2,char Key3); + static truth ConfigureCustomKeys(); + static truth CheckConflictingCmdKey(int iNewKey, int iIgnoreIndex); + static festring GetMoveKeyDesc(int i); static void LoadCustomCommandKeys(); private: static void UpdateCameraCoordinate(int&, int, int, int); diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index e268e8589..24e6e28f7 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -59,11 +59,11 @@ command::command(truth (*LinkedFunction)(character*), cchar* Description, char K game::ValidateCommandKeys(Key1,Key2,Key3); } -char command::GetKey() const +int command::GetKey() const { if(ivanconfig::IsSetupCustomKeys()){ if(Key4>0) - return (char)Key4; //TODO everything related should be integer now... + return Key4; } switch(ivanconfig::GetDirectionKeyMap()) @@ -246,16 +246,16 @@ truth commandsystem::IsForRegionSilhouette(int iIndex){ //see code generator hel return false; } -char findCmdKey(truth (*func)(character*)) +int findCmdKey(truth (*func)(character*)) { - char cKey=0; + int iKey=0; for(int i = 1; command* cmd = commandsystem::GetCommand(i); ++i) if(cmd->GetLinkedFunction()==func){ - cKey = cmd->GetKey(); + iKey = cmd->GetKey(); break; } - if(cKey==0)ABORT("can't find key for command."); //TODO how to show what command from *func??? - return cKey; + if(iKey==0)ABORT("can't find key for command."); //TODO how to show what command from *func??? + return iKey; } truth commandsystem::GoUp(character* Char) @@ -763,7 +763,7 @@ truth commandsystem::Quit(character* Char) truth commandsystem::Talk(character* Char) { - static char cmdKey = findCmdKey(&Talk); + static int cmdKey = findCmdKey(&Talk); if(!Char->CheckTalk()) return false; @@ -797,7 +797,7 @@ truth commandsystem::Talk(character* Char) else { static festring fsQ; - static bool bInitDummy=[](){fsQ<<"To whom do you wish to talk? [press a direction key or '"<CheckThrow()){ return false; diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index ab584c473..1fc77f2c5 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5815,6 +5815,15 @@ bonesghost* game::CreateGhost() return Ghost; } +int HexToInt(festring fs) +{ + std::string str = fs.CStr(); + std::string strHex = str.substr(str.size() -1 -4); // -1 is "\n" + int iVal=0; + sscanf(strHex.c_str(),"%x",&iVal); + return iVal; +} + void game::LoadCustomCommandKeys() { static festring fsFile = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; @@ -5822,47 +5831,181 @@ void game::LoadCustomCommandKeys() if(!fl)return; festring Line; - int index=0; static const int iBuffSz=0xFF; char str[iBuffSz]; - while(fgets(str, iBuffSz, fl)) - { - Line = str; - if(Line.IsEmpty())break; - - //std::cout << "Ln" << index << ", Read:'" << Line.CStr() <<"'"<< std::endl; - //game::MoveCustomCommandKey[index]=atoi(Line.CStr()); - - int iVal; - sscanf(Line.CStr(),"%x",&iVal); - game::MoveCustomCommandKey[index]=iVal; - - //std::cout << "ValueInt=" << game::MoveCustomCommandKey[index] << std::endl; - - index++; - if(index>7)break; //skip the last to keep as '.' - } - while(fgets(str, iBuffSz, fl)){ Line=str; command* cmd; for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c){ - festring::sizetype pos = Line.Find(cmd->GetDescription()); - if(pos==1){ - char ch = Line[Line.GetSize()-3]; // -3 skips '\n' and ending "'" - cmd->SetCustomKey((int)ch); //the last char between ' - DBG4(pos,ch,cmd->GetDescription(),Line.CStr()); -// ADD_MESSAGE("SYSTEM: set custom command key for \"%s\" '%c'", -// commandsystem::GetCommand(c)->GetDescription(), -// commandsystem::GetCommand(c)->GetKey()); //TODO this messes the gameplay message log... but is better than a popup? + if(Line.Find(cmd->GetDescription())==1){ // after " + cmd->SetCustomKey(HexToInt(Line)); + break; + } + } + + int iVal; + for(int c=0;c<8;c++){ + if(Line.Find(GetMoveKeyDesc(c))==1){ // after " + game::MoveCustomCommandKey[c]=HexToInt(Line); break; } + } + } + + fclose(fl); +} + +festring game::GetMoveKeyDesc(int i) +{ + switch(i){ + case 0: return "MoveKey Upper Left"; + case 1: return "MoveKey Up"; + case 2: return "MoveKey Upper Right"; + case 3: return "MoveKey Left"; + case 4: return "MoveKey Right"; + case 5: return "MoveKey Lower Left"; + case 6: return "MoveKey Down"; + case 7: return "MoveKey Lower Right"; + //case 8: return "MoveKey Stop"; //skip the last to keep as '.' + default: ABORT("invalid move key index %d",i); + } + return ""; //dummy +} + +truth game::CheckConflictingCmdKey(int iNewKey, int iIgnoreIndex) +{ + //TODO these SYSTEM messages messes the gameplay message log... but is better than a popup? + + command *cmd; + for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c){ + if(c==iIgnoreIndex)continue; + if(iNewKey==cmd->GetKey()){ + ADD_MESSAGE("SYSTEM: conflicting key '%c'(code is %d or 0x%X) with command \"%s\", retry...", + iNewKey,iNewKey,iNewKey,cmd->GetDescription()); + return true; + } + } + + for(int c=0;c<8;c++){ + if(c==iIgnoreIndex)continue; + if(iNewKey == game::MoveCustomCommandKey[c]){ + ADD_MESSAGE("SYSTEM: conflicting key '%c'(code is %d or 0x%X) with movement command \"%s\", retry...", + iNewKey,iNewKey,iNewKey,GetMoveKeyDesc(c).CStr()); + return true; + } + } + + return false; +} + +festring IntToHexStr(int i) +{ + static char hexbuf[100]; + sprintf(hexbuf, "0x%04X", i); + festring fs;fs=hexbuf; + return fs; +} + +truth game::ConfigureCustomKeys() +{ + festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; + + festring fsFlBkp=fsFl+".bkp"; + std::ifstream src(fsFl.CStr() , std::ios::binary); + std::ofstream dst(fsFlBkp.CStr(), std::ios::binary); + dst << src.rdbuf(); + + FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); + felist fel(CONST_S("Configure custom keys:")); + bool bRet=true; + command* cmd; + while(true){ + fel.Empty(); + int iMoveKeyStart=0; + bool bWizIni=false; + festring fsEntry; + for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c){ + fsEntry=cmd->GetDescription(); + fsEntry.Resize(60); + fsEntry<<"'"<<(char)cmd->GetKey()<<"' "; + fsEntry<GetKey()); + + if(!bWizIni && cmd->IsWizardModeFunction()){ + fel.AddEntry("Wizard mode keys:", DARK_GRAY, 20, NO_IMAGE, false); + bWizIni=true; + } + + fel.AddEntry(fsEntry, LIGHT_GRAY, 0, NO_IMAGE, true); + iMoveKeyStart++; } + fel.AddEntry("Movement keys:", DARK_GRAY, 20, NO_IMAGE, false); + for(int c=0;c<8;c++){ + fsEntry=GetMoveKeyDesc(c); + fsEntry.Resize(60); + fsEntry<<"'"<<(char)game::MoveCustomCommandKey[c]<<"' "; + fsEntry<=iMoveKeyStart) + iMvKey = Select-iMoveKeyStart; + if(iMvKey){ + fsAsk<GetDescription()<<"\""; + fsAsk<GetKey(); + } + fsAsk<<"' "; + fsAsk<GetKey()); + fsAsk<<")"; + + iNewKey=game::AskForKeyPress(fsAsk); + if(iNewKey==KEY_ESC){bIgnore=true;break;} + + if(!CheckConflictingCmdKey(iNewKey,Select))break; + } + if(bIgnore)continue; + + if(!bRet)break; + + festring fsWriteLine; + if(Select>=iMoveKeyStart){ + game::MoveCustomCommandKey[Select-iMoveKeyStart]=iNewKey; + fsWriteLine=GetMoveKeyDesc(Select-iMoveKeyStart); + }else{ + commandsystem::GetCommand(Select+1)->SetCustomKey(iNewKey); + fsWriteLine=cmd->GetDescription(); + } + + //save + fprintf(fl, "\"%s\"=0x%04X\n", fsWriteLine.CStr(), iNewKey); + fflush(fl); } fclose(fl); + + if(bRet)game::LoadCustomCommandKeys(); //this here is more to validate if all went ok + return bRet; } + /** * check all other command keys versus MOVEMENT command keys on their specific branch */ diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 7f1dffe2b..7e0759dcd 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -288,8 +288,8 @@ cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", DIR_NORM, 3, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SetupCustomKeys( "SetupCustomKeys", - "* Movement control custom keys", //TODO all keys one day, and let it work on main menu - "Let you assign all 8 movement keys to any available key of your preference. All other command keys will remain as assigned by the scheme above until you change them externally on the newly generated config file and restart the game. This global configuration won't work at main menu, load/start some game.", + "* Custom command and movement", //TODO all keys one day, and let it work on main menu + "Lets you assign all commands to any key of your preference. This global configuration won't work at main menu, load/start some game.", false, &configsystem::NormalTruthDisplayer, &configsystem::NormalTruthChangeInterface, @@ -551,77 +551,6 @@ void ivanconfig::AltSilhouetteDisplayer(const cycleoption* O, festring& Entry) } } - -truth ConfigureCustomKeys() -{ - festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; - - festring fsFlBkp=fsFl+".bkp"; - std::ifstream src(fsFl.CStr() , std::ios::binary); - std::ofstream dst(fsFlBkp.CStr(), std::ios::binary); - dst << src.rdbuf(); - - FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); - int iKey; - festring fsAsk; - bool bRet=true; - int index=0; - int aiKeyList[8]={0,0,0,0, 0,0,0,0}; - while(true){ - if(index>=8)break; //skip the last to keep as '.' - - bool bRetry=false; - - switch(index){ - case 0: fsAsk="Upper Left";break; - case 1: fsAsk="Up"; break; - case 2: fsAsk="Upper Right"; break; - case 3: fsAsk="Left"; break; - case 4: fsAsk="Right"; break; - case 5: fsAsk="Lower Left"; break; - case 6: fsAsk="Down"; break; - case 7: fsAsk="Lower Right"; break; - //case 8: fsAsk="Stop"; break; //skip the last to keep as '.' - } - iKey=game::AskForKeyPress(fsAsk); - if(iKey==KEY_ESC){bRet=false;break;} - - for(int c = 1; commandsystem::GetCommand(c); ++c){ - if(iKey==commandsystem::GetCommand(c)->GetKey()){ - ADD_MESSAGE("SYSTEM: conflicting command key '%c'(code is %d or 0x%X), retry...",iKey,iKey,iKey); //TODO this messes the gameplay message log... but is better than a popup? - bRetry=true; - break; - } - } - for(int c = 0; c<8; ++c){ - if(iKey==aiKeyList[c]){ - ADD_MESSAGE("SYSTEM: conflicting movement key '%c'(code is %d or 0x%X), retry...",iKey,iKey,iKey); //TODO this messes the gameplay message log... but is better than a popup? - bRetry=true; - break; - } - } - if(bRetry)continue; - - fprintf(fl, "%04X\n", iKey); - aiKeyList[index]=iKey; - - index++; - } - - for(int c = 1; commandsystem::GetCommand(c); ++c){ - fprintf(fl, "\"%s\"=0x%04X='%c'\n", - commandsystem::GetCommand(c)->GetDescription(), - commandsystem::GetCommand(c)->GetKey(), - commandsystem::GetCommand(c)->GetKey() - ); - } - - fclose(fl); - - if(bRet)game::LoadCustomCommandKeys(); - return bRet; -} - void ivanconfig::DirectionKeyMapDisplayer(const cycleoption* O, festring& Entry) { switch(O->Value) @@ -1070,7 +999,7 @@ void ivanconfig::SetupCustomKeysChanger(truthoption* O, truth What) if(game::IsRunning() || !What){ O->Value = What; if(O->Value) - ConfigureCustomKeys(); + game::ConfigureCustomKeys(); } } From ef990869e1bf5eac445a6d135f0dff1e6fdc581c Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 13 Apr 2020 22:14:47 -0300 Subject: [PATCH 107/235] fix to show key layout "properly" again. --- Main/Source/command.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 24e6e28f7..05b9e2ff9 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -993,7 +993,7 @@ truth commandsystem::ShowKeyLayout(character*) if(!GetCommand(c)->IsWizardModeFunction()) { Buffer.Empty(); - Buffer << GetCommand(c)->GetKey(); + Buffer << (char)GetCommand(c)->GetKey(); Buffer.Resize(10); List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY); } @@ -1008,7 +1008,7 @@ truth commandsystem::ShowKeyLayout(character*) if(GetCommand(c)->IsWizardModeFunction()) { Buffer.Empty(); - Buffer << GetCommand(c)->GetKey(); + Buffer << (char)GetCommand(c)->GetKey(); Buffer.Resize(10); List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY); } From 8811d39d6bb7f5a9cf156e198da698def8ab520c Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 14 Apr 2020 20:29:12 -0300 Subject: [PATCH 108/235] AutoPlay: fix dropping stethoscope; CustomKeys: -fixed crash about index detection/usage for movement keys; -improved showing some extended key names (above 0xFF); -better help info; --- Main/Include/char.h | 1 + Main/Include/game.h | 1 + Main/Source/char.cpp | 11 ++++++++++- Main/Source/command.cpp | 4 ++-- Main/Source/game.cpp | 43 ++++++++++++++++++++++++++++++++++++----- Main/Source/human.cpp | 8 ++------ Main/Source/iconf.cpp | 4 ++-- 7 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Main/Include/char.h b/Main/Include/char.h index 1711c9feb..cb4c7ed71 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -1228,6 +1228,7 @@ class character : public entity, public id static void AutoPlayAIDebugDrawSquareRect(v2 v2SqrPos, col16 color, int iPrintIndex=-1, bool bWide=false, bool bKeepColor=false); static void AutoPlayAIDebugDrawOverlay(); static bool AutoPlayAICheckAreaLevelChangedAndReset(); + truth AutoPlayAIcanApply(item* it); truth AutoPlayAIDropThings(); bool IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern); truth AutoPlayAIEquipAndPickup(bool bPlayerHasLantern); diff --git a/Main/Include/game.h b/Main/Include/game.h index c9786dfbf..7e997fa5c 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -515,6 +515,7 @@ class game static int GetSaveFileVersionHardcoded(); static void ValidateCommandKeys(char Key1,char Key2,char Key3); static truth ConfigureCustomKeys(); + static festring ToCharIfPossible(int i); static truth CheckConflictingCmdKey(int iNewKey, int iIgnoreIndex); static festring GetMoveKeyDesc(int i); static void LoadCustomCommandKeys(); diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 147e54703..53fbc2dab 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -2886,6 +2886,15 @@ void character::AutoPlayAIDebugDrawOverlay() AutoPlayAIDebugDrawSquareRect(vv2WrongGoingTo[i],BLUE,i,false,true); } +truth character::AutoPlayAIcanApply(item* it) +{ + if(!it->IsAppliable(this))return false; + if(it->IsZappable(this))return false; //not here, see zap section + if(dynamic_cast(it))return false; // too complex to make it auto work + if(dynamic_cast(it))return false; // too complex to make it auto work + return true; +} + truth character::AutoPlayAIDropThings() { // level* lvl = game::GetCurrentLevel(); DBG1(lvl); @@ -3016,7 +3025,7 @@ truth character::AutoPlayAIDropThings() if(iDirOk==-1){iDirOk=clock()%8;DBG2("RandomDir",iDirOk);}DBGLN; //TODO should just drop may be? unless hitting w/e is there could help bool bApplyDropped=false; //or vanished - if(dropMe->IsAppliable(this) && dropMe->Apply(this)){ + if(AutoPlayAIcanApply(dropMe) && dropMe->Apply(this)){ static itemvector ivChkDrop;ivChkDrop.clear(); GetStack()->FillItemVector(ivChkDrop); bApplyDropped=true; diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 05b9e2ff9..e21f1cd15 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -993,7 +993,7 @@ truth commandsystem::ShowKeyLayout(character*) if(!GetCommand(c)->IsWizardModeFunction()) { Buffer.Empty(); - Buffer << (char)GetCommand(c)->GetKey(); + Buffer << game::ToCharIfPossible(GetCommand(c)->GetKey()); Buffer.Resize(10); List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY); } @@ -1008,7 +1008,7 @@ truth commandsystem::ShowKeyLayout(character*) if(GetCommand(c)->IsWizardModeFunction()) { Buffer.Empty(); - Buffer << (char)GetCommand(c)->GetKey(); + Buffer << game::ToCharIfPossible(GetCommand(c)->GetKey()); Buffer.Resize(10); List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY); } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 1fc77f2c5..e9378bd31 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5906,6 +5906,39 @@ festring IntToHexStr(int i) return fs; } +festring game::ToCharIfPossible(int i) +{ + switch(i){ // these are above 0xFF + //TODO complete this list, if has no #define, use the hexa directly. + case KEY_UP: + return "Up"; + case KEY_DOWN: + return "Down"; + case KEY_RIGHT: + return "Right"; + case KEY_LEFT: + return "Left"; + case KEY_HOME: + return "Home"; + case KEY_END: + return "End"; + case KEY_PAGE_DOWN: + return "PgDn"; + case KEY_PAGE_UP: + return "PgUp"; + } + + if(i>=0 && i<=0xFF) //these are mapped at fonts gfx files + return festring()+(char)i; + + return IntToHexStr(i); +} + +/** + * Command's (and movement keys) descriptions are used as identifiers at the config file. + * These descriptions shall not clash and preferably should not be changed. + * @return + */ truth game::ConfigureCustomKeys() { festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; @@ -5927,7 +5960,7 @@ truth game::ConfigureCustomKeys() for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c){ fsEntry=cmd->GetDescription(); fsEntry.Resize(60); - fsEntry<<"'"<<(char)cmd->GetKey()<<"' "; + fsEntry<<"'"<GetKey())<<"' "; fsEntry<GetKey()); if(!bWizIni && cmd->IsWizardModeFunction()){ @@ -5942,7 +5975,7 @@ truth game::ConfigureCustomKeys() for(int c=0;c<8;c++){ fsEntry=GetMoveKeyDesc(c); fsEntry.Resize(60); - fsEntry<<"'"<<(char)game::MoveCustomCommandKey[c]<<"' "; + fsEntry<<"'"<=iMoveKeyStart) iMvKey = Select-iMoveKeyStart; - if(iMvKey){ + if(iMvKey>=0){ fsAsk<GetKey(); } fsAsk<<"' "; - fsAsk<GetKey()); + fsAsk<=0 ? game::MoveCustomCommandKey[iMvKey] : cmd->GetKey()); fsAsk<<")"; iNewKey=game::AskForKeyPress(fsAsk); diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index a1706f568..d4bfbf745 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -3857,12 +3857,8 @@ truth humanoid::AutoPlayAIequipConsumeZapReadApply() static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); static itemvector vitA;vitA.clear(); for(uint c = 0; c < vitEqW.size(); ++c){ - if(!vitEqW[c]->IsAppliable(this))continue; - if(vitEqW[c]->IsZappable(this))continue; //not here, see zap section - if(dynamic_cast(vitEqW[c]))continue; // too complex to make it auto work - if(dynamic_cast(vitEqW[c]))continue; // too complex to make it auto work - - vitA.push_back(vitEqW[c]); + if(AutoPlayAIcanApply(vitEqW[c])) + vitA.push_back(vitEqW[c]); } if(vitA.size()){ diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 7e0759dcd..88710655e 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -288,8 +288,8 @@ cycleoption ivanconfig::DirectionKeyMap( "DirectionKeyMap", DIR_NORM, 3, // {default value, number of options to cycle through} &DirectionKeyMapDisplayer); truthoption ivanconfig::SetupCustomKeys( "SetupCustomKeys", - "* Custom command and movement", //TODO all keys one day, and let it work on main menu - "Lets you assign all commands to any key of your preference. This global configuration won't work at main menu, load/start some game.", + "Custom command and movement", //TODO all keys one day, and let it work on main menu + "Lets you assign any command to any key binding of your preference. The default keys here will be from the control scheme option above. Only changed keybindings will be saved at the new config file. This global configuration won't work at main menu, load/start some game.", false, &configsystem::NormalTruthDisplayer, &configsystem::NormalTruthChangeInterface, From 456888d9138cdc8cc40509d63c8fcad1535f630b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 14 Apr 2020 22:03:34 -0300 Subject: [PATCH 109/235] CustomKeys: - improved validation during key config; - clarified the code readability; - it now writes all keybindings to the file (not only changes); - fixed/improved file loading; --- Main/Include/game.h | 2 +- Main/Source/game.cpp | 134 +++++++++++++++++++++++++++---------------- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/Main/Include/game.h b/Main/Include/game.h index 7e997fa5c..e14185e68 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -516,7 +516,7 @@ class game static void ValidateCommandKeys(char Key1,char Key2,char Key3); static truth ConfigureCustomKeys(); static festring ToCharIfPossible(int i); - static truth CheckConflictingCmdKey(int iNewKey, int iIgnoreIndex); + static truth ValidateCustomCmdKey(int iNewKey, int iIgnoreIndex, bool bMoveKeys); static festring GetMoveKeyDesc(int i); static void LoadCustomCommandKeys(); private: diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index e9378bd31..af2d58adf 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5831,13 +5831,17 @@ void game::LoadCustomCommandKeys() if(!fl)return; festring Line; + festring fsMatch; + festring fsPostFix="\"=0x"; static const int iBuffSz=0xFF; char str[iBuffSz]; while(fgets(str, iBuffSz, fl)){ Line=str; command* cmd; for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c){ - if(Line.Find(cmd->GetDescription())==1){ // after " + fsMatch.Empty(); + fsMatch<GetDescription()<SetCustomKey(HexToInt(Line)); break; } @@ -5845,7 +5849,9 @@ void game::LoadCustomCommandKeys() int iVal; for(int c=0;c<8;c++){ - if(Line.Find(GetMoveKeyDesc(c))==1){ // after " + fsMatch.Empty(); + fsMatch<GetKey()){ - ADD_MESSAGE("SYSTEM: conflicting key '%c'(code is %d or 0x%X) with command \"%s\", retry...", - iNewKey,iNewKey,iNewKey,cmd->GetDescription()); - return true; + // conflicts check + if(bValid){ + command *cmd; + for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c){ + if(!bMoveKeys && c==iIgnoreIndex)continue; + if(iNewKey==cmd->GetKey()){ + fsDesc=cmd->GetDescription(); + bValid=false; + break; + } } } + if(bValid){ + for(int c=0;c<8;c++){ + if(bMoveKeys && c==iIgnoreIndex)continue; + if(iNewKey == game::MoveCustomCommandKey[c]){ + fsDesc=GetMoveKeyDesc(c).CStr(); + bValid=false; + break; + } + } + } + if(!bValid){ + ADD_MESSAGE("SYSTEM: conflicting key '%s'(code is %d or 0x%X) with command \"%s\", retry...", + ToCharIfPossible(iNewKey).CStr(),iNewKey,iNewKey,fsDesc.CStr()); + } - for(int c=0;c<8;c++){ - if(c==iIgnoreIndex)continue; - if(iNewKey == game::MoveCustomCommandKey[c]){ - ADD_MESSAGE("SYSTEM: conflicting key '%c'(code is %d or 0x%X) with movement command \"%s\", retry...", - iNewKey,iNewKey,iNewKey,GetMoveKeyDesc(c).CStr()); - return true; + // general invalid key codes + if(bValid){ + if(iNewKey<0x20){ + ADD_MESSAGE("SYSTEM: invalid key code 0x%X, retry...",iNewKey); + bValid=false; } - } + } - return false; + return bValid; } festring IntToHexStr(int i) @@ -5934,6 +5958,11 @@ festring game::ToCharIfPossible(int i) return IntToHexStr(i); } +void WriteCustomKeyBindingsCfgFile(FILE *fl,festring fsDesc,int iKey){ + fprintf(fl, "\"%s\"=0x%04X\n", fsDesc.CStr(), iKey); + fflush(fl); +} + /** * Command's (and movement keys) descriptions are used as identifiers at the config file. * These descriptions shall not clash and preferably should not be changed. @@ -5941,14 +5970,8 @@ festring game::ToCharIfPossible(int i) */ truth game::ConfigureCustomKeys() { - festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; + game::LoadCustomCommandKeys(); //in case there is anything already set - festring fsFlBkp=fsFl+".bkp"; - std::ifstream src(fsFl.CStr() , std::ios::binary); - std::ofstream dst(fsFlBkp.CStr(), std::ios::binary); - dst << src.rdbuf(); - - FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); felist fel(CONST_S("Configure custom keys:")); bool bRet=true; command* cmd; @@ -5988,50 +6011,65 @@ truth game::ConfigureCustomKeys() break; } + bool bIsMoveKeys = Select >= iMoveKeyStart; + int iMvKeyIndex = bIsMoveKeys ? Select-iMoveKeyStart : -1; + int iCmdKeyIndex = bIsMoveKeys ? -1 : Select+1; int iNewKey; bool bIgnore=false; festring fsC = " (currently is set to '"; while(true){ cmd=NULL; festring fsAsk = "Press a key to assign to the command \""; - int iMvKey = -1; - if(Select>=iMoveKeyStart) - iMvKey = Select-iMoveKeyStart; - if(iMvKey>=0){ - fsAsk<GetDescription()<<"\""; - fsAsk<GetKey(); + fsAsk<GetKey()); } fsAsk<<"' "; - fsAsk<=0 ? game::MoveCustomCommandKey[iMvKey] : cmd->GetKey()); + fsAsk<GetKey()); fsAsk<<")"; iNewKey=game::AskForKeyPress(fsAsk); if(iNewKey==KEY_ESC){bIgnore=true;break;} - if(!CheckConflictingCmdKey(iNewKey,Select))break; + if(bIsMoveKeys){ + if(ValidateCustomCmdKey(iNewKey,iMvKeyIndex,true)) + break; + }else{ + if(ValidateCustomCmdKey(iNewKey,iCmdKeyIndex,false)) + break; + } } if(bIgnore)continue; if(!bRet)break; - festring fsWriteLine; - if(Select>=iMoveKeyStart){ - game::MoveCustomCommandKey[Select-iMoveKeyStart]=iNewKey; - fsWriteLine=GetMoveKeyDesc(Select-iMoveKeyStart); - }else{ - commandsystem::GetCommand(Select+1)->SetCustomKey(iNewKey); - fsWriteLine=cmd->GetDescription(); - } - - //save - fprintf(fl, "\"%s\"=0x%04X\n", fsWriteLine.CStr(), iNewKey); - fflush(fl); + if(bIsMoveKeys) + game::MoveCustomCommandKey[iMvKeyIndex]=iNewKey; + else + commandsystem::GetCommand(iCmdKeyIndex)->SetCustomKey(iNewKey); } + festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; + + // backup existing + festring fsFlBkp=fsFl+".bkp"; + std::ifstream src(fsFl.CStr() , std::ios::binary); + std::ofstream dst(fsFlBkp.CStr(), std::ios::binary); + dst << src.rdbuf(); + + // write a new in full + FILE *fl = fopen(fsFl.CStr(), "wt"); //"a"); + festring fsWriteLine; + int iNewKey; + for(int c = 1; (cmd=commandsystem::GetCommand(c)); ++c) + WriteCustomKeyBindingsCfgFile(fl,cmd->GetDescription(),cmd->GetKey()); + for(int c=0;c<8;c++) + WriteCustomKeyBindingsCfgFile(fl,GetMoveKeyDesc(c),game::MoveCustomCommandKey[c]); fclose(fl); if(bRet)game::LoadCustomCommandKeys(); //this here is more to validate if all went ok From 9b5853402557edff574285c516ed5051dd8ce2cb Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 14 Apr 2020 22:50:16 -0300 Subject: [PATCH 110/235] Favours are cheaper in case enough time has passed, as a normal pray could provide that favour freely and even with relation benefits. --- Main/Include/god.h | 1 + Main/Source/gods.cpp | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Main/Include/god.h b/Main/Include/god.h index 1c90ed645..deaecc0b6 100644 --- a/Main/Include/god.h +++ b/Main/Include/god.h @@ -99,6 +99,7 @@ class god static std::vector> vFavID; static void AddFavourID(int i,festring fs); static void FavourInit(); + int CalcDebit(int iDebit,int iDefault); std::vector knownSpellsID; }; diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 0cf697796..b4654f0b8 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -210,7 +210,7 @@ void god::FavourInit() //this one is better on this file AddFavourID(FAVOUR_TELEPORT,"Teleport"); } -int CalcDebit(god* G,int iDebit,int iDefault){ +int god::CalcDebit(int iDebit,int iDefault){ if(iDebit!=0){ switch(iDebit){ case FAVOURDEBIT_AUTO: iDebit=iDefault ;break; @@ -219,10 +219,18 @@ int CalcDebit(god* G,int iDebit,int iDefault){ } // can ask more favours if very well aligned - if(game::GetPlayerAlignment() == game::GetGodAlignmentVsPlayer(G)){ + if(game::GetPlayerAlignment() == game::GetGodAlignmentVsPlayer(this)){ iDebit/=2; } + /** + * if enough time has passed, a normal pray could provide the favour freely + * and even with relation benefits, so make it cheaper too, but not costless. + */ + if(Timer==0){ + iDebit/=2; // /=3 too cheap? + } + // skilled in manipulative praying :) iDebit -= ( (game::GetPlayer()->GetAttribute(MANA)*2.0) @@ -230,7 +238,11 @@ int CalcDebit(god* G,int iDebit,int iDefault){ game::GetPlayer()->GetAttribute(WISDOM) ) / 3.0; - if(iDebit<50)iDebit=50; //max of 20 vafours (50*20=1000) (too much?) in the best case (master) only + /** + * max of 20 vafours (50*20=1000) (too much?) + * in the best case (master prayer) only + */ + if(iDebit<50)iDebit=50; } return iDebit; } @@ -262,7 +274,7 @@ bool god::CallFavour(CallFavourType call, int iCallFavour, int iWhat, int iDebit if(iDebit==0) //came thru normal praying AddKnownSpell(knownSpellsID,iCallFavour); - iDebit=CalcDebit(this,iDebit,iDbtDefault); + iDebit=CalcDebit(iDebit,iDbtDefault); if(iDebit>0) if(!god::Favour(iWhat,iDebit)) From a2cc59b4d7303ed5c21952fbd3f69a162e6b708f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 00:07:24 -0300 Subject: [PATCH 111/235] Favours will reset last pray time too now, and will increase the god::Timer that counts against next safe pray time. --- Main/Source/gods.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index b4654f0b8..570f64272 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -282,8 +282,15 @@ bool god::CallFavour(CallFavourType call, int iCallFavour, int iWhat, int iDebit bool bWorked = false; if((*call)(this)){ - if(iDebit>0) + if(iDebit>0){ //was a favour + int iTm = 10000 - Relation*10; //by reaching here, Relation is always > 0 + if(iTm<1000)iTm=1000; + AdjustTimer(iTm); // this is a kind of debit too (counts against next safe pray time) + + LastPray=0; // to make it count as a pray too + Relation-=iDebit; + } bWorked = true; } From 1f97663118cb1cf3c144c73353d392e2c7591893 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 00:49:04 -0300 Subject: [PATCH 112/235] Workaround for a rare crash where mother entity is nowhere (catch on auto-play at crystal cave). --- Main/Source/materia.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Main/Source/materia.cpp b/Main/Source/materia.cpp index c336c7cc0..4416ca753 100644 --- a/Main/Source/materia.cpp +++ b/Main/Source/materia.cpp @@ -83,6 +83,15 @@ truth material::Effect(character* Char, int BodyPart, long Amount) if(!Amount) return false; + /** + * Pos is used to prepare a seed for temporary random state. + * Some rare times, mother entity is nowhere (square under is NULL) and the game would crash... + * So... why not just use the Char pos? it is random anyway... right? + */ + v2 Pos=Char->GetPos(); + if(GetMotherEntity() && GetMotherEntity()->GetSquareUnderEntity()) + Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); + switch(GetEffect()) { case EFFECT_POISON: Char->BeginTemporaryState(POISONED, Amount); break; @@ -111,7 +120,6 @@ truth material::Effect(character* Char, int BodyPart, long Amount) case EFFECT_SKUNK_SMELL: Char->BeginTemporaryState(POISONED, Amount); break; case EFFECT_MAGIC_MUSHROOM: { - v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); Char->ActivateRandomState(SRC_MAGIC_MUSHROOM, Amount, Volume % 250 + Pos.X + Pos.Y + 1); break; @@ -124,14 +132,12 @@ truth material::Effect(character* Char, int BodyPart, long Amount) case EFFECT_HOLY_BANANA: Char->ReceiveHolyBanana(Amount); break; case EFFECT_EVIL_WONDER_STAFF_VAPOUR: { - v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); Char->ActivateRandomState(SRC_EVIL, Amount, Volume % 250 + Pos.X + Pos.Y + 1); break; } case EFFECT_GOOD_WONDER_STAFF_VAPOUR: { - v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); Char->ActivateRandomState(SRC_GOOD, Amount, Volume % 250 + Pos.X + Pos.Y + 1); break; @@ -143,7 +149,6 @@ truth material::Effect(character* Char, int BodyPart, long Amount) case EFFECT_TELEPORT_CONTROL: Char->BeginTemporaryState(TELEPORT_CONTROL, Amount); break; case EFFECT_MUSHROOM: { - v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); Char->ActivateRandomState(SRC_MUSHROOM, Amount, Volume % 250 + Pos.X + Pos.Y + 1); break; From cd6dc7b89e1ac6f32590045a053d2dc8bc72d298 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 02:01:16 -0300 Subject: [PATCH 113/235] Workaround (again) for a rare crash where character is nowhere (catch on auto-play at crystal cave). --- Main/Source/materia.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Main/Source/materia.cpp b/Main/Source/materia.cpp index 4416ca753..d9b37e00e 100644 --- a/Main/Source/materia.cpp +++ b/Main/Source/materia.cpp @@ -88,9 +88,13 @@ truth material::Effect(character* Char, int BodyPart, long Amount) * Some rare times, mother entity is nowhere (square under is NULL) and the game would crash... * So... why not just use the Char pos? it is random anyway... right? */ - v2 Pos=Char->GetPos(); - if(GetMotherEntity() && GetMotherEntity()->GetSquareUnderEntity()) + v2 Pos; + if(Pos.Is0() && GetMotherEntity() && GetMotherEntity()->GetSquareUnderEntity()) Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); + if(Pos.Is0() && Char && Char->GetSquareUnder()) + Pos=Char->GetPos(); + if(Pos.Is0() && game::GetCurrentLevel()) + Pos=game::GetCurrentLevel()->GetRandomSquare(); //TODO any better idea? switch(GetEffect()) { From 9b45b01835d98ef0195e308a347d4731d47564f1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 17:09:20 -0300 Subject: [PATCH 114/235] Added _BUG_TRACK_ tag and stacktrace to some debug messages; Fix invalid vomit material config ID; --- Main/Include/confdef.h | 1 + Main/Source/char.cpp | 11 ++++++++--- Main/Source/materia.cpp | 7 ++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Main/Include/confdef.h b/Main/Include/confdef.h index 9206da1a7..2e06400f8 100644 --- a/Main/Include/confdef.h +++ b/Main/Include/confdef.h @@ -366,6 +366,7 @@ #define POLYMORPHINE (LIQUID_ID + 58) #define LAVA (LIQUID_ID + 59) #define NAPALM (LIQUID_ID + 60) +#define _LIQUID_ID_END_ (LIQUID_ID + 61) //this must be the last one always! #define FLESH_ID (5 << 12) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 53fbc2dab..e75d87176 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3862,10 +3862,15 @@ void character::Vomit(v2 Pos, int Amount, truth ShowMsg) DeActivateTemporaryState(PARASITE_TAPE_WORM); } - - if(!game::IsInWilderness()) + + if(!game::IsInWilderness()){ + if(GetMyVomitMaterial() < LIQUID_ID || GetMyVomitMaterial() > _LIQUID_ID_END_){ + DBGSTK;DBG4("_BUG_TRACK_:Fixing invalid vomit material config ID to prevent unnecessary ABORT()",GetMyVomitMaterial(),LIQUID_ID,_LIQUID_ID_END_); + SetNewVomitMaterial(VOMIT); + } GetNearLSquare(Pos)->ReceiveVomit(this, - liquid::Spawn(GetMyVomitMaterial(), long(sqrt(GetBodyVolume()) * Amount / 1000))); + liquid::Spawn(GetMyVomitMaterial(), long(sqrt(GetBodyVolume()) * Amount / 1000))); + } } truth character::Polymorph(character* NewForm, int Counter) diff --git a/Main/Source/materia.cpp b/Main/Source/materia.cpp index d9b37e00e..b3bcf7e06 100644 --- a/Main/Source/materia.cpp +++ b/Main/Source/materia.cpp @@ -12,6 +12,8 @@ /* Compiled through materset.cpp */ +#include "dbgmsgproj.h" + materialprototype::materialprototype(const materialprototype* Base, materialspawner Spawner, materialcloner Cloner, @@ -89,8 +91,11 @@ truth material::Effect(character* Char, int BodyPart, long Amount) * So... why not just use the Char pos? it is random anyway... right? */ v2 Pos; - if(Pos.Is0() && GetMotherEntity() && GetMotherEntity()->GetSquareUnderEntity()) + if(Pos.Is0() && GetMotherEntity() && GetMotherEntity()->GetSquareUnderEntity()){ Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); + }else{ + DBGSTK;DBG3("_BUG_TRACK_:Workaround for mother entity being nowhere to prevent crash",GetMotherEntity(),Char); + } if(Pos.Is0() && Char && Char->GetSquareUnder()) Pos=Char->GetPos(); if(Pos.Is0() && game::GetCurrentLevel()) From 68aeb84fddd3c421e4445c5977a70dbda79a90b3 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 18:36:19 -0300 Subject: [PATCH 115/235] Skip sfx playing if sfx sound volume is muted (zero) (catch on autoplay); scrollofearthquake::EarthQuakeMagic() fix crash about tunnels (catch on autoplay); --- Main/Source/message.cpp | 6 +++++- Main/Source/miscitem.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Main/Source/message.cpp b/Main/Source/message.cpp index 2e1e4392d..0938e836c 100644 --- a/Main/Source/message.cpp +++ b/Main/Source/message.cpp @@ -485,7 +485,11 @@ SoundFile *soundsystem::findMatchingSound(festring Buffer) void soundsystem::playSound(festring Buffer) { - if(!ivanconfig::GetPlaySounds()) return; + if(!ivanconfig::GetPlaySounds()) + return; + if(ivanconfig::GetSfxVolume()==0) + return; + initSound(); if(SoundState == 1) { diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index a979f6a76..eaa9387b7 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -195,8 +195,11 @@ void scrollofearthquake::EarthQuakeMagic(festring fsMsgHitNPC) if(!game::GetCurrentLevel()->EarthquakesAffectTunnels()) Tunnels = 0; - for(c = 0; c < Tunnels; ++c) - game::GetCurrentLevel()->AttachPos(game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE)); + for(c = 0; c < Tunnels; ++c){ + v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE); + if(Pos == ERROR_V2)continue; + game::GetCurrentLevel()->AttachPos(Pos); + } int ToEmpty = 10 + RAND() % 11; From 510e8ce2d8f9ac077211d37e0f29498a1383523b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 19:10:39 -0300 Subject: [PATCH 116/235] WIP-DeveloperConsole: improving; --- Main/Source/devcons.cpp | 48 +++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index bc4a61ae0..89993df3a 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -46,14 +46,20 @@ void ListChars(festring fsFilter){ if(!fsFilter.IsEmpty()) idFilter=atoi(fsFilter.CStr()); - DEVCMDMSG("params: %d",idFilter); + DEVCMDMSG2("params: NameHas=\"%s\" or ID=%d",fsFilter.CStr(),idFilter); // std::vector vc = game::GetAllCharacters(); // for(int i=0;isecond; - if(idFilter!=0 && idFilter!=C->GetID())continue; + if(idFilter==0){ + if(C->GetNameSingular().Find(fsFilter)==festring::NPos) + continue; + }else{ + if(idFilter!=C->GetID()) + continue; + } festring fsPos="NULL"; if(C->GetSquareUnder()!=NULL){ @@ -83,24 +89,29 @@ void ListChars(festring fsFilter){ } void ListItems(festring fsParams){ ulong idCharFilter=0; - ulong idFilter=0; + ulong idItemFilter=0; + festring fsFilter; if(!fsParams.IsEmpty()){ std::string part; std::stringstream iss(fsParams.CStr()); if(iss >> part){ if(part=="c"){ - if(iss >> part) - idCharFilter=atoi(part.c_str()); + if(iss >> part){ + fsFilter=part.c_str(); + idCharFilter=atoi(fsFilter.CStr()); + } } if(part=="i"){ - if(iss >> part) - idFilter=atoi(part.c_str()); + if(iss >> part){ + fsFilter=part.c_str(); + idItemFilter=atoi(fsFilter.CStr()); + } } } } - DEVCMDMSG2("params: %d %d",idFilter,idCharFilter); + DEVCMDMSG3("params: ItemID=%d CharID=%d ItemOrCharNameHas=\"%s\"",idItemFilter,idCharFilter,fsFilter.CStr()); itemidmap map = game::GetItemIDMapCopy(); for(itemidmap::iterator itr = map.begin();itr!=map.end();itr++){ @@ -121,9 +132,14 @@ void ListItems(festring fsParams){ DEVCMDMSG2("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened? } - if(idFilter!=0 && idFilter!=it->GetID()) - continue; - + if(idItemFilter!=0){ + if(idItemFilter!=it->GetID()) + continue; + }else{ + if(it->GetNameSingular().Find(fsFilter)==festring::NPos) + continue; + } + slot* Slot = it->GetSlot(); const entity* ent; @@ -165,9 +181,13 @@ void ListItems(festring fsParams){ entC=NULL; } - if(idCharFilter!=0) + if(idCharFilter!=0){ if(entC==NULL || entC->GetID()!=idCharFilter) continue; + }else{ + if(entC==NULL || entC->GetNameSingular().Find(fsFilter)==festring::NPos) + continue; + } bool bPlayerStuff = entC!=NULL && entC->IsPlayer(); @@ -246,8 +266,8 @@ void devcons::OpenCommandsConsole() AddDevCmd("?",Help,"show this help",false); #ifdef WIZARD ADDCMD(SetVar,festring()<<" set a float variable index (max "<<(iVarTot-1)<<") to be used on debug",true); - ADDCMD(ListChars,"[filterCharID:ulong] list all characters on current dungeon level",true); - ADDCMD(ListItems,"[[c|i] ] filter by char or item ID. List all items on current dungeon level, including on characters inventory and containers",true); + ADDCMD(ListChars,"[[filterCharID:ulong]|[strCharNamePart:string]] List characters on current dungeon level",true); + ADDCMD(ListItems,"[[c|i] <|>] List items on current dungeon level, including on characters inventory and containers",true); #endif return true; }(); From 3218b0528fffbeebd3b9fcabf92f7c2d883db5c9 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 19:49:11 -0300 Subject: [PATCH 117/235] WIP-DeveloperConsole: improving; --- Main/Include/devcons.h | 10 ++--- Main/Source/bugworkaround.cpp | 28 +++++++------- Main/Source/devcons.cpp | 71 ++++++++++++++++++++++++++--------- 3 files changed, 72 insertions(+), 37 deletions(-) diff --git a/Main/Include/devcons.h b/Main/Include/devcons.h index 6fb90b013..57537f3a7 100644 --- a/Main/Include/devcons.h +++ b/Main/Include/devcons.h @@ -18,11 +18,11 @@ #include "festring.h" // this causes warnings with LGTM checks: #define DEVCMDMSG(fmt,x,...) ADD_MESSAGE(" > " fmt,x); -#define DEVCMDMSG(fmt,x) ADD_MESSAGE(" > " fmt,x); -#define DEVCMDMSG2(fmt,x,y) ADD_MESSAGE(" > " fmt,x,y); -#define DEVCMDMSG3(fmt,x,y,z) ADD_MESSAGE(" > " fmt,x,y,z); -#define DEVCMDMSG4(fmt,x,y,z,a) ADD_MESSAGE(" > " fmt,x,y,z,a); -#define DEVCMDMSG15(fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) ADD_MESSAGE(" > " fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o); +#define DEVCMDMSG1P(fmt,x) ADD_MESSAGE(" > " fmt,x); +#define DEVCMDMSG2P(fmt,x,y) ADD_MESSAGE(" > " fmt,x,y); +#define DEVCMDMSG3P(fmt,x,y,z) ADD_MESSAGE(" > " fmt,x,y,z); +#define DEVCMDMSG4P(fmt,x,y,z,a) ADD_MESSAGE(" > " fmt,x,y,z,a); +#define DEVCMDMSG15P(fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) ADD_MESSAGE(" > " fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o); typedef void (*callcmd)(festring); diff --git a/Main/Source/bugworkaround.cpp b/Main/Source/bugworkaround.cpp index 5846370ba..9dcf562f2 100644 --- a/Main/Source/bugworkaround.cpp +++ b/Main/Source/bugworkaround.cpp @@ -334,7 +334,7 @@ bool bugfixdp::ItemWork(character* Char, item* itWork, bool bFix, const char* cI void bugfixdp::ValidateFullLevel() { - DEVCMDMSG("%s","validate full level against dup stuff"); + DEVCMDMSG1P("%s","validate full level against dup stuff"); // validate full level against other possible dup items std::vector vAllItemsOnLevel; @@ -357,9 +357,9 @@ void bugfixdp::DupPlayerFix(character* DupPlayer) */ int iFixedCount=0; iFixedCount=CharEquipmentsWork(DupPlayer,true,true);DBGLN; - DEVCMDMSG("fixed equipments %d",iFixedCount); + DEVCMDMSG1P("fixed equipments %d",iFixedCount); iFixedCount=CharInventoryWork (DupPlayer,true,true);DBGLN; - DEVCMDMSG("fixed inv %d",iFixedCount); + DEVCMDMSG1P("fixed inv %d",iFixedCount); ulong idOld = DupPlayer->GetID(); DupPlayer->_BugWorkaround_PlayerDup(game::CreateNewCharacterID(DupPlayer));DBGLN; // make it consistent as removing it is crashing (also empties inv) @@ -384,10 +384,10 @@ void bugfixdp::DupPlayerFix(character* DupPlayer) DupPlayer->SetNP(1); //to die soon at least iFixedCount=CharBodypartsWork(DupPlayer,true,false);DBGLN; //bodyparts sent to hell would crash!!! TODO only torso? - DEVCMDMSG("fixed bodyparts %d",iFixedCount); + DEVCMDMSG1P("fixed bodyparts %d",iFixedCount); //BEWARE!!! this leads to crash: DupPlayer->Remove(); - DEVCMDMSG4("fixed dup player '%s' id=%d/%d 0x%X",DupPlayer->GetName(DEFINITE).CStr(),idOld,DupPlayer->GetID(),DupPlayer); + DEVCMDMSG4P("fixed dup player '%s' id=%d/%d 0x%X",DupPlayer->GetName(DEFINITE).CStr(),idOld,DupPlayer->GetID(),DupPlayer); DBGCHAR(DupPlayer,"CharFix:CharToBeLost"); } @@ -518,7 +518,7 @@ character* bugfixdp::FindByPlayerID1(v2 ReqPosL,bool bAndFixIt) // if(PB!=NULL && PB==CharID1){ if(PBtmp!=NULL && PBtmp->GetID()==1){ PBID1 = PBtmp; - DEVCMDMSG("polymorphed has backup with id=1 ref 0x%X",PBID1); + DEVCMDMSG1P("polymorphed has backup with id=1 ref 0x%X",PBID1); PPolymL=vCL[j]; break; } @@ -551,7 +551,7 @@ character* bugfixdp::FindByPlayerID1(v2 ReqPosL,bool bAndFixIt) character* CharPlayerOk = NULL; if(bAndFixIt && PPolymL!=NULL){ // DEVCMDMSG("killing polymorphed id=%d",PPolymL->GetID()); - DEVCMDMSG("vanishing polymorphed id=%d",PPolymL->GetID()); + DEVCMDMSG1P("vanishing polymorphed id=%d",PPolymL->GetID()); PPolymL->SetPolymorphBackup(NULL); PPolymL->RemoveTraps(); PPolymL->Remove(); @@ -573,16 +573,16 @@ character* bugfixdp::FindByPlayerID1(v2 ReqPosL,bool bAndFixIt) } // }else{ CharPlayerOk = CharID1; - DEVCMDMSG("%s","ID1 will be ok now"); + DEVCMDMSG1P("%s","ID1 will be ok now"); if(bAndFixIt && CharID1->GetSquareUnder()==NULL){ - DEVCMDMSG2("placing the character ID1 at %d,%d",ReqPosL.X,ReqPosL.Y); + DEVCMDMSG2P("placing the character ID1 at %d,%d",ReqPosL.X,ReqPosL.Y); CharID1->PutToOrNear(ReqPosL); //place he where expected } // } if(bAndFixIt) if(!CharPlayerOk->IsPlayer() || PLAYER!=CharPlayerOk){ - DEVCMDMSG("restoring player reference to id=%d",CharPlayerOk->GetID()); + DEVCMDMSG1P("restoring player reference to id=%d",CharPlayerOk->GetID()); game::SetPlayer(CharPlayerOk); } @@ -633,18 +633,18 @@ character* bugfixdp::BugWorkaroundDupPlayer(){ if(!bFound){ // game::RemoveCharacterID(Cid); //causes weird crashes elsewhere // DEVCMDMSG("removed inconsistent character id '%d'",Cid); - DEVCMDMSG("possibly inconsistent character id '%d'",Cid); + DEVCMDMSG1P("possibly inconsistent character id '%d'",Cid); } } // last thing is grant player's stuff is consistent int iFixedCount=0; iFixedCount=CharEquipmentsWork(CharPlayerOk,true,false);DBGLN; - DEVCMDMSG2("fixed player '%s' equipments %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); + DEVCMDMSG2P("fixed player '%s' equipments %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); iFixedCount=CharInventoryWork (CharPlayerOk,true,false);DBGLN; - DEVCMDMSG2("fixed player '%s' inventory %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); + DEVCMDMSG2P("fixed player '%s' inventory %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount); iFixedCount=TrapsWork(); - DEVCMDMSG("fixed traps %d",iFixedCount); + DEVCMDMSG1P("fixed traps %d",iFixedCount); // just a final validation, may abort on failure ValidateFullLevel(); diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 89993df3a..f9a6f651a 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -40,17 +40,21 @@ * Any information/functionality that provides an advantage must be considered a cheat, therefore WIZARD MODE!!! */ +std::vector vCharLastSearch; +std::vector vItemLastSearch; + #ifdef WIZARD void ListChars(festring fsFilter){ ulong idFilter=0; if(!fsFilter.IsEmpty()) idFilter=atoi(fsFilter.CStr()); - DEVCMDMSG2("params: NameHas=\"%s\" or ID=%d",fsFilter.CStr(),idFilter); + DEVCMDMSG2P("params: NameHas=\"%s\" or ID=%d",fsFilter.CStr(),idFilter); // std::vector vc = game::GetAllCharacters(); // for(int i=0;isecond; if(idFilter==0){ @@ -76,21 +80,47 @@ void ListChars(festring fsFilter){ if(PB!=NULL) fsMsg << " PB='"<GetID() <<"/"<< PB->GetName(DEFINITE)<<"'"; fsMsg << "."; - DEVCMDMSG("%s",fsMsg.CStr()); -// DEVCMDMSG("%sid=%d[%d] (%s) '%s'.", -//// ADD_MESSAGE("%sid=%d (%d,%d) '%s'.", -// C->IsPlayer()?"@":" ", -// C->GetID(), -// itr->first, -// fsPos.CStr(), -// C->GetName(DEFINITE).CStr() -// ); + DEVCMDMSG1P("%s",fsMsg.CStr()); + vCharLastSearch.push_back(C); + } + DEVCMDMSG1P("total:%d",vCharLastSearch.size()); +} +void DelChars(festring fsParams){ + ulong count=0; + if(!fsParams.IsEmpty()) + count=atoi(fsParams.CStr()); + + if(count==0) + count=vCharLastSearch.size(); + + DEVCMDMSG1P("params: count=%d",count); + + for(int i=0;iGetID(),vCharLastSearch[i]->GetNameSingular().CStr()); + vCharLastSearch[i]->SendToHell(); + } +} +void DelItems(festring fsParams){ + ulong count=0; + if(!fsParams.IsEmpty()) + count=atoi(fsParams.CStr()); + + if(count==0) + count=vItemLastSearch.size(); + + DEVCMDMSG1P("params: count=%d",count); + + for(int i=0;iGetID(),vCharLastSearch[i]->GetNameSingular().CStr()); + vItemLastSearch[i]->SendToHell(); } } void ListItems(festring fsParams){ ulong idCharFilter=0; ulong idItemFilter=0; festring fsFilter; + vCharLastSearch.clear(); + vItemLastSearch.clear(); if(!fsParams.IsEmpty()){ std::string part; @@ -111,7 +141,7 @@ void ListItems(festring fsParams){ } } - DEVCMDMSG3("params: ItemID=%d CharID=%d ItemOrCharNameHas=\"%s\"",idItemFilter,idCharFilter,fsFilter.CStr()); + DEVCMDMSG3P("params: ItemID=%d CharID=%d ItemOrCharNameHas=\"%s\"",idItemFilter,idCharFilter,fsFilter.CStr()); itemidmap map = game::GetItemIDMapCopy(); for(itemidmap::iterator itr = map.begin();itr!=map.end();itr++){ @@ -129,7 +159,7 @@ void ListItems(festring fsParams){ it->GetSquaresUnder()>100 || //something improbable, could be just 8 I guess... false ){ - DEVCMDMSG2("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened? + DEVCMDMSG2P("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened? } if(idItemFilter!=0){ @@ -201,7 +231,7 @@ void ListItems(festring fsParams){ fsPos<GetPos().X<<","<GetPos().Y; } - DEVCMDMSG15("%sid=%d (%s) '%s' owner '%d/%s' '%d/%s' (%s%s%s).", + DEVCMDMSG15P("%sid=%d (%s) '%s' owner '%d/%s' '%d/%s' (%s%s%s).", bPlayerStuff?"@":" ", it->GetID(), @@ -221,7 +251,10 @@ void ListItems(festring fsParams){ 0,0,0,0 //dummy ); + if(entC!=NULL)vCharLastSearch.push_back((character*)entC); + if(entI!=NULL)vItemLastSearch.push_back((item*)entI); } + DEVCMDMSG2P("total: Chars=%d Items=%d",vCharLastSearch.size(),vItemLastSearch.size()); } #endif @@ -268,6 +301,8 @@ void devcons::OpenCommandsConsole() ADDCMD(SetVar,festring()<<" set a float variable index (max "<<(iVarTot-1)<<") to be used on debug",true); ADDCMD(ListChars,"[[filterCharID:ulong]|[strCharNamePart:string]] List characters on current dungeon level",true); ADDCMD(ListItems,"[[c|i] <|>] List items on current dungeon level, including on characters inventory and containers",true); + ADDCMD(DelChars,"[count:int] delete characters (from the list filled on the previous command) up to count if set.",true); + ADDCMD(DelItems,"[count:int] delete items (from the list filled on the previous command) up to count if set.",true); #endif return true; }(); @@ -354,10 +389,10 @@ void devcons::Help(festring fsFilter) fsWM=""; } } - DEVCMDMSG3("%s - %s%s",vCmd[j].fsCmd.CStr(),fsWM.CStr(),vCmd[j].fsHelp.CStr()); + DEVCMDMSG3P("%s - %s%s",vCmd[j].fsCmd.CStr(),fsWM.CStr(),vCmd[j].fsHelp.CStr()); } // ADD_MESSAGE("%sPs.: main commands are case insensitive.",cPrompt); - DEVCMDMSG("%s","Ps.: main commands are case insensitive."); + DEVCMDMSG1P("%s","Ps.: main commands are case insensitive."); } callcmd devcons::Find(festring fsCmd) @@ -389,7 +424,7 @@ void devcons::runCommand(festring fsFullCmd) } // ADD_MESSAGE("%sTrying to run: %s ('%s' '%s')",cPrompt,strFullCmd.c_str(),strCmd.c_str(),strParams.c_str()); - DEVCMDMSG3("Trying to run: %s ('%s' '%s')", + DEVCMDMSG3P("Trying to run: %s ('%s' '%s')", festring(strFullCmd.c_str()).CStr(), festring(strCmd.c_str()).CStr(), festring(strParams.c_str()).CStr() @@ -399,9 +434,9 @@ void devcons::runCommand(festring fsFullCmd) if(cc){ cc(strParams.c_str()); // ADD_MESSAGE("%scommand %s completed",cPrompt,strCmd.c_str()); - DEVCMDMSG("command %s completed",strCmd.c_str()); + DEVCMDMSG1P("command %s completed",strCmd.c_str()); }else{ // ADD_MESSAGE("%scommand %s not found",cPrompt,strCmd.c_str()); - DEVCMDMSG("command %s not found",strCmd.c_str()); + DEVCMDMSG1P("command %s not found",strCmd.c_str()); } } From 726d2d71e09dc91c8828a02850ce933da0f00181 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 20:25:39 -0300 Subject: [PATCH 118/235] WIP-DeveloperConsole: improving; --- Main/Source/devcons.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index f9a6f651a..b8c7a0dc6 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -58,7 +58,7 @@ void ListChars(festring fsFilter){ for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ character* C = itr->second; if(idFilter==0){ - if(C->GetNameSingular().Find(fsFilter)==festring::NPos) + if(C->GetName(DEFINITE).Find(fsFilter)==festring::NPos) continue; }else{ if(idFilter!=C->GetID()) @@ -96,8 +96,10 @@ void DelChars(festring fsParams){ DEVCMDMSG1P("params: count=%d",count); for(int i=0;iGetID(),vCharLastSearch[i]->GetNameSingular().CStr()); - vCharLastSearch[i]->SendToHell(); + character* C = vCharLastSearch[i]; + if(C->IsPlayer() || C->IsPet())continue; + DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",C->GetID(),C->GetNameSingular().CStr()); + C->Die(); } } void DelItems(festring fsParams){ @@ -111,8 +113,11 @@ void DelItems(festring fsParams){ DEVCMDMSG1P("params: count=%d",count); for(int i=0;iGetID(),vCharLastSearch[i]->GetNameSingular().CStr()); - vItemLastSearch[i]->SendToHell(); + item* it = vItemLastSearch[i]; + //TODO dont remove from player's inventory/equipped + DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",it->GetID(),it->GetNameSingular().CStr()); + it->RemoveFromSlot(); + it->SendToHell(); } } void ListItems(festring fsParams){ @@ -215,7 +220,7 @@ void ListItems(festring fsParams){ if(entC==NULL || entC->GetID()!=idCharFilter) continue; }else{ - if(entC==NULL || entC->GetNameSingular().Find(fsFilter)==festring::NPos) + if(entC==NULL || entC->GetName(DEFINITE).Find(fsFilter)==festring::NPos) continue; } From ae8f8a8444119032be6c518c3dda147d036e3257 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 21:10:24 -0300 Subject: [PATCH 119/235] WIP-DeveloperConsole: improving; --- Main/Source/devcons.cpp | 81 +++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index b8c7a0dc6..a77b752d6 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -90,35 +90,75 @@ void DelChars(festring fsParams){ if(!fsParams.IsEmpty()) count=atoi(fsParams.CStr()); - if(count==0) + if(count==0 || count>vCharLastSearch.size()) count=vCharLastSearch.size(); DEVCMDMSG1P("params: count=%d",count); + int iCount=0; for(int i=0;iIsPlayer() || C->IsPet())continue; - DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",C->GetID(),C->GetNameSingular().CStr()); + if(!C->Exists() || C->IsPlayer() || C->IsPet())continue; + DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",C->GetID(),C->GetName(DEFINITE).CStr()); C->Die(); + iCount++; } + DEVCMDMSG1P("total=%d",iCount); +} +festring fsDummy; +entity* GetOwner(item* it,festring& rfsType = fsDummy){ + slot* Slot = it->GetSlot(); + + const entity* ent; + if(dynamic_cast(Slot)!=NULL){ + ent=((gearslot*)Slot)->FindCarrier(); + rfsType="gear"; + }else + if(dynamic_cast(Slot)!=NULL){ + ent=((bodypartslot*)Slot)->GetMaster(); + rfsType="bodypart"; + }else + if(dynamic_cast(Slot)!=NULL){ + stackslot* sl = ((stackslot*)Slot); + ent=sl->FindCarrier(); + if(sl->GetMotherStack()!=NULL) + ent=sl->GetMotherStack()->GetMotherEntity(); + rfsType="stack"; + }else + ent=NULL; + + return (entity*)ent; +} +character* GetOwnerChar(item* it,festring& rfsType = fsDummy){ + entity* ent = GetOwner(it,rfsType); + if(dynamic_cast(ent)!=NULL) + return (character*)ent; + return NULL; } void DelItems(festring fsParams){ ulong count=0; if(!fsParams.IsEmpty()) count=atoi(fsParams.CStr()); - if(count==0) + if(count==0 || count>vItemLastSearch.size()) count=vItemLastSearch.size(); DEVCMDMSG1P("params: count=%d",count); + int iCount=0; + item* it; + character* C; for(int i=0;iGetID(),it->GetNameSingular().CStr()); + it = vItemLastSearch[i]; + if(!it->Exists())continue; + C = GetOwnerChar(it); + if(C && (C->IsPlayer() || C->IsPet()))continue; + DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",it->GetID(),it->GetName(DEFINITE).CStr()); it->RemoveFromSlot(); it->SendToHell(); + iCount++; } + DEVCMDMSG1P("total=%d",iCount); } void ListItems(festring fsParams){ ulong idCharFilter=0; @@ -158,43 +198,28 @@ void ListItems(festring fsParams){ //TODO could check app memory range if pointer is within limits... if( //TODO items could have a random key to detect if they were not deleted improperly or w/e, could still segfault when reading it tho... + !it->Exists() || it->GetVolume()==0 || it->GetID()==0 || + it->GetLSquareUnder()==NULL || it->GetSquaresUnder()==0 || it->GetSquaresUnder()>100 || //something improbable, could be just 8 I guess... - false + false //dummy ){ DEVCMDMSG2P("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened? + continue; } if(idItemFilter!=0){ if(idItemFilter!=it->GetID()) continue; }else{ - if(it->GetNameSingular().Find(fsFilter)==festring::NPos) + if(it->GetName(DEFINITE).Find(fsFilter)==festring::NPos) continue; } - slot* Slot = it->GetSlot(); - - const entity* ent; festring fsType; - if(dynamic_cast(Slot)!=NULL){ - ent=((gearslot*)Slot)->FindCarrier(); - fsType="gear"; - }else - if(dynamic_cast(Slot)!=NULL){ - ent=((bodypartslot*)Slot)->GetMaster(); - fsType="bodypart"; - }else - if(dynamic_cast(Slot)!=NULL){ - stackslot* sl = ((stackslot*)Slot); - ent=sl->FindCarrier(); - if(sl->GetMotherStack()!=NULL) - ent=sl->GetMotherStack()->GetMotherEntity(); - fsType="stack"; - }else - ent=NULL; + const entity* ent=GetOwner(it,fsType); festring fsDec; citem* entI; From 54c15143ea3cffb05a0ab4aff76b1eae7acff7a2 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 21:55:32 -0300 Subject: [PATCH 120/235] WIP-DeveloperConsole: improving; --- Main/Source/devcons.cpp | 98 ++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index a77b752d6..5bbfd691e 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -95,15 +95,16 @@ void DelChars(festring fsParams){ DEVCMDMSG1P("params: count=%d",count); - int iCount=0; - for(int i=0;i=0;i--){ character* C = vCharLastSearch[i]; if(!C->Exists() || C->IsPlayer() || C->IsPet())continue; DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",C->GetID(),C->GetName(DEFINITE).CStr()); C->Die(); - iCount++; + vCharLastSearch.pop_back(); + iRm++; } - DEVCMDMSG1P("total=%d",iCount); + DEVCMDMSG2P("total=%d, remaining=%d",iRm,vCharLastSearch.size()); } festring fsDummy; entity* GetOwner(item* it,festring& rfsType = fsDummy){ @@ -120,8 +121,9 @@ entity* GetOwner(item* it,festring& rfsType = fsDummy){ }else if(dynamic_cast(Slot)!=NULL){ stackslot* sl = ((stackslot*)Slot); - ent=sl->FindCarrier(); - if(sl->GetMotherStack()!=NULL) + if(sl->GetMotherStack()==NULL) + ent=sl->FindCarrier(); + else ent=sl->GetMotherStack()->GetMotherEntity(); rfsType="stack"; }else @@ -145,10 +147,10 @@ void DelItems(festring fsParams){ DEVCMDMSG1P("params: count=%d",count); - int iCount=0; + int iRm=0; item* it; character* C; - for(int i=0;i=0;i--){ it = vItemLastSearch[i]; if(!it->Exists())continue; C = GetOwnerChar(it); @@ -156,9 +158,10 @@ void DelItems(festring fsParams){ DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",it->GetID(),it->GetName(DEFINITE).CStr()); it->RemoveFromSlot(); it->SendToHell(); - iCount++; + vItemLastSearch.pop_back(); + iRm++; } - DEVCMDMSG1P("total=%d",iCount); + DEVCMDMSG2P("total=%d, remaining=%d",iRm,vItemLastSearch.size()); } void ListItems(festring fsParams){ ulong idCharFilter=0; @@ -166,6 +169,7 @@ void ListItems(festring fsParams){ festring fsFilter; vCharLastSearch.clear(); vItemLastSearch.clear(); + bool bItemMode=false; if(!fsParams.IsEmpty()){ std::string part; @@ -176,12 +180,14 @@ void ListItems(festring fsParams){ fsFilter=part.c_str(); idCharFilter=atoi(fsFilter.CStr()); } + bItemMode=false; } if(part=="i"){ if(iss >> part){ fsFilter=part.c_str(); idItemFilter=atoi(fsFilter.CStr()); } + bItemMode=true; } } } @@ -210,49 +216,53 @@ void ListItems(festring fsParams){ continue; } - if(idItemFilter!=0){ - if(idItemFilter!=it->GetID()) - continue; - }else{ - if(it->GetName(DEFINITE).Find(fsFilter)==festring::NPos) - continue; + if(bItemMode){ + if(idItemFilter!=0){ + if(idItemFilter!=it->GetID()) + continue; + }else{ + if(it->GetName(DEFINITE).Find(fsFilter)==festring::NPos) + continue; + } } festring fsType; const entity* ent=GetOwner(it,fsType); festring fsDec; - citem* entI; - ccharacter* entC; + citem* ownerI=NULL; + ccharacter* ownerC=NULL; if(dynamic_cast(ent)){ - entI=(citem*)ent; - entC=NULL; + ownerI=(citem*)ent; + ownerC=NULL; if(dynamic_cast(ent)){ const corpse* CP = (const corpse*)ent; - entC = CP->GetDeceased(); + ownerC = CP->GetDeceased(); fsDec=",Dec"; } }else if(dynamic_cast(ent)){ - entI=NULL; - entC=(ccharacter*)ent; + ownerI=NULL; + ownerC=(ccharacter*)ent; }else{ - entI=NULL; - entC=NULL; + ownerI=NULL; + ownerC=NULL; } - if(idCharFilter!=0){ - if(entC==NULL || entC->GetID()!=idCharFilter) - continue; - }else{ - if(entC==NULL || entC->GetName(DEFINITE).Find(fsFilter)==festring::NPos) - continue; + if(!bItemMode){ + if(idCharFilter!=0){ + if(ownerC==NULL || ownerC->GetID()!=idCharFilter) + continue; + }else{ + if(ownerC==NULL || ownerC->GetName(DEFINITE).Find(fsFilter)==festring::NPos) + continue; + } } - bool bPlayerStuff = entC!=NULL && entC->IsPlayer(); + bool bPlayerStuff = ownerC!=NULL && ownerC->IsPlayer(); festring fsPB; - if(entC!=NULL && entC->GetPolymorphBackup()!=NULL && entC->GetPolymorphBackup()->IsPlayer()) + if(ownerC!=NULL && ownerC->GetPolymorphBackup()!=NULL && ownerC->GetPolymorphBackup()->IsPlayer()) fsPB=",PB"; festring fsPos="NULL"; @@ -269,11 +279,11 @@ void ListItems(festring fsParams){ it->GetName(DEFINITE).CStr(), - entC!=NULL ? entC->GetID() : 0, - entC!=NULL ? entC->GetName(DEFINITE).CStr() : "NoEntC", + ownerC!=NULL ? ownerC->GetID() : 0, + ownerC!=NULL ? ownerC->GetName(DEFINITE).CStr() : "NoEntC", - entI!=NULL ? entI->GetID() : 0, - entI!=NULL ? entI->GetName(DEFINITE).CStr() : "NoEntI", + ownerI!=NULL ? ownerI->GetID() : 0, + ownerI!=NULL ? ownerI->GetName(DEFINITE).CStr() : "NoEntI", fsType.CStr(), fsPB.CStr(), @@ -281,8 +291,16 @@ void ListItems(festring fsParams){ 0,0,0,0 //dummy ); - if(entC!=NULL)vCharLastSearch.push_back((character*)entC); - if(entI!=NULL)vItemLastSearch.push_back((item*)entI); + if(ownerC!=NULL){ + bool bSkip=false; + for(auto pCchk = vCharLastSearch.begin(); pCchk != vCharLastSearch.end(); ++pCchk){ + bSkip = (*pCchk == ownerC); + if(bSkip)break; + } + if(!bSkip) + vCharLastSearch.push_back((character*)ownerC); + } + if(ownerI!=NULL)vItemLastSearch.push_back(it); } DEVCMDMSG2P("total: Chars=%d Items=%d",vCharLastSearch.size(),vItemLastSearch.size()); } @@ -330,7 +348,7 @@ void devcons::OpenCommandsConsole() #ifdef WIZARD ADDCMD(SetVar,festring()<<" set a float variable index (max "<<(iVarTot-1)<<") to be used on debug",true); ADDCMD(ListChars,"[[filterCharID:ulong]|[strCharNamePart:string]] List characters on current dungeon level",true); - ADDCMD(ListItems,"[[c|i] <|>] List items on current dungeon level, including on characters inventory and containers",true); + ADDCMD(ListItems,"[[c|i] <|>] List items on current dungeon level, including on characters ('c' will filter by character ID or name) inventory and containers",true); ADDCMD(DelChars,"[count:int] delete characters (from the list filled on the previous command) up to count if set.",true); ADDCMD(DelItems,"[count:int] delete items (from the list filled on the previous command) up to count if set.",true); #endif From 053a306f7df8f9b149af74d5f8741feb38df47b8 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 22:10:54 -0300 Subject: [PATCH 121/235] WIP-DeveloperConsole: improving; --- Main/Source/devcons.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 5bbfd691e..27268009f 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -57,11 +57,14 @@ void ListChars(festring fsFilter){ vCharLastSearch.clear(); for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ character* C = itr->second; - if(idFilter==0){ - if(C->GetName(DEFINITE).Find(fsFilter)==festring::NPos) + if(!C->Exists()) + continue; + + if(idFilter!=0){ + if(idFilter!=C->GetID()) continue; }else{ - if(idFilter!=C->GetID()) + if(C->GetName(DEFINITE).Find(fsFilter)==festring::NPos) continue; } @@ -153,6 +156,7 @@ void DelItems(festring fsParams){ for(int i=count-1;i>=0;i--){ it = vItemLastSearch[i]; if(!it->Exists())continue; + C = GetOwnerChar(it); if(C && (C->IsPlayer() || C->IsPet()))continue; DEVCMDMSG2P("Go to hell! id=%d name=\"%s\"",it->GetID(),it->GetName(DEFINITE).CStr()); @@ -212,7 +216,7 @@ void ListItems(festring fsParams){ it->GetSquaresUnder()>100 || //something improbable, could be just 8 I guess... false //dummy ){ - DEVCMDMSG2P("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened? + DEVCMDMSG3P("item REFERENCE INVALID at consistent list ID=%d 0x%X \"%s\"",itr->first,it,it->GetName(DEFINITE).CStr()); //was the item deleted or what happened? continue; } @@ -300,7 +304,7 @@ void ListItems(festring fsParams){ if(!bSkip) vCharLastSearch.push_back((character*)ownerC); } - if(ownerI!=NULL)vItemLastSearch.push_back(it); + vItemLastSearch.push_back(it); } DEVCMDMSG2P("total: Chars=%d Items=%d",vCharLastSearch.size(),vItemLastSearch.size()); } From eed25e50e866903475e5294838043a812538c292 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 22:47:55 -0300 Subject: [PATCH 122/235] WIP-DeveloperConsole: improving; --- Main/Source/devcons.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 27268009f..6cc5b4de5 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -44,6 +44,28 @@ std::vector vCharLastSearch; std::vector vItemLastSearch; #ifdef WIZARD +truth IsValidChar(character* C){ + if(!C->Exists()) + return false; + if(C->IsDead()) + return false; + if(!C->GetLSquareUnder()) + return false; + return true; +} +void TeleToChar(festring fsFilter){ + characteridmap map = game::GetCharacterIDMapCopy(); + for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ + character* C = itr->second; + if(!IsValidChar(C)) + continue; + + if(C->GetName(DEFINITE).Find(fsFilter)!=festring::NPos){ + game::GetPlayer()->TeleportNear(C); + return; + } + } +} void ListChars(festring fsFilter){ ulong idFilter=0; if(!fsFilter.IsEmpty()) @@ -57,7 +79,7 @@ void ListChars(festring fsFilter){ vCharLastSearch.clear(); for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ character* C = itr->second; - if(!C->Exists()) + if(!IsValidChar(C)) continue; if(idFilter!=0){ @@ -355,6 +377,7 @@ void devcons::OpenCommandsConsole() ADDCMD(ListItems,"[[c|i] <|>] List items on current dungeon level, including on characters ('c' will filter by character ID or name) inventory and containers",true); ADDCMD(DelChars,"[count:int] delete characters (from the list filled on the previous command) up to count if set.",true); ADDCMD(DelItems,"[count:int] delete items (from the list filled on the previous command) up to count if set.",true); + ADDCMD(TeleToChar," teleports near 1st character matching filter.",true); #endif return true; }(); From 294ff823a5c0af91693091735c2db8ee6a354599 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 15 Apr 2020 23:41:48 -0300 Subject: [PATCH 123/235] WIP-DeveloperConsole: improving; --- Main/Source/devcons.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 6cc5b4de5..5ce2a1d1a 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -133,7 +133,12 @@ void DelChars(festring fsParams){ } festring fsDummy; entity* GetOwner(item* it,festring& rfsType = fsDummy){ + if(!it->GetLSquareUnder()) + return NULL; + slot* Slot = it->GetSlot(); + if(!Slot->GetSquareUnder()) + return NULL; const entity* ent; if(dynamic_cast(Slot)!=NULL){ @@ -146,10 +151,10 @@ entity* GetOwner(item* it,festring& rfsType = fsDummy){ }else if(dynamic_cast(Slot)!=NULL){ stackslot* sl = ((stackslot*)Slot); - if(sl->GetMotherStack()==NULL) - ent=sl->FindCarrier(); - else + if(sl->GetMotherStack()!=NULL) ent=sl->GetMotherStack()->GetMotherEntity(); + else + ent=sl->FindCarrier(); rfsType="stack"; }else ent=NULL; From 21719af5283a2e86519569a3d4dcfc04560460bc Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 16 Apr 2020 00:38:58 -0300 Subject: [PATCH 124/235] Fix SIGFPE in case BodyPart->GetHP() returns 0 (was polymorphed into a ghost during auto-play); This fix lets experience gain be 300 in case HP is too low, as expected. Basically HP = 0 works as HP = 1. --- Main/Source/char.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index e75d87176..e233841d4 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -5672,7 +5672,7 @@ void character::Regenerate() EditNP(-Max(7500 / MaxHP, 1)); RegenerationCounter -= 1250000; int HP = BodyPart->GetHP(); - EditExperience(ENDURANCE, Min(1000 * BodyPart->GetMaxHP() / (HP * HP), 300), 1000); + EditExperience(ENDURANCE, Min(1000 * BodyPart->GetMaxHP() / Max(HP * HP,1), 300), 1000); } } From 406c282690397bf9fe8c7b7b15de027fa54f55dc Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 16 Apr 2020 20:17:06 -0300 Subject: [PATCH 125/235] DeveloperConsole: new command TeleToMe; --- Main/Include/devcons.h | 11 ++++++----- Main/Source/devcons.cpp | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Main/Include/devcons.h b/Main/Include/devcons.h index 57537f3a7..eb18c34b1 100644 --- a/Main/Include/devcons.h +++ b/Main/Include/devcons.h @@ -18,11 +18,12 @@ #include "festring.h" // this causes warnings with LGTM checks: #define DEVCMDMSG(fmt,x,...) ADD_MESSAGE(" > " fmt,x); -#define DEVCMDMSG1P(fmt,x) ADD_MESSAGE(" > " fmt,x); -#define DEVCMDMSG2P(fmt,x,y) ADD_MESSAGE(" > " fmt,x,y); -#define DEVCMDMSG3P(fmt,x,y,z) ADD_MESSAGE(" > " fmt,x,y,z); -#define DEVCMDMSG4P(fmt,x,y,z,a) ADD_MESSAGE(" > " fmt,x,y,z,a); -#define DEVCMDMSG15P(fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) ADD_MESSAGE(" > " fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o); +#define DEVCMDMSGTAG " > " +#define DEVCMDMSG1P(fmt,x) ADD_MESSAGE(DEVCMDMSGTAG fmt,x); +#define DEVCMDMSG2P(fmt,x,y) ADD_MESSAGE(DEVCMDMSGTAG fmt,x,y); +#define DEVCMDMSG3P(fmt,x,y,z) ADD_MESSAGE(DEVCMDMSGTAG fmt,x,y,z); +#define DEVCMDMSG4P(fmt,x,y,z,a) ADD_MESSAGE(DEVCMDMSGTAG fmt,x,y,z,a); +#define DEVCMDMSG15P(fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) ADD_MESSAGE(DEVCMDMSGTAG fmt,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o); typedef void (*callcmd)(festring); diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 5ce2a1d1a..a61cb6449 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -66,6 +66,19 @@ void TeleToChar(festring fsFilter){ } } } +void TeleToMe(festring fsFilter){ + characteridmap map = game::GetCharacterIDMapCopy(); + for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ + character* C = itr->second; + if(!IsValidChar(C)) + continue; + + if(C->GetName(DEFINITE).Find(fsFilter)!=festring::NPos){ + C->TeleportNear(game::GetPlayer()); + DEVCMDMSG2P("%d %s",C,C->GetName(DEFINITE).CStr()); + } + } +} void ListChars(festring fsFilter){ ulong idFilter=0; if(!fsFilter.IsEmpty()) @@ -383,6 +396,7 @@ void devcons::OpenCommandsConsole() ADDCMD(DelChars,"[count:int] delete characters (from the list filled on the previous command) up to count if set.",true); ADDCMD(DelItems,"[count:int] delete items (from the list filled on the previous command) up to count if set.",true); ADDCMD(TeleToChar," teleports near 1st character matching filter.",true); + ADDCMD(TeleToMe," teleports all NPCs matching filter to you.",true); #endif return true; }(); @@ -504,6 +518,7 @@ void devcons::runCommand(festring fsFullCmd) } // ADD_MESSAGE("%sTrying to run: %s ('%s' '%s')",cPrompt,strFullCmd.c_str(),strCmd.c_str(),strParams.c_str()); + DEVCMDMSG1P("%s",DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG DEVCMDMSGTAG ); DEVCMDMSG3P("Trying to run: %s ('%s' '%s')", festring(strFullCmd.c_str()).CStr(), festring(strCmd.c_str()).CStr(), @@ -514,9 +529,9 @@ void devcons::runCommand(festring fsFullCmd) if(cc){ cc(strParams.c_str()); // ADD_MESSAGE("%scommand %s completed",cPrompt,strCmd.c_str()); - DEVCMDMSG1P("command %s completed",strCmd.c_str()); + DEVCMDMSG1P(" <<< command %s completed",strCmd.c_str()); }else{ // ADD_MESSAGE("%scommand %s not found",cPrompt,strCmd.c_str()); - DEVCMDMSG1P("command %s not found",strCmd.c_str()); + DEVCMDMSG1P(" <<< command %s not found",strCmd.c_str()); } } From 0ee0e2fc526885927e85c17a5a35ec45d698cacb Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 16 Apr 2020 21:29:05 -0300 Subject: [PATCH 126/235] whandler (key handler) globalwindowhandler::ProcessKeyDownMessage() Allowed more keys to be detected. DELETE key now works on editing any string question. INSERT is now detected too and can be used on custom keybindings. --- .../nbproject/configurations.xml | 488 ++++++++++++++++++ .../AquariusPower/nbproject/project.xml | 1 + FeLib/Source/feio.cpp | 22 +- FeLib/Source/whandler.cpp | 16 +- Main/Source/game.cpp | 4 +- 5 files changed, 516 insertions(+), 15 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 491efccfb..9952dd0ec 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -31,6 +31,83 @@ graphics.h hscore.h + + + SDL.h + SDL_assert.h + SDL_atomic.h + SDL_audio.h + SDL_bits.h + SDL_blendmode.h + SDL_clipboard.h + SDL_config.h + SDL_cpuinfo.h + SDL_egl.h + SDL_endian.h + SDL_error.h + SDL_events.h + SDL_filesystem.h + SDL_gamecontroller.h + SDL_gesture.h + SDL_haptic.h + SDL_hints.h + SDL_joystick.h + SDL_keyboard.h + SDL_keycode.h + SDL_loadso.h + SDL_log.h + SDL_main.h + SDL_messagebox.h + SDL_mixer.h + SDL_mouse.h + SDL_mutex.h + SDL_name.h + SDL_opengl.h + SDL_opengl_glext.h + SDL_opengles.h + SDL_opengles2.h + SDL_opengles2_gl2.h + SDL_opengles2_gl2ext.h + SDL_opengles2_gl2platform.h + SDL_opengles2_khrplatform.h + SDL_pixels.h + SDL_platform.h + SDL_power.h + SDL_quit.h + SDL_rect.h + SDL_render.h + SDL_revision.h + SDL_rwops.h + SDL_scancode.h + SDL_shape.h + SDL_stdinc.h + SDL_surface.h + SDL_system.h + SDL_syswm.h + SDL_test.h + SDL_test_assert.h + SDL_test_common.h + SDL_test_compare.h + SDL_test_crc32.h + SDL_test_font.h + SDL_test_fuzzer.h + SDL_test_harness.h + SDL_test_images.h + SDL_test_log.h + SDL_test_md5.h + SDL_test_memory.h + SDL_test_random.h + SDL_thread.h + SDL_timer.h + SDL_touch.h + SDL_types.h + SDL_version.h + SDL_video.h + SDL_vulkan.h + begin_code.h + close_code.h + + bitmap.cpp config.cpp @@ -135,6 +212,7 @@ Main/Source Main/Include FeLib/Include + SDL2-2.0.4/include Makefile @@ -158,6 +236,203 @@ ${CMAKE} -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${IDE_CC} -DCMAKE_CXX_COMPILER=${IDE_CXX} -DCMAKE_C_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_CXX_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -173,7 +448,15 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean + + + SDL2-2.0.4/include + + + + SDL2-2.0.4/include + DBGMSG FELIST_WAITKEYUP @@ -353,6 +636,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -375,6 +855,14 @@ + + + + SDL2-2.0.4/include/SDL.h + SDL2-2.0.4/include/SDL_version.h + + + diff --git a/.devsPrefs/AquariusPower/nbproject/project.xml b/.devsPrefs/AquariusPower/nbproject/project.xml index 491b5d418..04a97821a 100644 --- a/.devsPrefs/AquariusPower/nbproject/project.xml +++ b/.devsPrefs/AquariusPower/nbproject/project.xml @@ -17,6 +17,7 @@ Main/Source Main/Include FeLib/Include + SDL2-2.0.4/include diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp index f995cd602..93f874f05 100644 --- a/FeLib/Source/feio.cpp +++ b/FeLib/Source/feio.cpp @@ -397,11 +397,15 @@ int iosystem::StringQuestion(festring& Input, character not available in the font */ while(!(LastKey >= 0x20 && LastKey < 0x7F) - && LastKey != KEY_ENTER && LastKey != KEY_ESC - && LastKey != KEY_HOME && LastKey != KEY_END - && LastKey != KEY_LEFT && LastKey != KEY_RIGHT - && LastKey != KEY_BACK_SPACE) - { + && LastKey != KEY_BACK_SPACE + && LastKey != SDLK_DELETE + && LastKey != KEY_END + && LastKey != KEY_ENTER + && LastKey != KEY_ESC + && LastKey != KEY_HOME + && LastKey != KEY_LEFT + && LastKey != KEY_RIGHT + ){ LastKey = GET_KEY(false); if(StringKeyHandler != 0 && StringKeyHandler(LastKey, Input)) @@ -427,6 +431,14 @@ int iosystem::StringQuestion(festring& Input, continue; } + if(LastKey == SDLK_DELETE) + { + if(CursorPos < Input.GetSize()) + Input.Erase(static_cast(CursorPos), 1); + + continue; + } + if(LastKey == KEY_ENTER) { if(Input.GetSize() >= MinLetters) diff --git a/FeLib/Source/whandler.cpp b/FeLib/Source/whandler.cpp index 90054e091..6dde08f6b 100644 --- a/FeLib/Source/whandler.cpp +++ b/FeLib/Source/whandler.cpp @@ -657,7 +657,7 @@ void globalwindowhandler::ProcessKeyDownMessage(SDL_Event* Event) { case SDLK_RETURN: case SDLK_KP_ENTER: - // both SDL keys are mixed into KEY_ENTER + // ex.: both SDL keys are mixed into KEY_ENTER KeyPressed = KEY_ENTER; //TODO SDL1? old comment tip or deadCode: Event->key.keysym.unicode; break; @@ -704,17 +704,17 @@ void globalwindowhandler::ProcessKeyDownMessage(SDL_Event* Event) KeyPressed = iRestWaitKey; break; -#if SDL_MAJOR_VERSION == 2 //TODO there is no ESC on SDL1??? but does SDL1 still compiles? anyone uses it yet??? the same question about DJGPP... - case SDLK_ESCAPE: - case SDLK_BACKSPACE: - KeyPressed = Event->key.keysym.sym; - break; +#if SDL_MAJOR_VERSION == 2 + default: + KeyPressed = Event->key.keysym.sym; + if(!KeyPressed) + return; #endif -#if SDL_MAJOR_VERSION == 1 +//TODO SDL1 still compiles? anyone uses it yet??? the same question about DJGPP... +#if SDL_MAJOR_VERSION == 1 default: KeyPressed = Event->key.keysym.unicode; - if(!KeyPressed) return; #endif diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index af2d58adf..e46066960 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5907,14 +5907,14 @@ truth game::ValidateCustomCmdKey(int iNewKey, int iIgnoreIndex, bool bMoveKeys) } } if(!bValid){ - ADD_MESSAGE("SYSTEM: conflicting key '%s'(code is %d or 0x%X) with command \"%s\", retry...", + ADD_MESSAGE("SYSTEM: conflicting key '%s'(code is %d or 0x%04X) with command \"%s\", retry...", ToCharIfPossible(iNewKey).CStr(),iNewKey,iNewKey,fsDesc.CStr()); } // general invalid key codes if(bValid){ if(iNewKey<0x20){ - ADD_MESSAGE("SYSTEM: invalid key code 0x%X, retry...",iNewKey); + ADD_MESSAGE("SYSTEM: invalid key code 0x%04X, retry...",iNewKey); bValid=false; } } From ce3cb6786094fdee292a02bc52442693c87c82b0 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 16 Apr 2020 23:34:41 -0300 Subject: [PATCH 127/235] moved some functions: fixChars() GetUserDataDir() to be more useful; WIP: adding history to every iosystem::StringQuestion(); fixed/restored Shift keys usability; --- .../nbproject/configurations.xml | 485 +----------------- FeLib/Include/feio.h | 1 + FeLib/Include/save.h | 2 + FeLib/Source/feio.cpp | 94 +++- FeLib/Source/save.cpp | 21 + FeLib/Source/whandler.cpp | 5 +- Main/Include/game.h | 1 - Main/Source/char.cpp | 2 +- Main/Source/command.cpp | 4 +- Main/Source/definesvalidator.cpp | 2 +- Main/Source/game.cpp | 44 +- Main/Source/iconf.cpp | 4 +- Main/Source/main.cpp | 4 +- Main/Source/message.cpp | 2 +- 14 files changed, 126 insertions(+), 545 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 9952dd0ec..425f2bcc0 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -31,82 +31,7 @@ graphics.h hscore.h - - - SDL.h - SDL_assert.h - SDL_atomic.h - SDL_audio.h - SDL_bits.h - SDL_blendmode.h - SDL_clipboard.h - SDL_config.h - SDL_cpuinfo.h - SDL_egl.h - SDL_endian.h - SDL_error.h - SDL_events.h - SDL_filesystem.h - SDL_gamecontroller.h - SDL_gesture.h - SDL_haptic.h - SDL_hints.h - SDL_joystick.h - SDL_keyboard.h - SDL_keycode.h - SDL_loadso.h - SDL_log.h - SDL_main.h - SDL_messagebox.h - SDL_mixer.h - SDL_mouse.h - SDL_mutex.h - SDL_name.h - SDL_opengl.h - SDL_opengl_glext.h - SDL_opengles.h - SDL_opengles2.h - SDL_opengles2_gl2.h - SDL_opengles2_gl2ext.h - SDL_opengles2_gl2platform.h - SDL_opengles2_khrplatform.h - SDL_pixels.h - SDL_platform.h - SDL_power.h - SDL_quit.h - SDL_rect.h - SDL_render.h - SDL_revision.h - SDL_rwops.h - SDL_scancode.h - SDL_shape.h - SDL_stdinc.h - SDL_surface.h - SDL_system.h - SDL_syswm.h - SDL_test.h - SDL_test_assert.h - SDL_test_common.h - SDL_test_compare.h - SDL_test_crc32.h - SDL_test_font.h - SDL_test_fuzzer.h - SDL_test_harness.h - SDL_test_images.h - SDL_test_log.h - SDL_test_md5.h - SDL_test_memory.h - SDL_test_random.h - SDL_thread.h - SDL_timer.h - SDL_touch.h - SDL_types.h - SDL_version.h - SDL_video.h - SDL_vulkan.h - begin_code.h - close_code.h - + bitmap.cpp @@ -236,203 +161,6 @@ ${CMAKE} -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${IDE_CC} -DCMAKE_CXX_COMPILER=${IDE_CXX} -DCMAKE_C_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_CXX_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -448,14 +176,10 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean - - - SDL2-2.0.4/include - - SDL2-2.0.4/include + /usr/include DBGMSG @@ -636,203 +360,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -855,14 +382,6 @@ - - - - SDL2-2.0.4/include/SDL.h - SDL2-2.0.4/include/SDL_version.h - - - diff --git a/FeLib/Include/feio.h b/FeLib/Include/feio.h index 880435731..76f6b63dd 100644 --- a/FeLib/Include/feio.h +++ b/FeLib/Include/feio.h @@ -29,6 +29,7 @@ class iosystem static festring ContinueMenu(col16, col16, cfestring&, const int iSaveFileVersion, bool bAllowImportOldSavegame); static void SetSkipSeekSave(skipseeksave); static void SetSaveGameSortMode(int i); + static void fixChars(festring& fs); static int StringQuestion(festring&, cfestring&, v2, col16, festring::sizetype, festring::sizetype, truth, truth, stringkeyhandler = 0); diff --git a/FeLib/Include/save.h b/FeLib/Include/save.h index be94a9343..f92fad191 100644 --- a/FeLib/Include/save.h +++ b/FeLib/Include/save.h @@ -474,4 +474,6 @@ inline inputfile& LoadArray(inputfile& SaveFile, return SaveFile; } +festring GetUserDataDir(); + #endif diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp index 93f874f05..b4135ada7 100644 --- a/FeLib/Source/feio.cpp +++ b/FeLib/Source/feio.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef WIN32 #define stat _stat @@ -325,6 +326,21 @@ int iosystem::Menu(cbitmap* BackGround, v2 Pos, return iSelected; } +/** + * this prevents all possibly troublesome characters in all OSs + */ +void iosystem::fixChars(festring& fs) +{ + for(festring::sizetype i = 0; i < fs.GetSize(); ++i) + { + if(fs[i]>='A' && fs[i]<='Z')continue; + if(fs[i]>='a' && fs[i]<='z')continue; + if(fs[i]>='0' && fs[i]<='9')continue; + + fs[i] = '_'; + } +} + /* Asks the user a question requiring a string answer. The answer is saved to Input. Input can also already have a default something pretyped for the user. Topic is the question or other topic for the question. Pos is the @@ -346,7 +362,23 @@ int iosystem::StringQuestion(festring& Input, stringkeyhandler StringKeyHandler) { bInUse=true; - + + // history + festring fsHistFile = festring()+GetUserDataDir()+".QuestionHistory-"+Topic; + fixChars(fsHistFile); + fsHistFile<<".txt"; + FILE *fl = fopen(fsHistFile.CStr(), "a+"); + rewind(fl); + festring Line; + static const int iBuffSz=0xFF; + char str[iBuffSz]; + std::vector vHist; + while(fgets(str, iBuffSz, fl)){ + Line=str; + Line.Resize(Line.GetSize()-1); + vHist.push_back(Line); //removes trailing \n + } + v2 V(RES.X, 10); ///??????????? bitmap BackUp(V, 0); blitdata B = { &BackUp, @@ -372,6 +404,9 @@ int iosystem::StringQuestion(festring& Input, FONT->Printf(DOUBLE_BUFFER, Pos, Color, "%s", Topic.CStr()); Swap(B.Src, B.Dest); + bool bAbort = false; + int iHistIndex = 0; + if(vHist.size())iHistIndex=vHist.size()-1; for(int LastKey = 0, CursorPos = Input.GetSize();; LastKey = 0) { B.Bitmap = DOUBLE_BUFFER; @@ -397,20 +432,22 @@ int iosystem::StringQuestion(festring& Input, character not available in the font */ while(!(LastKey >= 0x20 && LastKey < 0x7F) - && LastKey != KEY_BACK_SPACE - && LastKey != SDLK_DELETE - && LastKey != KEY_END - && LastKey != KEY_ENTER - && LastKey != KEY_ESC - && LastKey != KEY_HOME - && LastKey != KEY_LEFT - && LastKey != KEY_RIGHT + && LastKey != KEY_BACK_SPACE + && LastKey != SDLK_DELETE + && LastKey != KEY_DOWN + && LastKey != KEY_END + && LastKey != KEY_ENTER + && LastKey != KEY_ESC + && LastKey != KEY_HOME + && LastKey != KEY_LEFT + && LastKey != KEY_RIGHT + && LastKey != KEY_UP ){ LastKey = GET_KEY(false); if(StringKeyHandler != 0 && StringKeyHandler(LastKey, Input)) { - LastKey = 0; + LastKey = 0; // to `continue;` below break; } } @@ -420,7 +457,8 @@ int iosystem::StringQuestion(festring& Input, if(LastKey == KEY_ESC && AllowExit){ bInUse=false; - return ABORTED; + bAbort=true; + break; } if(LastKey == KEY_BACK_SPACE) @@ -480,12 +518,40 @@ int iosystem::StringQuestion(festring& Input, continue; } + if(LastKey == KEY_UP) + { + if(iHistIndex == (vHist.size()-1)) + vHist.push_back(Input); + + --iHistIndex; + if(iHistIndex<0)iHistIndex=0; + Input=vHist[iHistIndex]; + if(CursorPos>Input.GetSize()) + CursorPos=Input.GetSize(); + + continue; + } + + if(LastKey == KEY_DOWN) + { + ++iHistIndex; + if(iHistIndex>=vHist.size())iHistIndex=vHist.size()-1; + Input=vHist[iHistIndex]; + if(CursorPos>Input.GetSize()) + CursorPos=Input.GetSize(); + + continue; + } + if(LastKey >= 0x20 && LastKey < 0x7F && (LastKey != ' ' || !Input.IsEmpty()) && Input.GetSize() < MaxLetters) Input.Insert(static_cast(CursorPos++), static_cast(LastKey)); } + + if(bAbort) + return ABORTED; /* Delete all the trailing spaces */ @@ -499,6 +565,12 @@ int iosystem::StringQuestion(festring& Input, Input.Resize(LastAlpha + 1); + if(!vHist.size() || Input!=vHist[vHist.size()-1]){ + //vHist.push_back(Input); + fprintf(fl, "%s\n", Input.CStr()); + } + fclose(fl); + bInUse=false; return NORMAL_EXIT; } diff --git a/FeLib/Source/save.cpp b/FeLib/Source/save.cpp index 3300db812..fb1c40473 100644 --- a/FeLib/Source/save.cpp +++ b/FeLib/Source/save.cpp @@ -808,3 +808,24 @@ void MakePath(cfestring& Path) #endif } } + +festring GetUserDataDir() +{ +#ifdef UNIX + festring Dir; + Dir << getenv("HOME"); +#ifdef MAC_APP + Dir << "/Library/Application Support/IVAN/"; +#else + Dir << "/.ivan/"; +#endif +#ifdef DBGMSG + dbgmsg::SetDebugLogPath(Dir.CStr()); +#endif + return Dir; +#endif + +#if defined(WIN32) || defined(__DJGPP__) + return "./"; +#endif +} diff --git a/FeLib/Source/whandler.cpp b/FeLib/Source/whandler.cpp index 6dde08f6b..60e1f880d 100644 --- a/FeLib/Source/whandler.cpp +++ b/FeLib/Source/whandler.cpp @@ -628,6 +628,9 @@ void globalwindowhandler::ProcessKeyDownMessage(SDL_Event* Event) break; } return; + }else + if(Event->key.keysym.mod & KMOD_SHIFT){ + return; } // other special non buffered keys @@ -703,7 +706,7 @@ void globalwindowhandler::ProcessKeyDownMessage(SDL_Event* Event) case SDLK_KP_5: KeyPressed = iRestWaitKey; break; - + #if SDL_MAJOR_VERSION == 2 default: KeyPressed = Event->key.keysym.sym; diff --git a/Main/Include/game.h b/Main/Include/game.h index e14185e68..ed69ec00b 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -371,7 +371,6 @@ class game static void SetIsInGetCommand(truth What) { InGetCommand = What; } static truth IsInGetCommand() { return InGetCommand; } static festring GetDataDir(); - static festring GetUserDataDir(); static festring GetSaveDir(); static festring GetScrshotDir(); static festring GetBoneDir(); diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index e233841d4..e2b20b966 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -2547,7 +2547,7 @@ void character::AddScoreEntry(cfestring& Description, double Multiplier, truth A { if(!game::WizardModeIsReallyActive()) { - highscore HScore(game::GetUserDataDir() + HIGH_SCORE_FILENAME); + highscore HScore(GetUserDataDir() + HIGH_SCORE_FILENAME); if(!HScore.CheckVersion()) { diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index e21f1cd15..414a127f5 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -2371,8 +2371,8 @@ truth commandsystem::SecretKnowledge(character* Char) delete Material[c]; } - List.PrintToFile(game::GetUserDataDir() + "secret" + Chosen + ".txt"); - ADD_MESSAGE("Info written also to %ssecret%d.txt.", game::GetUserDataDir().CStr(), Chosen); + List.PrintToFile(GetUserDataDir() + "secret" + Chosen + ".txt"); + ADD_MESSAGE("Info written also to %ssecret%d.txt.", GetUserDataDir().CStr(), Chosen); return false; } diff --git a/Main/Source/definesvalidator.cpp b/Main/Source/definesvalidator.cpp index d860c7c72..9d9c11179 100644 --- a/Main/Source/definesvalidator.cpp +++ b/Main/Source/definesvalidator.cpp @@ -36,7 +36,7 @@ void DefinesValidatorAppend(std::string s) static bool bDummyInit = [](){ DefinesValidator.open( - festring(game::GetUserDataDir() + "definesvalidator.h").CStr(), + festring(GetUserDataDir() + "definesvalidator.h").CStr(), std::ios::binary); return true;}(); diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index e46066960..90ae874d7 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -3587,21 +3587,6 @@ int game::Load(cfestring& saveName) return LOADED; } -/** - * this prevents all possibly troublesome characters in all OSs - */ -void fixChars(festring& fs) -{ - for(festring::sizetype i = 0; i < fs.GetSize(); ++i) - { - if(fs[i]>='A' && fs[i]<='Z')continue; - if(fs[i]>='a' && fs[i]<='z')continue; - if(fs[i]>='0' && fs[i]<='9')continue; - - fs[i] = '_'; - } -} - bool chkAutoSaveSuffix(festring& fs,bool bAlsoFixIt=false){DBG1(fs.CStr()); std::string strChk; strChk = fs.CStr(); @@ -3639,7 +3624,7 @@ festring game::SaveName(cfestring& Base,bool bLoadingFromAnAutosave) else { // this is important in case player name changes like when using the fantasy name generator - festring fsPN; fsPN<SetCustomKey(iNewKey); } - festring fsFl = game::GetUserDataDir() + CUSTOM_KEYS_FILENAME; + festring fsFl = GetUserDataDir() + CUSTOM_KEYS_FILENAME; // backup existing festring fsFlBkp=fsFl+".bkp"; diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 88710655e..d4af591cc 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -1192,9 +1192,9 @@ void ivanconfig::Initialize() * LOAD AND APPLY some SETTINGS * ********************************/ #if defined(WIN32) || defined(__DJGPP__) - configsystem::SetConfigFileName(game::GetUserDataDir() + "ivan.cfg"); + configsystem::SetConfigFileName(GetUserDataDir() + "ivan.cfg"); #else - configsystem::SetConfigFileName(game::GetUserDataDir() + "ivan.conf"); + configsystem::SetConfigFileName(GetUserDataDir() + "ivan.conf"); #endif configsystem::Load(); diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index 9344b9320..6ea753fa4 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char** argv) signal(SIGQUIT, CrashHandler); #endif - game::GetUserDataDir(); //just to properly initialize as soon as possible DBGMSG correct path b4 everywhere it may be used. + GetUserDataDir(); //just to properly initialize as soon as possible DBGMSG correct path b4 everywhere it may be used. if(argc > 1 && festring(argv[1]) == "--version") { @@ -235,7 +235,7 @@ int main(int argc, char** argv) break; case 3: { - highscore HScore(game::GetUserDataDir() + HIGH_SCORE_FILENAME); + highscore HScore(GetUserDataDir() + HIGH_SCORE_FILENAME); HScore.Draw(); break; } diff --git a/Main/Source/message.cpp b/Main/Source/message.cpp index 0938e836c..25c3da0aa 100644 --- a/Main/Source/message.cpp +++ b/Main/Source/message.cpp @@ -336,7 +336,7 @@ void soundsystem::initSound() if(SoundState == 0) { - festring fsSndDbgFile = game::GetUserDataDir() + "ivanSndDebug.txt"; + festring fsSndDbgFile = GetUserDataDir() + "ivanSndDebug.txt"; debf = fopen(fsSndDbgFile.CStr(), "wt"); //"a"); if(debf)fprintf(debf, "This file can be used to diagnose problems with sound.\n"); From c75854e5eb14dc8b540350983e1cf522af7649f9 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 16 Apr 2020 23:40:49 -0300 Subject: [PATCH 128/235] fixed history files placement; --- FeLib/Source/feio.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp index b4135ada7..239e9430a 100644 --- a/FeLib/Source/feio.cpp +++ b/FeLib/Source/feio.cpp @@ -364,9 +364,10 @@ int iosystem::StringQuestion(festring& Input, bInUse=true; // history - festring fsHistFile = festring()+GetUserDataDir()+".QuestionHistory-"+Topic; - fixChars(fsHistFile); - fsHistFile<<".txt"; + festring fsFixTopicToFileName = Topic; + fixChars(fsFixTopicToFileName); + festring fsHistFile = festring()+GetUserDataDir()+".QuestionHistory_"+fsFixTopicToFileName+".txt"; + DBG1(fsHistFile.CStr()); FILE *fl = fopen(fsHistFile.CStr(), "a+"); rewind(fl); festring Line; From e716f42bbfddcdd9372b1b4d637b3762474d78be Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 16 Apr 2020 23:52:45 -0300 Subject: [PATCH 129/235] fixed string question history navigation and file naming. --- FeLib/Source/feio.cpp | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp index 239e9430a..0172df9d9 100644 --- a/FeLib/Source/feio.cpp +++ b/FeLib/Source/feio.cpp @@ -363,8 +363,19 @@ int iosystem::StringQuestion(festring& Input, { bInUse=true; - // history + /** + * History files are based on tokens to make them more shareable. + * Questions (topic) must have '?' or ':' after the static string part and + * after that all can be dynamic. + * So the files will be named up to these tokens only! + * TODO This means some questions should be revised to share the history file for the very "same" base question... + */ festring fsFixTopicToFileName = Topic; + ulong cutAt; + cutAt = fsFixTopicToFileName.Find("?"); + if(cutAt!=festring::NPos)fsFixTopicToFileName.Resize(cutAt); + cutAt = fsFixTopicToFileName.Find(":"); + if(cutAt!=festring::NPos)fsFixTopicToFileName.Resize(cutAt); fixChars(fsFixTopicToFileName); festring fsHistFile = festring()+GetUserDataDir()+".QuestionHistory_"+fsFixTopicToFileName+".txt"; DBG1(fsHistFile.CStr()); @@ -521,25 +532,29 @@ int iosystem::StringQuestion(festring& Input, if(LastKey == KEY_UP) { - if(iHistIndex == (vHist.size()-1)) - vHist.push_back(Input); - - --iHistIndex; - if(iHistIndex<0)iHistIndex=0; - Input=vHist[iHistIndex]; - if(CursorPos>Input.GetSize()) - CursorPos=Input.GetSize(); + if(vHist.size()){ + if(iHistIndex == (vHist.size()-1)) + vHist.push_back(Input); + + --iHistIndex; + if(iHistIndex<0)iHistIndex=0; + Input=vHist[iHistIndex]; + if(CursorPos>Input.GetSize()) + CursorPos=Input.GetSize(); + } continue; } if(LastKey == KEY_DOWN) { - ++iHistIndex; - if(iHistIndex>=vHist.size())iHistIndex=vHist.size()-1; - Input=vHist[iHistIndex]; - if(CursorPos>Input.GetSize()) - CursorPos=Input.GetSize(); + if(vHist.size()){ + ++iHistIndex; + if(iHistIndex>=vHist.size())iHistIndex=vHist.size()-1; + Input=vHist[iHistIndex]; + if(CursorPos>Input.GetSize()) + CursorPos=Input.GetSize(); + } continue; } From d49eb9a4dd2cf2f4713b0adff7336e763f4224d5 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 17 Apr 2020 02:00:14 -0300 Subject: [PATCH 130/235] developer console: FillWithWalls() new test; --- Main/Source/devcons.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index a61cb6449..125a69bb7 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -26,6 +26,8 @@ #include "message.h" #include "stack.h" #include "specialkeys.h" +#include "confdef.h" +#include "lterras.h" /** * ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! @@ -79,6 +81,16 @@ void TeleToMe(festring fsFilter){ } } } +void FillWithWalls(festring fsFilter){ + for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ + lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(iX,iY); + + if(lsqr->GetOLTerrain())continue; + if(lsqr->GetCharacter())continue; + if(!(lsqr->GetGLTerrain()->GetWalkability() & WALK))continue; + lsqr->ChangeOLTerrainAndUpdateLights(wall::Spawn(STONE_WALL)); + }} +} void ListChars(festring fsFilter){ ulong idFilter=0; if(!fsFilter.IsEmpty()) @@ -390,11 +402,12 @@ void devcons::OpenCommandsConsole() ADDCMD(Help,"show this help",false); AddDevCmd("?",Help,"show this help",false); #ifdef WIZARD - ADDCMD(SetVar,festring()<<" set a float variable index (max "<<(iVarTot-1)<<") to be used on debug",true); - ADDCMD(ListChars,"[[filterCharID:ulong]|[strCharNamePart:string]] List characters on current dungeon level",true); - ADDCMD(ListItems,"[[c|i] <|>] List items on current dungeon level, including on characters ('c' will filter by character ID or name) inventory and containers",true); + ADDCMD(FillWithWalls,"all empty (of OLTerrain) squares.",true); ADDCMD(DelChars,"[count:int] delete characters (from the list filled on the previous command) up to count if set.",true); ADDCMD(DelItems,"[count:int] delete items (from the list filled on the previous command) up to count if set.",true); + ADDCMD(ListChars,"[[filterCharID:ulong]|[strCharNamePart:string]] List characters on current dungeon level",true); + ADDCMD(ListItems,"[[c|i] <|>] List items on current dungeon level, including on characters ('c' will filter by character ID or name) inventory and containers",true); + ADDCMD(SetVar,festring()<<" set a float variable index (max "<<(iVarTot-1)<<") to be used on debug",true); ADDCMD(TeleToChar," teleports near 1st character matching filter.",true); ADDCMD(TeleToMe," teleports all NPCs matching filter to you.",true); #endif From 571d63c53f454492d6c0ec202067d88c98d42d20 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 17 Apr 2020 02:28:51 -0300 Subject: [PATCH 131/235] developer console: FillWithWalls() fixed memorizeds; --- Main/Source/devcons.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 125a69bb7..89ca4d37e 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -82,6 +82,7 @@ void TeleToMe(festring fsFilter){ } } void FillWithWalls(festring fsFilter){ + int iCount=0; for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(iX,iY); @@ -89,7 +90,12 @@ void FillWithWalls(festring fsFilter){ if(lsqr->GetCharacter())continue; if(!(lsqr->GetGLTerrain()->GetWalkability() & WALK))continue; lsqr->ChangeOLTerrainAndUpdateLights(wall::Spawn(STONE_WALL)); + iCount++; +// lsqr->Reveal(); }} + DEVCMDMSG1P("new walls: %d",iCount); + level* l = game::GetCurrentLevel(); + if(l)l->Reveal(); } void ListChars(festring fsFilter){ ulong idFilter=0; From 9cca7e5ebded7060499b0894ae2094df3386974b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 17 Apr 2020 16:47:01 -0300 Subject: [PATCH 132/235] fixed devcons character validation for it's commands; WIP: detection and naming for DELETE and INSERT keys; --- .devsPrefs/AquariusPower/nbproject/configurations.xml | 8 ++++---- FeLib/Include/felibdef.h | 1 + Main/Source/devcons.cpp | 7 ++++--- Main/Source/game.cpp | 4 ++++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 425f2bcc0..d86d10355 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -31,9 +31,9 @@ graphics.h hscore.h - + - + bitmap.cpp config.cpp dbgmsg.cpp @@ -51,7 +51,7 @@ specialkeys.cpp whandler.cpp - + action.cpp actions.cpp actset.cpp @@ -110,7 +110,7 @@ wterra.cpp wterras.cpp - + config.h xbrz.cpp diff --git a/FeLib/Include/felibdef.h b/FeLib/Include/felibdef.h index 3d40e85b5..b08161756 100644 --- a/FeLib/Include/felibdef.h +++ b/FeLib/Include/felibdef.h @@ -187,6 +187,7 @@ inline int GetMinColor24(col24 Color) #endif #if SDL_MAJOR_VERSION == 2 || !defined(__APPLE__) #define KEY_BACK_SPACE 0x08 +#define KEY_DELETE 0x7F //ugh... this conflicts with SDL1 backspace? well does it still even compiles on SDL1??? #else #define KEY_BACK_SPACE 0x7F #endif diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 89ca4d37e..62cb2b3c8 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -53,6 +53,8 @@ truth IsValidChar(character* C){ return false; if(!C->GetLSquareUnder()) return false; + if(!C->IsEnabled()) + return false; return true; } void TeleToChar(festring fsFilter){ @@ -83,6 +85,7 @@ void TeleToMe(festring fsFilter){ } void FillWithWalls(festring fsFilter){ int iCount=0; + ulong Tick = game::GetLOSTick(); for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(iX,iY); @@ -90,12 +93,10 @@ void FillWithWalls(festring fsFilter){ if(lsqr->GetCharacter())continue; if(!(lsqr->GetGLTerrain()->GetWalkability() & WALK))continue; lsqr->ChangeOLTerrainAndUpdateLights(wall::Spawn(STONE_WALL)); + lsqr->Reveal(Tick); iCount++; -// lsqr->Reveal(); }} DEVCMDMSG1P("new walls: %d",iCount); - level* l = game::GetCurrentLevel(); - if(l)l->Reveal(); } void ListChars(festring fsFilter){ ulong idFilter=0; diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 90ae874d7..7b09489df 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5914,6 +5914,10 @@ festring game::ToCharIfPossible(int i) return "PgDn"; case KEY_PAGE_UP: return "PgUp"; + case KEY_DELETE: + return "Del"; + case 0x3FFF2049: //KEY_INSERT: //TODO everykeyboard generates this huge value??? + return "Ins"; } if(i>=0 && i<=0xFF) //these are mapped at fonts gfx files From 268d8fc70fcf735f9fd35199076c397287f5e46f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 17 Apr 2020 16:57:43 -0300 Subject: [PATCH 133/235] devcons: FillWithWalls optional distance around player; --- Main/Source/devcons.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 62cb2b3c8..0ce13f34d 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -84,14 +84,25 @@ void TeleToMe(festring fsFilter){ } } void FillWithWalls(festring fsFilter){ + int iAround=0; + if(!fsFilter.IsEmpty()) + iAround=atoi(fsFilter.CStr()); int iCount=0; ulong Tick = game::GetLOSTick(); + v2 v2PPos=game::GetPlayer()->GetPos(); for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(iX,iY); if(lsqr->GetOLTerrain())continue; if(lsqr->GetCharacter())continue; if(!(lsqr->GetGLTerrain()->GetWalkability() & WALK))continue; + if(iAround>0){ + if(lsqr->GetPos().X > v2PPos.X+iAround)continue; + if(lsqr->GetPos().X < v2PPos.X-iAround)continue; + if(lsqr->GetPos().Y > v2PPos.Y+iAround)continue; + if(lsqr->GetPos().Y < v2PPos.Y-iAround)continue; + } + lsqr->ChangeOLTerrainAndUpdateLights(wall::Spawn(STONE_WALL)); lsqr->Reveal(Tick); iCount++; @@ -409,7 +420,7 @@ void devcons::OpenCommandsConsole() ADDCMD(Help,"show this help",false); AddDevCmd("?",Help,"show this help",false); #ifdef WIZARD - ADDCMD(FillWithWalls,"all empty (of OLTerrain) squares.",true); + ADDCMD(FillWithWalls,"[distance:int] all empty (of OLTerrain) squares (optionally around player max distance).",true); ADDCMD(DelChars,"[count:int] delete characters (from the list filled on the previous command) up to count if set.",true); ADDCMD(DelItems,"[count:int] delete items (from the list filled on the previous command) up to count if set.",true); ADDCMD(ListChars,"[[filterCharID:ulong]|[strCharNamePart:string]] List characters on current dungeon level",true); From b038c754115e3b69def1ffcf7df74c5ae9b22959 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 17 Apr 2020 17:14:06 -0300 Subject: [PATCH 134/235] autoplay: applies wielded items too; --- Main/Source/human.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index d4bfbf745..c50562692 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -3856,6 +3856,8 @@ truth humanoid::AutoPlayAIequipConsumeZapReadApply() static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); static itemvector vitA;vitA.clear(); + if(GetLeftWielded())vitEqW.push_back(GetLeftWielded()); + if(GetRightWielded())vitEqW.push_back(GetRightWielded()); for(uint c = 0; c < vitEqW.size(); ++c){ if(AutoPlayAIcanApply(vitEqW[c])) vitA.push_back(vitEqW[c]); From 632b42884e3e77e6428fa680dee1e386ed343e77 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 19 Apr 2020 01:28:44 -0300 Subject: [PATCH 135/235] felib new KEY_DELETE KEY_INSERT safe translation from SDL captured scancode; restored KP_PERIOD as wait key; clarified craft::Handle() tool wielding code; CutWeb: does not use craft::Handle(), so removed wielding tool to prevent a bug of not restoring previously equipped weapon; Crafting: everywhere using clock()%SomeInteger was NOT reliable, like in it would never fumble, fixed with proper RAND() that shall be used to anything related to normal gameplay randomness; Crafting: clarified code just before allowing crafting; --- FeLib/Include/felibdef.h | 17 +- FeLib/Source/whandler.cpp | 9 + Main/Include/char.h | 2 +- Main/Include/craft.h | 1 + Main/Include/human.h | 2 +- Main/Source/actions.cpp | 22 ++- Main/Source/cmdcraft.cpp | 402 ++++++++++++++++++++------------------ Main/Source/game.cpp | 2 +- Main/Source/human.cpp | 78 ++++---- 9 files changed, 288 insertions(+), 247 deletions(-) diff --git a/FeLib/Include/felibdef.h b/FeLib/Include/felibdef.h index b08161756..acc4ffce0 100644 --- a/FeLib/Include/felibdef.h +++ b/FeLib/Include/felibdef.h @@ -187,20 +187,21 @@ inline int GetMinColor24(col24 Color) #endif #if SDL_MAJOR_VERSION == 2 || !defined(__APPLE__) #define KEY_BACK_SPACE 0x08 -#define KEY_DELETE 0x7F //ugh... this conflicts with SDL1 backspace? well does it still even compiles on SDL1??? #else #define KEY_BACK_SPACE 0x7F #endif #define KEY_ESC 0x1B #define KEY_ENTER 0x0D -#define KEY_UP 0x148 -#define KEY_DOWN 0x150 -#define KEY_RIGHT 0x14D -#define KEY_LEFT 0x14B -#define KEY_HOME 0x147 -#define KEY_END 0x14F +#define KEY_HOME 0x147 +#define KEY_UP 0x148 +#define KEY_PAGE_UP 0x149 +#define KEY_LEFT 0x14B +#define KEY_RIGHT 0x14D +#define KEY_END 0x14F +#define KEY_DOWN 0x150 #define KEY_PAGE_DOWN 0x151 -#define KEY_PAGE_UP 0x149 +#define KEY_DELETE 0x152 +#define KEY_INSERT 0x153 #define KEY_SPACE ' ' #define KEY_NUMPAD_5 2 diff --git a/FeLib/Source/whandler.cpp b/FeLib/Source/whandler.cpp index 60e1f880d..54fb614ec 100644 --- a/FeLib/Source/whandler.cpp +++ b/FeLib/Source/whandler.cpp @@ -703,7 +703,16 @@ void globalwindowhandler::ProcessKeyDownMessage(SDL_Event* Event) KeyPressed = KEY_PAGE_DOWN + 0xE000; break; + case SDLK_DELETE: + KeyPressed = KEY_DELETE + 0xE000; + break; + + case SDLK_INSERT: + KeyPressed = KEY_INSERT + 0xE000; + break; + case SDLK_KP_5: + case SDLK_KP_PERIOD: KeyPressed = iRestWaitKey; break; diff --git a/Main/Include/char.h b/Main/Include/char.h index cb4c7ed71..eb7f2e935 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -428,7 +428,7 @@ class character : public entity, public id virtual truth CanConsume(material*) const; action* GetAction() const { return Action; } void SetAction(action* What) { Action = What; } - virtual void SwitchToCraft(recipedata rpd) { } + virtual bool SwitchToCraft(recipedata rpd) {return false;} virtual void SwitchToDig(item*, v2) { } virtual void SetRightWielded(item*) { } virtual void SetLeftWielded(item*) { } diff --git a/Main/Include/craft.h b/Main/Include/craft.h index f16206595..ae36040c2 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -235,6 +235,7 @@ class recipedata { item* GetTool2(){return itTool2;} bool IsFailedSuspendOrCancel(){return bFailedTerminateCancel || bFailedSuspend;} + void SetAlreadyExplained(){bAlreadyExplained=true;} }; class craftcore { diff --git a/Main/Include/human.h b/Main/Include/human.h index a9302f067..1ea5af57e 100644 --- a/Main/Include/human.h +++ b/Main/Include/human.h @@ -70,7 +70,7 @@ CHARACTER(humanoid, character) virtual bodypart* GetBodyPartOfEquipment(int) const; virtual item* GetEquipment(int) const; virtual int GetEquipments() const { return 13; } - virtual void SwitchToCraft(recipedata rpd); + virtual truth SwitchToCraft(recipedata rpd); virtual void SwitchToDig(item*, v2); virtual int GetUsableLegs() const; virtual int GetUsableArms() const; diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index fdfbd52fa..5e968ad1b 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -331,21 +331,23 @@ void craft::Handle() if(h){ if(h->GetRightArm()){ item* RightBackup = game::SearchItem(RightBackupID); - - if(RightBackup && RightBackup->Exists() && ActorLocal->IsOver(RightBackup)) - {DBGLN; - RightBackup->RemoveFromSlot(); - ActorLocal->SetRightWielded(RightBackup); + if(h->GetRightWielded() != RightBackup){ + if(RightBackup && RightBackup->Exists() && ActorLocal->IsOver(RightBackup)) + {DBGLN; + RightBackup->RemoveFromSlot(); + h->SetRightWielded(RightBackup); + } } } if(h->GetLeftArm()){ item* LeftBackup = game::SearchItem(LeftBackupID); - - if(LeftBackup && LeftBackup->Exists() && ActorLocal->IsOver(LeftBackup)) - {DBGLN; - LeftBackup->RemoveFromSlot(); - ActorLocal->SetLeftWielded(LeftBackup); + if(h->GetLeftWielded() != LeftBackup){ + if(LeftBackup && LeftBackup->Exists() && ActorLocal->IsOver(LeftBackup)) + {DBGLN; + LeftBackup->RemoveFromSlot(); + h->SetLeftWielded(LeftBackup); + } } } } diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index f60c60aa4..1161abf33 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -668,18 +668,18 @@ struct recipe{ void failPlacementMsg(recipedata& rpd){ ADD_MESSAGE("%s can't be placed here.",name.CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); } void failIngredientsMsg(recipedata& rpd){ festring fsMsg; fsMsg<<"Required ingredients to "<IsUsable())la=NULL; if(!ra && !la){ ADD_MESSAGE("You have no usable arm to do that."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } bool bSelfPos = rpd.lsqrPlaceAt->GetPos() == h->GetPos(); - rpd.itTool = FindCuttingTool(rpd); // no blunt, no non-cutting, imagine a web that can wold on air the weight of a whole body, only cutting tools - rpd.bAlreadyExplained=false; - item* wieldBkp=NULL; - bool bIsWBkpRHand = true; - if(rpd.itTool!=NULL){ - bool bWielded=false; - #define RLWIELD(rl,br) \ - if(!bWielded && h->Get##rl##Arm() && !h->Get##rl##Arm()->IsStuck()){ \ - if(h->Get##rl##Wielded()){ \ - wieldBkp = h->Get##rl##Wielded(); \ - wieldBkp->MoveTo(h->GetStack()); \ - } \ - rpd.itTool->RemoveFromSlot(); \ - h->Set##rl##Wielded(rpd.itTool); \ - bWielded=true; \ - bIsWBkpRHand=br; \ - } - RLWIELD(Right,true); - RLWIELD(Left,false); - } + rpd.itTool = FindCuttingTool(rpd); // no blunt, no non-cutting, imagine a web that can hold the weight of a whole body, only cutting tools + if(rpd.itTool) + ADD_MESSAGE("You will use your %s to cut the web.",rpd.itTool->GetName(UNARTICLED).CStr()); +// rpd.bAlreadyExplained=false; +// item* wieldBkp=NULL; +// bool bIsWBkpRHand = true; +// if(rpd.itTool!=NULL){ +// bool bWielded=false; +// #define RLWIELD(rl,br) \ +// if(!bWielded && h->Get##rl##Arm() && !h->Get##rl##Arm()->IsStuck()){ \ +// if(h->Get##rl##Wielded()){ \ +// wieldBkp = h->Get##rl##Wielded(); \ +// wieldBkp->MoveTo(h->GetStack()); \ +// } \ +// rpd.itTool->RemoveFromSlot(); \ +// h->Set##rl##Wielded(rpd.itTool); \ +// bWielded=true; \ +// bIsWBkpRHand=br; \ +// } +// RLWIELD(Right,true); +// RLWIELD(Left,false); +// } /** * IMPORTANT! @@ -1428,9 +1430,9 @@ struct srpCutWeb : public recipe{ } if(bSuccess){ - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); material* matSSilk=material::MakeMaterial(SPIDER_SILK); - craftcore::FinishSpawning(rpd, craftcore::PrepareRemains(rpd,matSSilk,CIT_LUMP,clock()%6+3)); + craftcore::FinishSpawning(rpd, craftcore::PrepareRemains(rpd,matSSilk,CIT_LUMP,RAND()%6+3)); }else{ bool bGetStuckOnTheWeb=false; bool bLoseWeapon=false; //TODO if has no weapon, lose one glove instead! @@ -1440,7 +1442,7 @@ struct srpCutWeb : public recipe{ if(bCriticalFumble){DBGLN; bGetStuckOnTheWeb=true; if(rpd.itTool!=NULL) - if(clock()%100 < 50){DBGLN; + if(RAND()%100 < 50){DBGLN; bLoseWeapon=true; } }else{DBGLN; @@ -1458,7 +1460,7 @@ struct srpCutWeb : public recipe{ h->PutTo(rpd.lsqrPlaceAt->GetPos()); //TODO check for walkability first! w->StepOnEffect(h); ADD_MESSAGE("You've got stuck on the web!"); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); }else{ if(rpd.itTool!=NULL){ bLoseWeapon=true; @@ -1470,7 +1472,7 @@ struct srpCutWeb : public recipe{ rpd.itTool->RemoveFromSlot(); rpd.itTool->MoveTo(rpd.lsqrPlaceAt->GetStack()); //TODO check if is not a WALL!!! ADD_MESSAGE("You lost your %s!",rpd.itTool->GetName(UNARTICLED).CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); } if(bSelfPos && !bGetStuckOnTheWeb && !bLoseWeapon){ @@ -1478,32 +1480,32 @@ struct srpCutWeb : public recipe{ } int iSt = w->GetTrapBaseModifier();DBG1(iSt); - iSt -= 1 + clock()%5; // small spider = 10, big = 25, wand beam = 50 + iSt -= 1 + RAND()%5; // small spider = 10, big = 25, wand beam = 50 if(iSt<=0) iSt=1; w->SetStrength(iSt); if(!rpd.bAlreadyExplained){ ADD_MESSAGE("You fail to tear down the web."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); } } - if(wieldBkp!=NULL){ - if(wieldBkp->GetSlot()->FindCarrier() == h){ - wieldBkp->RemoveFromSlot();; - if(bIsWBkpRHand){ - if(h->GetRightWielded()) - h->GetRightWielded()->MoveTo(h->GetStack()); - h->SetRightWielded(wieldBkp); - }else{ - if(h->GetLeftWielded()) - h->GetLeftWielded()->MoveTo(h->GetStack()); - h->SetLeftWielded(wieldBkp); - } - } - } +// if(wieldBkp!=NULL){ +// if(wieldBkp->GetSlot()->FindCarrier() == h){ +// wieldBkp->RemoveFromSlot();; +// if(bIsWBkpRHand){ +// if(h->GetRightWielded()) +// h->GetRightWielded()->MoveTo(h->GetStack()); +// h->SetRightWielded(wieldBkp); +// }else{ +// if(h->GetLeftWielded()) +// h->GetLeftWielded()->MoveTo(h->GetStack()); +// h->SetLeftWielded(wieldBkp); +// } +// } +// } h->EditAP(-500); //to let time pass craftcore::CraftSkillAdvance(rpd); //TODO this should be related to collect spider silk to craft one day with it @@ -1555,7 +1557,7 @@ struct srpOltBASE : public recipe{ if(rpd.lsqrPlaceAt->GetOLTerrain()!=NULL){ ADD_MESSAGE("It can't be placed here."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -1753,14 +1755,14 @@ struct srpJoinLumps : public recipe{ if(rpd.ingredientsIDs.empty()){ ADD_MESSAGE("You have no lumps to work with."); - rpd.bAlreadyExplained = true; + rpd.SetAlreadyExplained(); return false; } joinLumpsEqualToFirst(rpd); craftcore::CraftSkillAdvance(rpd); - rpd.bAlreadyExplained = true; + rpd.SetAlreadyExplained(); return true; } @@ -1795,7 +1797,7 @@ struct srpMelt : public srpJoinLumps{ if(LumpMeltable==NULL){ ADD_MESSAGE("Can't melt %s.",Lump->GetName(INDEFINITE).CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -1880,7 +1882,7 @@ struct srpDismantle : public recipe{ //TODO this is instantaneous, should take t ci CI; CI.bAllowDegradation=true; //to let user know what is happening w/o spamming it. if(!choseOneIngredient(rpd,&CI)){ - rpd.bAlreadyExplained=true; //no need to explain if nothing chosen + rpd.SetAlreadyExplained(); //no need to explain if nothing chosen return false; } @@ -1896,25 +1898,25 @@ struct srpDismantle : public recipe{ //TODO this is instantaneous, should take t if(game::IsQuestItem(itToUse)){ ADD_MESSAGE("You feel that would be a bad idea and carefully store it back in your inventory."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } // This is a quick fix, maybe allow dismantling mirrored items into mirrored lumps? if(itToUse->GetLifeExpectancy()) { ADD_MESSAGE("%s is made of temporary magical force rather than physical matter and cannot be dismantled.",itToUse->GetName(DEFINITE).CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } if(dynamic_cast(itToUse)!=NULL || matM==NULL){ //TODO may be there are other things than corpses that also have no main material? ADD_MESSAGE("You should try to split %s instead.",itToUse->GetName(DEFINITE).CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } if(craftcore::IsDegraded(itToUse,true)){ - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -1933,7 +1935,7 @@ struct srpDismantle : public recipe{ //TODO this is instantaneous, should take t /////////////////////// dismantle into lumps if(dynamic_cast(itToUse)!=NULL){ ADD_MESSAGE("%s is already a lump.", itToUse->GetName(DEFINITE).CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -1943,7 +1945,7 @@ struct srpDismantle : public recipe{ //TODO this is instantaneous, should take t * if(dynamic_cast(itToUse)!=NULL){ ADD_MESSAGE("%s is already a stick.", itToUse->GetName(DEFINITE).CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } */ @@ -1957,7 +1959,7 @@ struct srpDismantle : public recipe{ //TODO this is instantaneous, should take t craftcore::EmptyContentsIfPossible(rpd,itToUse,true); ADD_MESSAGE("%s was completely dismantled.", itToUse->GetName(DEFINITE).CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); craftcore::SendToHellSafely(itToUse); DBG3("SentToHell",itToUse->GetID(),itToUse); //TODO if has any magic should release it and also harm @@ -1983,7 +1985,7 @@ struct srpInspect : public recipe{ //TODO this is instantaneous, should take tim virtual bool work(recipedata& rpd){ ci CI; if(!choseOneIngredient(rpd,&CI)){ - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -2005,7 +2007,7 @@ struct srpInspect : public recipe{ //TODO this is instantaneous, should take tim }else{ ADD_MESSAGE("You can't inspect %s.",it0->GetName(INDEFINITE).CStr()); } - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return true; } };srpInspect rpInspect; @@ -2020,18 +2022,18 @@ struct srpResistanceVS : public recipe{ //TODO this is instantaneous, should tak ci CI; CI.iMinMainMaterStr=1; if(!choseOneIngredient(rpd,&CI)){ - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } // yes, a 2nd time if(!choseOneIngredient(rpd,&CI)){ - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } if(rpd.ingredientsIDs.size()!=2){ - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -2065,7 +2067,7 @@ struct srpResistanceVS : public recipe{ //TODO this is instantaneous, should tak itWeaker->ReceiveDamage(rpd.rc.H(), (int)dmg, THROW|PHYSICAL_DAMAGE); //based on item::Fly() "breaks" but not that much } - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); craftcore::CraftSkillAdvance(rpd); return true; @@ -2075,7 +2077,7 @@ struct srpResistanceVS : public recipe{ //TODO this is instantaneous, should tak struct srpSplitLump : public recipe{ void explain(recipedata& rpd,festring fs){ ADD_MESSAGE("You need a cutting tool to split %s.",fs.CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); } bool reqCut(recipedata& rpd,cfestring fs){ @@ -2120,7 +2122,7 @@ struct srpSplitLump : public recipe{ * despite their action is random and not timed, so let it be random here too :) * TODO instead of just lump, also remove head and limbs (if available) so a friendly zombie could attach it? */ - rpd.iMinTurns = 3 + clock()%3; + rpd.iMinTurns = 3 + RAND()%3; rpd.bGradativeCraftOverride=false; // tmp flesh lump @@ -2139,7 +2141,7 @@ struct srpSplitLump : public recipe{ rpd.itSpawnType = CIT_LUMP; - rpd.bAlreadyExplained=true; //no need to say anything + rpd.SetAlreadyExplained(); //no need to say anything } if(ToSplit==NULL && choseOneIngredient(rpd)){ @@ -2163,13 +2165,13 @@ struct srpSplitLump : public recipe{ } if(ToSplit==NULL){ - rpd.bAlreadyExplained=true; //no need to say anything + rpd.SetAlreadyExplained(); //no need to say anything return false; } /* if(craftcore::IsDegraded(ToSplit)){ - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } */ @@ -2184,14 +2186,14 @@ struct srpSplitLump : public recipe{ ToSplit->AddInventoryEntry(rpd.rc.H(),fsInfo,1,true); rpd.itSpawnTot = game::NumberQuestion(festring()+"Split "+fsInfo+" in how many parts? [2 or more]", WHITE, true); if(rpd.itSpawnTot==1 || rpd.itSpawnTot==0){ - rpd.bAlreadyExplained=true; //no need to say anything + rpd.SetAlreadyExplained(); //no need to say anything return false; } if(rpd.itSpawnTot<0){ //single cut mode if(bHumanoidCorpse){ ADD_MESSAGE("This needs to be split first."); //see 'why' about necromancers above... TODO a better message? - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -2200,7 +2202,7 @@ struct srpSplitLump : public recipe{ if(matM==NULL) ABORT("main material is null for %s?",ToSplit->GetName(DEFINITE).CStr()); if((matM->GetVolume() - iCutVol) < 1){ - rpd.bAlreadyExplained=true; //no need to say anything + rpd.SetAlreadyExplained(); //no need to say anything return false; } @@ -2208,14 +2210,14 @@ struct srpSplitLump : public recipe{ //cut->GetMainMaterial()->SetVolume(iCutVol); matM->SetVolume(matM->GetVolume() - iCutVol); ToSplit->CalculateAll(); - rpd.bAlreadyExplained=true; //no need to say anything + rpd.SetAlreadyExplained(); //no need to say anything return true; //TODO should take more turns? } rpd.itSpawnMatMainVol = volTot/rpd.itSpawnTot; if(rpd.itSpawnMatMainVol < 1){ ADD_MESSAGE("The split part must have some volume."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -2241,7 +2243,7 @@ struct srpSplitLump : public recipe{ if(rpd.iBaseTurnsToFinish<1) rpd.iBaseTurnsToFinish=1; - rpd.bAlreadyExplained=true; //no need to say anything + rpd.SetAlreadyExplained(); //no need to say anything rpd.bCanStart = true; @@ -2357,7 +2359,7 @@ struct srpForgeItem : public recipe{ } if(itSpawn==NULL){ - rpd.bAlreadyExplained=true; //actually was just cancelled by user + rpd.SetAlreadyExplained(); //actually was just cancelled by user return false; } @@ -2448,7 +2450,7 @@ struct srpForgeItem : public recipe{ float fPerc = bIsItemContainer ? 1.0 : 0.5; fsM<<(int)(fPerc*100)<<"%)"; /** - * stick shape can't provide enough to the required dimensions (this is a xtremely wild simplification btw :)) + * stick shape can't provide enough to the required dimensions (this is a extremely wild simplification btw :)) * so, this will require twice as much sticks if not a container, to be crafted ex.: 35/0.5=70 */ if(!bMainMatOk){ @@ -2470,7 +2472,7 @@ struct srpForgeItem : public recipe{ if(!bMainMatOk){ ADD_MESSAGE("You don't have the materials to craft a %s.", Default.CStr()); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); craftcore::SendToHellSafely(itSpawn); return false; } @@ -2517,7 +2519,7 @@ struct srpForgeItem : public recipe{ if(!bSecondMatOk){ ADD_MESSAGE("You will craft it later..."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); craftcore::SendToHellSafely(itSpawn); return false; } @@ -2536,7 +2538,7 @@ struct srpForgeItem : public recipe{ CISW.iReqMatCfgMain=SPIDER_SILK; if(!choseIngredients(cfestring("as sewing material"),lVolSewing,rpd,iSCfg,CISW)){ //TODO instead of should be with new graphics ADD_MESSAGE("You don't have enough sewing thread..."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); craftcore::SendToHellSafely(itSpawn); return false; } @@ -2778,7 +2780,7 @@ struct srpFluidsBASE : public recipe{ if(itCorpse==NULL){ ADD_MESSAGE("No useful corpse to work with."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } @@ -2833,12 +2835,12 @@ struct srpFluidsBASE : public recipe{ if(itBottle==NULL){ ADD_MESSAGE("No bottle available."); - rpd.bAlreadyExplained=true; + rpd.SetAlreadyExplained(); return false; } // ready - int iAddVolume = +iAddVolMin +(clock()%iAddVolExtra); + int iAddVolume = +iAddVolMin +(RAND()%iAddVolExtra); int volume = currentVolume + iAddVolume; if(volume > itBottle->GetDefaultSecondaryVolume()) @@ -3033,6 +3035,10 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif if(sel & FELIST_ERROR_BIT) return false; + if(sel==0 && !craftcore::HasSuspended()){ + ADD_MESSAGE("You were doing nothing special."); + return false; + } if(sel==0 && craftcore::HasSuspended()){ int key = game::KeyQuestion(CONST_S("There are suspended crafting actions: (r)esume/ENTER or (c)ancel?"), KEY_ESC, 3, 'r', 'c', KEY_ENTER); @@ -3125,116 +3131,126 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif if(bInitRecipes) return Craft(Char); //init recipes descriptions at least, one time recursion and returns here :> - if(prp==NULL){DBGLN; + if(prp==NULL){ return false; - }DBGLN; + } //ADD_MESSAGE("Your chosen crafting action is to %s %s.",prp->action.CStr(),prp->name.CStr()); bool bDummy = prp->work(rpd); //bDummy(fied) as there is more detailed fail status from rpd bools //TODO these messages are generic, therefore dont look good... improve it - if(rpd.bCanStart){DBGLN; - if(rpd.ingredientsIDs.size()==0) - ABORT("no ingredients chosen?"); - - festring fsTools; - if(rpd.itTool!=NULL) - fsTools=rpd.itTool->GetName(INDEFINITE); - if(rpd.itTool2!=NULL){ - if(!fsTools.IsEmpty()) - fsTools<<" and "; - fsTools<GetName(INDEFINITE); + + if(!rpd.bCanStart){ + if(!rpd.bAlreadyExplained) + ABORT("explain why crafting won't work."); + return true; + } + + if(rpd.ingredientsIDs.size()==0){ + ABORT("no ingredients chosen?"); + return true; // dummy + } + + if(rpd.otSpawnType==CTT_NONE && rpd.itSpawnType==CIT_NONE){ + ABORT("requested to craft nothing? %s",rpd.dbgInfo().CStr()); + return true; //dummy + } + + if(rpd.iBaseTurnsToFinish<1){ + ABORT("invalid iBaseTurnsToFinish %d",rpd.iBaseTurnsToFinish); + return true; //dummy + } + + if(rpd.itTool!=NULL && rpd.itTool2!=NULL){ + if(rpd.itTool==rpd.itTool2){ //keep this check to fix any code bofore this. + ABORT("both tools are the same item %lu:%s %lu:%s",rpd.itTool->GetID(),rpd.itTool->GetName(INDEFINITE).CStr(),rpd.itTool2->GetID(),rpd.itTool2->GetName(INDEFINITE).CStr()); + return true; //dummy } + } + + ///////////////////////// finally all looks ok ////////////////////////////// + + festring fsTools; + if(rpd.itTool!=NULL) + fsTools=rpd.itTool->GetName(INDEFINITE); + if(rpd.itTool2!=NULL){ if(!fsTools.IsEmpty()) - ADD_MESSAGE("You will use %s as a tool.",fsTools.CStr()); - - if(rpd.otSpawnType!=CTT_NONE || rpd.itSpawnType!=CIT_NONE) { - int iCraftTimeMult=1; - - if(!bLOk || !bROk){ //using only one hand will take more time even if only one tool is required as even if 2 were, only 1 would be handled per time - ADD_MESSAGE("You only have one arm, this will take longer."); - iCraftTimeMult++; - } - - if(rpd.itTool !=NULL && rpd.itTool ->IsBroken()){ - ADD_MESSAGE("The first tool is broken, this will take longer."); - iCraftTimeMult++; - } - if(rpd.itTool2!=NULL && rpd.itTool2->IsBroken()){ - ADD_MESSAGE("The second tool is broken, this will take longer."); - iCraftTimeMult++; - } - - if(rpd.iBaseTurnsToFinish<1) - ABORT("invalid iBaseTurnsToFinish %d",rpd.iBaseTurnsToFinish); - - DBGEXEC( //solved, the problem was the duplicate item code that modifies the duplicated ID ... - for(int iDbg123=0;iDbg123=1 || iD>1){ - festring fs; - fs<<"It will take "; - if(iD>1) - fs<GetPos() : rpd.lsqrCharPos->GetPos(); //may be ignored anyway, is just a fallback + fsTools<<" and "; + fsTools<GetName(INDEFINITE); + } + if(!fsTools.IsEmpty()) + ADD_MESSAGE("You will use %s as a tool.",fsTools.CStr()); + + int iCraftTimeMult=1; + + if(!bLOk || !bROk){ //using only one hand will take more time even if only one tool is required as even if 2 were, only 1 would be handled per time + ADD_MESSAGE("You only have one arm, this will take longer."); + iCraftTimeMult++; + } + + if(rpd.itTool !=NULL && rpd.itTool ->IsBroken()){ + ADD_MESSAGE("The first tool is broken, this will take longer."); + iCraftTimeMult++; + } + if(rpd.itTool2!=NULL && rpd.itTool2->IsBroken()){ + ADD_MESSAGE("The second tool is broken, this will take longer."); + iCraftTimeMult++; + } + +// DBGEXEC( //solved, the problem was the duplicate item code that modifies the duplicated ID ... +// for(int iDbg123=0;iDbg123=1 || iD>1){ + festring fs; + fs<<"It will take "; + if(iD>1) + fs<GetPos() : rpd.lsqrCharPos->GetPos(); //may be ignored anyway, is just a fallback - rpd.v2PlayerCraftingAt = Char->GetPos(); + rpd.iAddDexterity=5; //TODO crafting difficult things should give more dexterity (wisdom too?) - if(rpd.itTool!=NULL && rpd.itTool2!=NULL) - if(rpd.itTool==rpd.itTool2) //keep this check to fix any code bofore this. - ABORT("both tools are the same item %lu:%s %lu:%s",rpd.itTool->GetID(),rpd.itTool->GetName(INDEFINITE).CStr(),rpd.itTool2->GetID(),rpd.itTool2->GetName(INDEFINITE).CStr()); - if(rpd.itTool !=NULL)rpd.itToolID =rpd.itTool ->GetID(); - if(rpd.itTool2!=NULL)rpd.itTool2ID=rpd.itTool2->GetID(); + rpd.v2PlayerCraftingAt = Char->GetPos(); - rpd.fsCraftInfo = - prp->action+" "+prp->name+ - (rpd.itSpawnCfg!=0 ? festring(" ("+rpd.fsItemSpawnSearchPrototype+")") : festring())+ - ", started at "+game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex(), true); + if(rpd.itTool !=NULL)rpd.itToolID =rpd.itTool ->GetID(); + if(rpd.itTool2!=NULL)rpd.itTool2ID=rpd.itTool2->GetID(); - rpd.ClearRefs(); //pointers must be revalidated on the action handler - DBG1(rpd.dbgInfo().CStr()); - Char->SwitchToCraft(rpd); // everything must be set before this!!! + rpd.fsCraftInfo = + prp->action+" "+prp->name+ + (rpd.itSpawnCfg!=0 ? festring(" ("+rpd.fsItemSpawnSearchPrototype+")") : festring())+ + ", started at "+game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex(), true); - ADD_MESSAGE("You will work on %s now.",prp->name.CStr()); - }else{ - ABORT("requested to craft nothing? %s",rpd.dbgInfo().CStr()); - } - - return true; //spends current turn - }else{ - if(!rpd.bAlreadyExplained) - ABORT("explain why crafting won't work."); - } + rpd.ClearRefs(); //pointers must be revalidated on the action handler + DBG1(rpd.dbgInfo().CStr()); + if(Char->SwitchToCraft(rpd)) // everything must be set before this!!! + ADD_MESSAGE("You will work on %s now.",prp->name.CStr()); /** * ATTENTION!!! @@ -3244,7 +3260,7 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif * is the SAFEST thing! dont change this please even if you are sure all looks perfect! ;) if(rpd.bSpendCurrentTurn)return true;else return false; //old code */ - return true; + return true; //spends current turn } /** @@ -3422,7 +3438,7 @@ void crafthandle::CraftWorkTurn(recipedata& rpd){ DBG1(rpd.iRemainingTurnsToFini rpd.iRemainingTurnsToFinish--; rpd.bSuccesfullyCompleted = rpd.iRemainingTurnsToFinish==0; - if(clock()%2==0 ? rpd.iRemainingTurnsToFinish%3==0 : rpd.iRemainingTurnsToFinish%5==0){ // to avoid unnecessarily spamming hiteffects + if(RAND()%2==0 ? rpd.iRemainingTurnsToFinish%3==0 : rpd.iRemainingTurnsToFinish%5==0){ // to avoid unnecessarily spamming hiteffects // keep this preference order! lsquare* lsqrHF=NULL; if(!lsqrHF)lsqrHF=rpd.lsqrPlaceAt; @@ -3581,11 +3597,11 @@ bool craftcore::CheckFumble(recipedata& rpd, bool& bCriticalFumble,int& iFumbleP * To fumble, base reference is 15% chance at a craft skill of 20. * ex.: Craft skill of 10 will have 30% fumble chance. */ - int iLuckPerc=clock()%100; + int iLuckPerc=RAND()%100; float fBaseCraftSkillToNormalFumble=20.0*rpd.fDifficulty; static const int iBaseFumbleChancePerc=15; int iFumbleBase=iBaseFumbleChancePerc/(craftcore::CraftSkill(rpd.rc.H())/fBaseCraftSkillToNormalFumble); //ex.: 30% - if(iFumbleBase>99)iFumbleBase=99; //%1 granted luck + if(iFumbleBase>98)iFumbleBase=98; //%1 granted luck as it is 0-99 int iDiv=0; iDiv=1;if(iFumbleBase>iDiv && iLuckPerc<=iFumbleBase/iDiv)iFumblePower++; //ex.: <=30% iDiv=2;if(iFumbleBase>iDiv && iLuckPerc<=iFumbleBase/iDiv)iFumblePower++; //ex.: <=15% @@ -3596,7 +3612,9 @@ bool craftcore::CheckFumble(recipedata& rpd, bool& bCriticalFumble,int& iFumbleP bCriticalFumble=true; } //current max chance per round of spawning broken is 5% - if(clock()%100<=iFumblePower) + int iTry=RAND()%100; + DBG5(iTry,iFumbleBase,iFumblePower,fBaseCraftSkillToNormalFumble,iLuckPerc); + if(iTry<=iFumblePower) return true; return false; @@ -3609,7 +3627,7 @@ void crafthandle::CheckFumble(recipedata& rpd,bool bChangeTurns) if(rpd.fDifficulty>1.0){ int xplodXtra=0; for(int i=0;i0){DBG2(rpd.xplodStr,rpd.dbgInfo().CStr()); - rpd.xplodStr+=clock()%5+xplodXtra; //reference: weak lantern xplod str is 5 + rpd.xplodStr+=RAND()%5+xplodXtra; //reference: weak lantern xplod str is 5 //TODO anvil should always be near the forge. Anvil have no sparks. Keeping messages like that til related code is improved } } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 7b09489df..b557529ed 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -5916,7 +5916,7 @@ festring game::ToCharIfPossible(int i) return "PgUp"; case KEY_DELETE: return "Del"; - case 0x3FFF2049: //KEY_INSERT: //TODO everykeyboard generates this huge value??? + case KEY_INSERT: return "Ins"; } diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index c50562692..9ae93962b 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -1884,51 +1884,61 @@ void humanoid::SetEquipment(int I, item* What) } } -void humanoid::SwitchToCraft(recipedata rpd) +truth humanoid::SwitchToCraft(recipedata rpd) {DBGLN; craft* Craft = craft::Spawn(this);DBGLN; - - if(rpd.GetTool()!=NULL){DBGLN; - if(GetRightArm()) - {DBGLN; - item* Item = GetRightArm()->GetWielded(); - - if(Item && Item != rpd.GetTool()) - { - Craft->SetRightBackupID(GetRightArm()->GetWielded()->GetID()); - GetRightArm()->GetWielded()->MoveTo(GetStack()); - } + + bool b1OK=false; + bool b2OK=false; + item* it; + if(rpd.GetTool()!=NULL){ + if( + (GetRightArm() && GetRightArm()->IsUsable() && GetRightWielded() == rpd.GetTool()) || + (GetLeftArm() && GetLeftArm()->IsUsable() && GetLeftWielded() == rpd.GetTool()) + ){ + b1OK=true; + Craft->SetMoveCraftTool(false); } + + if(!b1OK && GetRightArm() && GetRightArm()->IsUsable()){ + if((it = GetRightWielded())){ + Craft->SetRightBackupID(it->GetID()); + it->MoveTo(GetStack()); + } - if(GetLeftArm()) - {DBGLN; - item* Item = GetLeftArm()->GetWielded(); + rpd.GetTool()->RemoveFromSlot(); + SetRightWielded(rpd.GetTool()); - if(Item && Item != rpd.GetTool()) - { - Craft->SetLeftBackupID(GetLeftArm()->GetWielded()->GetID()); - GetLeftArm()->GetWielded()->MoveTo(GetStack()); - } + b1OK=true; + Craft->SetMoveCraftTool(true); } + + if(!b1OK && GetLeftArm() && GetLeftArm()->IsUsable()){ + if((it = GetLeftWielded())){ + Craft->SetLeftBackupID(it->GetID()); + it->MoveTo(GetStack()); + } - if(GetMainWielded() != rpd.GetTool()) - {DBGLN; - Craft->SetMoveCraftTool(true); rpd.GetTool()->RemoveFromSlot(); + SetLeftWielded(rpd.GetTool()); - if(GetMainArm() && GetMainArm()->IsUsable()) - GetMainArm()->SetWielded(rpd.GetTool()); - else - GetSecondaryArm()->SetWielded(rpd.GetTool()); + b1OK=true; + Craft->SetMoveCraftTool(true); } - else - Craft->SetMoveCraftTool(false); - }DBGLN; - - //TODO let the GetTool2() be equipped too + + } + + //TODO let the GetTool2() be equipped too? - Craft->SetCraftWhat(rpd);DBGLN; - SetAction(Craft);DBGLN; + if(b1OK){ + Craft->SetCraftWhat(rpd);DBGLN; + SetAction(Craft);DBGLN; + return true; + } + + ADD_MESSAGE("You have no usable arm."); + rpd.SetAlreadyExplained(); + return false; } void humanoid::SwitchToDig(item* DigItem, v2 Square) From d4329d8c73186322918e7bcbcccd901bc1d37f19 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 19 Apr 2020 01:45:22 -0300 Subject: [PATCH 136/235] crafting: fixed fumbling; --- Main/Source/cmdcraft.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 1161abf33..6f0579409 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -1437,7 +1437,7 @@ struct srpCutWeb : public recipe{ bool bGetStuckOnTheWeb=false; bool bLoseWeapon=false; //TODO if has no weapon, lose one glove instead! bool bCriticalFumble=false; - int iFumblePower=0; + int iFumblePower=10; if(craftcore::CheckFumble(rpd, bCriticalFumble, iFumblePower)){DBGLN; if(bCriticalFumble){DBGLN; bGetStuckOnTheWeb=true; @@ -3630,7 +3630,7 @@ void crafthandle::CheckFumble(recipedata& rpd,bool bChangeTurns) xplodXtra+=RAND()%5; bool bCriticalFumble=false; - int iFumblePower=0; + int iFumblePower=5; if(craftcore::CheckFumble(rpd,bCriticalFumble,iFumblePower)){ if(!rpd.bSpawnBroken && bChangeTurns){ rpd.iBaseTurnsToFinish/=2; //just complete the broken item (as AV gets halved) TODO repair system From 0cb782bf22f3eda6b50e28509c6334b573c1f802 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 19 Apr 2020 02:02:40 -0300 Subject: [PATCH 137/235] fumble while cutting a web may make you lose one gauntlet; --- Main/Source/cmdcraft.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 6f0579409..1a28734e2 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -1435,19 +1435,29 @@ struct srpCutWeb : public recipe{ craftcore::FinishSpawning(rpd, craftcore::PrepareRemains(rpd,matSSilk,CIT_LUMP,RAND()%6+3)); }else{ bool bGetStuckOnTheWeb=false; - bool bLoseWeapon=false; //TODO if has no weapon, lose one glove instead! + bool bLoseWeapon=false; + bool bLoseGlove=false; bool bCriticalFumble=false; int iFumblePower=10; + item* itGlove = ra?ra->GetGauntlet():NULL; + if(!itGlove)itGlove = la?la->GetGauntlet():NULL; if(craftcore::CheckFumble(rpd, bCriticalFumble, iFumblePower)){DBGLN; if(bCriticalFumble){DBGLN; bGetStuckOnTheWeb=true; - if(rpd.itTool!=NULL) + if(rpd.itTool){ if(RAND()%100 < 50){DBGLN; bLoseWeapon=true; } + }else if(itGlove){ + if(RAND()%100 < 50){DBGLN; + bLoseGlove=true; + } + } }else{DBGLN; - if(rpd.itTool!=NULL){DBGLN; + if(rpd.itTool){DBGLN; bLoseWeapon=true; + }else if(itGlove){ + bLoseGlove=true; }else{DBGLN; bGetStuckOnTheWeb=true; } @@ -1462,8 +1472,10 @@ struct srpCutWeb : public recipe{ ADD_MESSAGE("You've got stuck on the web!"); rpd.SetAlreadyExplained(); }else{ - if(rpd.itTool!=NULL){ + if(rpd.itTool){ bLoseWeapon=true; + }else if(itGlove){ + bLoseGlove=true; } } } @@ -1475,6 +1487,13 @@ struct srpCutWeb : public recipe{ rpd.SetAlreadyExplained(); } + if(bLoseGlove){ + itGlove->RemoveFromSlot(); + itGlove->MoveTo(rpd.lsqrPlaceAt->GetStack()); + ADD_MESSAGE("You lost your %s!",itGlove->GetName(UNARTICLED).CStr()); + rpd.SetAlreadyExplained(); + } + if(bSelfPos && !bGetStuckOnTheWeb && !bLoseWeapon){ w->StepOnEffect(h); //so every try will make it more difficult!! :) } From 716d6e5b89b943fa463032793c9d48faa7cb5b5e Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 19 Apr 2020 02:36:46 -0300 Subject: [PATCH 138/235] crafting: got broken, fixed; --- Main/Include/craft.h | 4 ++-- Main/Source/cmdcraft.cpp | 10 ++++++++++ Main/Source/human.cpp | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index ae36040c2..dadfb590e 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -231,8 +231,8 @@ class recipedata { void CopySpawnTerrainCfgFrom(olterrain* otCfg); void ClearRefs(); - item* GetTool(){return itTool;} - item* GetTool2(){return itTool2;} + item* GetTool(); + item* GetTool2(); bool IsFailedSuspendOrCancel(){return bFailedTerminateCancel || bFailedSuspend;} void SetAlreadyExplained(){bAlreadyExplained=true;} diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 1a28734e2..2214fb6f4 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -3282,6 +3282,16 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif return true; //spends current turn } +item* recipedata::GetTool() +{ + return !itTool && itToolID ? game::SearchItem(itToolID) : NULL; +} + +item* recipedata::GetTool2() +{ + return !itTool2 && itTool2ID ? game::SearchItem(itTool2ID) : NULL; +} + /** * * @param bAllowBreak diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 9ae93962b..f46fd452b 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -1887,11 +1887,12 @@ void humanoid::SetEquipment(int I, item* What) truth humanoid::SwitchToCraft(recipedata rpd) {DBGLN; craft* Craft = craft::Spawn(this);DBGLN; + DBG4(rpd.GetTool(),rpd.GetTool2(),GetRightArm()?GetRightArm()->IsUsable():0,GetLeftArm()?GetLeftArm()->IsUsable():0); bool b1OK=false; bool b2OK=false; item* it; - if(rpd.GetTool()!=NULL){ + if(rpd.GetTool()){ if( (GetRightArm() && GetRightArm()->IsUsable() && GetRightWielded() == rpd.GetTool()) || (GetLeftArm() && GetLeftArm()->IsUsable() && GetLeftWielded() == rpd.GetTool()) @@ -1926,6 +1927,8 @@ truth humanoid::SwitchToCraft(recipedata rpd) Craft->SetMoveCraftTool(true); } + }else{ + b1OK=true; //can craft somethings w/o tools } //TODO let the GetTool2() be equipped too? From 7dcadf798e9483d74e52df702315882f30fab6f5 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 19 Apr 2020 03:08:35 -0300 Subject: [PATCH 139/235] fixed sewing requirements to only be requested for tailorable materials; --- Main/Source/cmdcraft.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 2214fb6f4..a8e90449c 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2439,9 +2439,11 @@ struct srpForgeItem : public recipe{ ci CI = CIM; CI.fUsablePercVol=fPerc; - CI.bMustBeTailorable = rpd.bTailoringMode = true; + CI.bMustBeTailorable = true; CI.bMixRemainingLump = false; bMainMatOk = choseIngredients(fsM,lVolM, rpd, iCfgM, CI); //TODO instead of should be a new item with new graphics called + if(bMainMatOk) + rpd.bTailoringMode=true; } } if(!bMustTailor){ From ec2b455a9c0aebbe5dae227392f9d5db2002f1e9 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 19 Apr 2020 22:29:22 -0300 Subject: [PATCH 140/235] Unified SaveLife() base/common code (with improved healing from burning, felt like it was missing...); WIP-craft(scratch): preparing nails for better wood crafting; Added some gimp gfx files to easify work on them, are at my dev folder. --- .../AquariusPower/Graphics.WorkFiles/Item.xcf | Bin 0 -> 43847 bytes .../Graphics.WorkFiles/OLTerra.xcf | Bin 0 -> 50036 bytes Graphics/Item.png | Bin 12466 -> 12467 bytes Main/Include/char.h | 1 + Main/Include/miscitem.h | 18 ++--- Main/Source/char.cpp | 64 +++++++++--------- Main/Source/miscitem.cpp | 5 +- Main/Source/nonhuman.cpp | 18 +---- Script/item.dat | 27 ++++++++ 9 files changed, 75 insertions(+), 58 deletions(-) create mode 100644 .devsPrefs/AquariusPower/Graphics.WorkFiles/Item.xcf create mode 100644 .devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf diff --git a/.devsPrefs/AquariusPower/Graphics.WorkFiles/Item.xcf b/.devsPrefs/AquariusPower/Graphics.WorkFiles/Item.xcf new file mode 100644 index 0000000000000000000000000000000000000000..a4b669fb383e890ad6dae03af048a4c0b7c6c940 GIT binary patch literal 43847 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 literal 0 HcmV?d00001 diff --git a/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf b/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf new file mode 100644 index 0000000000000000000000000000000000000000..46509975eb1363ffcf14824806cced3bb6124876 GIT binary patch literal 50036 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 E9~B{9g8%>k literal 0 HcmV?d00001 diff --git a/Graphics/Item.png b/Graphics/Item.png index 3451b7934e3c3946e5bca9e187687033e01b2cc7..55202f613f0a4fb3611f6a17409ea1199449799c 100644 GIT binary patch delta 9600 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 Yr0<%4z*lENHt^3(3@r4^^srI?1MCAG$p8QV delta 9597 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 0; } -// protected: -// virtual truth WeightIsIrrelevant() const { return true; } -//}; +/* +ITEM(nail, item) +{ + public: + virtual long GetTruePrice() const; + virtual truth IsLuxuryItem(ccharacter*) const { return GetTruePrice() > 0; } + protected: + virtual truth WeightIsIrrelevant() const { return true; } +}; +*/ ITEM(scrolloftaming, scroll) { diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index e2b20b966..44c332d54 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1734,20 +1734,11 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) if(!bInstaResurrect && !game::TruthQuestion(CONST_S("Do you want to do this, cheater? [y/n]"), REQUIRES_ANSWER))bInstaResurrect=true; if(bInstaResurrect) { - RestoreBodyParts(); - ResetSpoiling(); - if(IsBurning()) - { - doforbodypartswithparam()(this, &bodypart::Extinguish, false); - doforbodyparts()(this, &bodypart::ResetThermalEnergies); - doforbodyparts()(this, &bodypart::ResetBurning); - } - RestoreHP(); - RestoreStamina(); - ResetStates(); - SetNP(SATIATED_LEVEL); - SendNewDrawRequest(); - if(IsPlayerAutoPlay())AutoPlayAITeleport(true); + SaveLifeBase(); + + if(IsPlayerAutoPlay()) + AutoPlayAITeleport(true); + return; } } @@ -6914,6 +6905,32 @@ void character::LycanthropyHandler() } } +void character::SaveLifeBase() +{ + if(IsPlayer() && !IsPlayerAutoPlay()) + game::AskForKeyPress(CONST_S("Life saved! [press any key to continue]")); + + RestoreBodyParts(); + ResetSpoiling(); + if(IsBurning()) + { + doforbodypartswithparam()(this, &bodypart::Extinguish, false); + doforbodyparts()(this, &bodypart::ResetThermalEnergies); + doforbodyparts()(this, &bodypart::ResetBurning); + } + RestoreHP(); + RestoreStamina(); + ResetStates(); + + if(GetNP() < SATIATED_LEVEL) + SetNP(SATIATED_LEVEL); + + SendNewDrawRequest(); + + if(GetAction()) + GetAction()->Terminate(false); +} + void character::SaveLife() { if(TemporaryStateIsActivated(LIFE_SAVED)) @@ -6951,23 +6968,8 @@ void character::SaveLife() LifeSaver->RemoveFromSlot(); LifeSaver->SendToHell(); } - - if(IsPlayer()) - game::AskForKeyPress(CONST_S("Life saved! [press any key to continue]")); - - RestoreBodyParts(); - ResetSpoiling(); - RestoreHP(); - RestoreStamina(); - ResetStates(); - - if(GetNP() < SATIATED_LEVEL) - SetNP(SATIATED_LEVEL); - - SendNewDrawRequest(); - - if(GetAction()) - GetAction()->Terminate(false); + + SaveLifeBase(); } character* character::PolymorphRandomly(int MinDanger, int MaxDanger, int Time) diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index eaa9387b7..bfab84a42 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -61,8 +61,9 @@ long nuke::GetTotalExplosivePower() const { return GetSecondaryMaterial() ? GetSecondaryMaterial()->GetTotalExplosivePower() : 0; } long stone::GetTruePrice() const { return item::GetTruePrice() << 1; } - -//long ingot::GetTruePrice() const { return item::GetTruePrice() << 1; } +/* +long nail::GetTruePrice() const { return item::GetTruePrice() << 1; } +*/ col16 whistle::GetMaterialColorB(int) const { return MakeRGB16(80, 32, 16); } diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp index 98450b755..135246336 100644 --- a/Main/Source/nonhuman.cpp +++ b/Main/Source/nonhuman.cpp @@ -2535,23 +2535,7 @@ truth largecat::SpecialSaveLife() if(!IsPlayer() && CanBeSeenByPlayer()) ADD_MESSAGE("%s appears!", CHAR_NAME(INDEFINITE)); - if(IsPlayer()) - game::AskForKeyPress(CONST_S("Life saved! [press any key to continue]")); - - RestoreBodyParts(); - ResetSpoiling(); - ResetBurning(); - RestoreHP(); - RestoreStamina(); - ResetStates(); - - if(GetNP() < SATIATED_LEVEL) - SetNP(SATIATED_LEVEL); - - SendNewDrawRequest(); - - if(GetAction()) - GetAction()->Terminate(false); + SaveLifeBase(); return true; } diff --git a/Script/item.dat b/Script/item.dat index 196cf6adf..f111f1815 100644 --- a/Script/item.dat +++ b/Script/item.dat @@ -7971,3 +7971,30 @@ pica EnchantmentPlusChance = 15; } } + +/* nail was initially based on stone and ingot data */ +/* +nail +{ + DefaultSize = 5; + Possibility = 100; + Category = VALUABLE; + BitmapPos = 128, 32; + NameSingular = "nail"; + IsValuable = true; + MainMaterialConfig = { 35, + VALPURIUM, BRASS, HEPATIZON, OCTIRON, ORICHALCUM, SILVER, + MOON_SILVER, FAIRY_STEEL, MITHRIL, GALVORN, STAR_METAL, + PLATINUM, DARK_GOLD, ELECTRUM, IRIDIUM, PALLADIUM, CHROME, + TITANITE, STAINLESS_STEEL, PLASTIC_STEEL, DURALLOY, + VITRELLOY, URANIUM, SOLARIUM, NEUTRONIUM, ARCANITE, + OCCULTUM, ILLITHIUM, METEORIC_STEEL, UKKU_STEEL, ADAMANT, + SOUL_STEEL, BLOOD_STEEL, WHITE_STEEL, UR_STEEL; } + MaterialConfigChances = { 35, + 1, 200, 75, 25, 5, 300, 250, 100, 75, 25, 5, 50, 50, 50, + 30, 20, 10, 5, 20, 15, 10, 5, 15, 10, 5, 100, 15, 150, + 150, 75, 5, 200, 200, 200, 75; } + Alias == "metal"; + DescriptiveInfo = "Useful in wood crafting."; +} +*/ From 21c43e13dff87b0afeed72c0b8a9b7bbd0a532e0 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 02:11:12 -0300 Subject: [PATCH 141/235] New pre-processor definition: CURSEDDEVELOPER. The related code will only be compiled if CURSEDDEVELOPER is defined. It will only work if environment variable IVAN_CURSEDDEVELOPER=true. This lets the non wizard gameplay be tested by developers. It will prevent player's death and scoring. It will show before player's name "[Cursed Developer!]". It will increase the buffs at player's killer every time it succeeds. --- .../nbproject/configurations.xml | 1 + Main/Include/char.h | 1 + Main/Source/char.cpp | 132 ++++++++++++++---- Main/Source/main.cpp | 7 +- 4 files changed, 113 insertions(+), 28 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index d86d10355..653d045b5 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -182,6 +182,7 @@ /usr/include + CURSEDDEVELOPER DBGMSG FELIST_WAITKEYUP FIX_LARGECREATURE_TELEPORT_GLITCH diff --git a/Main/Include/char.h b/Main/Include/char.h index 066f9b2c7..55c4a28dd 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -364,6 +364,7 @@ class character : public entity, public id void ActivateEquipmentState(long What) { EquipmentState |= What; } void DeActivateEquipmentState(long What) { EquipmentState &= ~What; } truth TemporaryStateIsActivated(long What) const; + truth HasStateFlag(long Flag); truth EquipmentStateIsActivated(long What) const { return EquipmentState & What; } truth StateIsActivated(long What) const; truth LoseConsciousness(int, truth = false); diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 44c332d54..7f1dc0c33 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1708,9 +1708,62 @@ void character::AutoPlayAITeleport(bool bDeathCountBased) } if(bTeleportNow) - Move(GetLevel()->GetRandomSquare(this), true); //not using teleport function to avoid prompts, but this code is from there TODO and should be in sync! create TeleportRandomDirectly() ? + Move(GetLevel()->GetRandomSquare(this), true); //not using teleport function to avoid prompts, but this code is from there } +/** + * This is a developer environment variable to test the game without wizard mode. + */ +#ifdef CURSEDDEVELOPER +bool bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); +#else +bool bCursedDeveloper = false; +#endif + +#ifdef CURSEDDEVELOPER +/** + * this will make the NPC that kills the player more challenging for every kill + * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? + */ +void BuffPlayerKiller(character* Killer) +{ + if(!bCursedDeveloper)return; + + if(!Killer)return; + + if(!Killer->HasStateFlag(ESP)){ + Killer->GainIntrinsic(ESP); + Killer->GainIntrinsic(INFRA_VISION); + Killer->GainIntrinsic(FASTING); + Killer->GainIntrinsic(SEARCHING); + Killer->GainIntrinsic(POLYMORPH_LOCK); + Killer->GainIntrinsic(TELEPORT_LOCK); + return; + } + + if(!Killer->HasStateFlag(GAS_IMMUNITY)){ + Killer->GainIntrinsic(GAS_IMMUNITY); + Killer->GainIntrinsic(VAMPIRISM); + Killer->GainIntrinsic(DISEASE_IMMUNITY); + return; + } + + if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return;} + + if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return;} + + if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return;} + + if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return;} + + if(!Killer->HasStateFlag(ETHEREAL_MOVING)){Killer->GainIntrinsic(ETHEREAL_MOVING);return;} + + if(!Killer->HasStateFlag(REGENERATION)){Killer->GainIntrinsic(REGENERATION);return;} + + if(!Killer->HasStateFlag(LEVITATION)){Killer->GainIntrinsic(LEVITATION);return;} +} +#endif + void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) { /* Note: This function musn't delete any objects, since one of these may be @@ -1724,7 +1777,21 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) if(IsPlayer()) { ADD_MESSAGE("You die."); - + +#ifdef CURSEDDEVELOPER // so that this code wont be compiled to non developer players + if(bCursedDeveloper){ + game::DrawEverything(); + + HP=1; //only enough to continue testing normal gameplay + BuffPlayerKiller((character*)Killer); //to spice it up somewhat + if(GetAction())GetAction()->Terminate(false); //just to avoid messing any action + if(!game::IsInWilderness())Move(GetLevel()->GetRandomSquare(this), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn + + ADD_MESSAGE("But no! You are a cursed developer! You are forbidden to rest! And your doings will be forever forgotten..."); + return; + } +#endif + if(game::WizardModeIsActive()) { game::DrawEverything(); @@ -2536,36 +2603,38 @@ truth character::TestForPickup(item* ToBeTested) const void character::AddScoreEntry(cfestring& Description, double Multiplier, truth AddEndLevel) const { - if(!game::WizardModeIsReallyActive()) - { - highscore HScore(GetUserDataDir() + HIGH_SCORE_FILENAME); - - if(!HScore.CheckVersion()) - { - if(game::Menu(0, v2(RES.X >> 1, RES.Y >> 1), - CONST_S("The highscore version doesn't match.\rDo you want to erase " - "previous records and start a new file?\rNote, if you answer " - "no, the score of your current game will be lost!\r"), - CONST_S("Yes\rNo\r"), LIGHT_GRAY)) - return; + if(game::WizardModeIsReallyActive()) + return; + if(bCursedDeveloper) + return; + + highscore HScore(GetUserDataDir() + HIGH_SCORE_FILENAME); - HScore.Clear(); - } + if(!HScore.CheckVersion()) + { + if(game::Menu(0, v2(RES.X >> 1, RES.Y >> 1), + CONST_S("The highscore version doesn't match.\rDo you want to erase " + "previous records and start a new file?\rNote, if you answer " + "no, the score of your current game will be lost!\r"), + CONST_S("Yes\rNo\r"), LIGHT_GRAY)) + return; - festring Desc = game::GetPlayerName(); - Desc << ", " << Description; + HScore.Clear(); + } - if(AddEndLevel) - { - if(game::IsInWilderness()) - Desc << " in the wilderness"; - else - Desc << " in " << game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex()); - } + festring Desc = game::GetPlayerName(); + Desc << ", " << Description; - HScore.Add(long(game::GetScore() * Multiplier), Desc); - HScore.Save(); + if(AddEndLevel) + { + if(game::IsInWilderness()) + Desc << " in the wilderness"; + else + Desc << " in " << game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex()); } + + HScore.Add(long(game::GetScore() * Multiplier), Desc); + HScore.Save(); } truth character::CheckDeath(cfestring& Msg, ccharacter* Murderer, ulong DeathFlags) @@ -9106,6 +9175,10 @@ festring character::GetPanelName() const festring PanelName; if(!game::IsInWilderness()){ +#ifdef CURSEDDEVELOPER + if(bCursedDeveloper) + PanelName << "[Cursed Developer!] "; +#endif PanelName << Name; if(ivanconfig::IsShowFullDungeonName()){ PanelName << " (at " << game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex(), true) << ')'; @@ -13131,6 +13204,11 @@ truth character::IsESPBlockedByEquipment() const return false; } +truth character::HasStateFlag(long Flag) +{ + return TemporaryState & Flag; +} + truth character::TemporaryStateIsActivated (long What) const {DBG7(this,GetNameSingular().CStr(),TemporaryState&What,TemporaryState,std::bitset<32>(TemporaryState),What,std::bitset<32>(What)); if((What&PANIC) && (TemporaryState&PANIC) && StateIsActivated(FEARLESS)) diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index 6ea753fa4..79652e0d8 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -141,7 +141,12 @@ int main(int argc, char** argv) std::cout << "IVAN_LISTDRAWABOVE=[true] #DEBUG output the draw above priority list to text console terminal" << std::endl; std::cout << "IVAN_DebugGenDungeonLevelLoopID=[DungeonLevelIndex] #DEBUG DungeonLevelIndex must be an integer matching a some dungeon level" << std::endl; std::cout << "IVAN_DebugGenDungeonLevelLoopMax=[integer] #DEBUG generate the dungeon level how many times" << std::endl; - std::cout << "IVAN_DebugStayOnDungeonLevel=[DungeonLevelIndex] #DEBUG auto play AI will not leave that Dungeon Level after entering it" << std::endl; +#ifdef WIZARD + std::cout << "IVAN_DebugStayOnDungeonLevel=[DungeonLevelIndex] #DEBUG wizard auto play AI will not leave that Dungeon Level after entering it" << std::endl; +#endif +#ifdef CURSEDDEVELOPER + std::cout << "IVAN_CURSEDDEVELOPER=[true] #DEBUG (only for developer tests) this will prevent player's death (and scoring) in normal (non wizard) gameplay" << std::endl; +#endif return 0; } From 0c3af2cb6a5d0c56ab25762820ca612bf00da92a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 02:34:16 -0300 Subject: [PATCH 142/235] fixed a crash in case no favour is selectable (only happens in non wizard mode). --- Main/Source/command.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 414a127f5..462212a2e 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1207,7 +1207,8 @@ truth commandsystem::AskFavour(character* Char) int Select = LIST_WAS_EMPTY; if(iTot>0){ game::SetStandardListAttributes(felFavourList); - felFavourList.AddFlags(SELECTABLE); + if(vSelectableFavours.size()>0) + felFavourList.AddFlags(SELECTABLE); Select = felFavourList.Draw(); } From a00ebdf1b2848ecbad4e65338ebff276a71318fa Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 03:29:34 -0300 Subject: [PATCH 143/235] CURSEDDEVELOPER mode: making it usable; --- Main/Source/char.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 7f1dc0c33..44bd6d37b 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1783,11 +1783,21 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) game::DrawEverything(); HP=1; //only enough to continue testing normal gameplay - BuffPlayerKiller((character*)Killer); //to spice it up somewhat - if(GetAction())GetAction()->Terminate(false); //just to avoid messing any action - if(!game::IsInWilderness())Move(GetLevel()->GetRandomSquare(this), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn + BuffPlayerKiller((character*)Killer); //to spice it up + for(int c = 0; c < BodyParts; ++c){ //to be able to do something at least + if(!GetBodyPart(c) && CanCreateBodyPart(c)){ + CreateBodyPart(c); + GetBodyPart(c)->SetHP(1); + } + } + if(GetNP() < HUNGER_LEVEL) + SetNP(HUNGER_LEVEL); //to avoid endless sleeping + if(GetAction()) + GetAction()->Terminate(false); //just to avoid messing any action + if(Killer && !game::IsInWilderness()) + Move(GetLevel()->GetRandomSquare(this), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn - ADD_MESSAGE("But no! You are a cursed developer! You are forbidden to rest! And your doings will be forever forgotten..."); + ADD_MESSAGE("But no! You are cursed! You are forbidden to rest! And your doings will be forever forgotten..."); return; } #endif From b8b6b1817cde2a3094c5f26e66ee2fbb90a2c383 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 13:11:13 -0300 Subject: [PATCH 144/235] Auto mapnote over existing engrave will prepend it. WIP-CURSEDDEVELOPER: improving usability; --- Main/Source/char.cpp | 103 ++++++++++++++++++++++++++++++------------- Main/Source/game.cpp | 4 ++ 2 files changed, 77 insertions(+), 30 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 44bd6d37b..1bc29fcc7 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1724,43 +1724,80 @@ bool bCursedDeveloper = false; /** * this will make the NPC that kills the player more challenging for every kill * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? + * @return if player should stay (true) or teleport (false) */ -void BuffPlayerKiller(character* Killer) +truth BuffAndDebuffPlayerKiller(character* Killer) { - if(!bCursedDeveloper)return; + if(!bCursedDeveloper)return true; + if(!Killer)return true; - if(!Killer)return; + // BUFFs, every death makes it harder to player: + if(!Killer->HasStateFlag(ESP)){Killer->GainIntrinsic(ESP);return false;} + + if(!Killer->HasStateFlag(INFRA_VISION)){Killer->GainIntrinsic(INFRA_VISION);return false;} + + if(!Killer->HasStateFlag(VAMPIRISM)){Killer->GainIntrinsic(VAMPIRISM);return false;} - if(!Killer->HasStateFlag(ESP)){ - Killer->GainIntrinsic(ESP); - Killer->GainIntrinsic(INFRA_VISION); - Killer->GainIntrinsic(FASTING); - Killer->GainIntrinsic(SEARCHING); - Killer->GainIntrinsic(POLYMORPH_LOCK); - Killer->GainIntrinsic(TELEPORT_LOCK); - return; + if(!Killer->HasStateFlag(PANIC)) + if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return false;} + + if(!Killer->HasStateFlag(SLOW)) + if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return false;} + + if(!Killer->HasStateFlag(HICCUPS)) + if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return false;} + + if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return false;} + + if(!Killer->HasStateFlag(ETHEREAL_MOVING)){Killer->GainIntrinsic(ETHEREAL_MOVING);return false;} + + if(!Killer->HasStateFlag(REGENERATION)){Killer->GainIntrinsic(REGENERATION);return false;} + + if(!Killer->HasStateFlag(LEVITATION)){Killer->GainIntrinsic(LEVITATION);return false;} + + if(!Killer->HasStateFlag(GAS_IMMUNITY)){Killer->GainIntrinsic(GAS_IMMUNITY);return false;} + + if(!Killer->HasStateFlag(TELEPORT_LOCK)){Killer->GainIntrinsic(TELEPORT_LOCK);return false;} + + if(!Killer->HasStateFlag(POLYMORPH_LOCK)){Killer->GainIntrinsic(POLYMORPH_LOCK);return false;} + + // DEBUFFs, after player has taken too much it is time to make it stop, but slowly: + if(!Killer->HasStateFlag(HICCUPS)){ + Killer->DeActivateTemporaryState(INVISIBLE); + Killer->GainIntrinsic(HICCUPS); + return false; } - if(!Killer->HasStateFlag(GAS_IMMUNITY)){ - Killer->GainIntrinsic(GAS_IMMUNITY); - Killer->GainIntrinsic(VAMPIRISM); - Killer->GainIntrinsic(DISEASE_IMMUNITY); - return; + if(!Killer->HasStateFlag(SLOW)){ + Killer->DeActivateTemporaryState(HASTE); + Killer->GainIntrinsic(SLOW); + return false; } - if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return;} + if(!Killer->HasStateFlag(PARASITE_TAPE_WORM)){Killer->GainIntrinsic(PARASITE_TAPE_WORM);return false;} - if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return;} - - if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return;} + if(!Killer->HasStateFlag(CONFUSED)){Killer->GainIntrinsic(CONFUSED);return false;} - if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return;} + if(!Killer->HasStateFlag(LEPROSY)){Killer->GainIntrinsic(LEPROSY);return false;} - if(!Killer->HasStateFlag(ETHEREAL_MOVING)){Killer->GainIntrinsic(ETHEREAL_MOVING);return;} - - if(!Killer->HasStateFlag(REGENERATION)){Killer->GainIntrinsic(REGENERATION);return;} + if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} + + if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} - if(!Killer->HasStateFlag(LEVITATION)){Killer->GainIntrinsic(LEVITATION);return;} + if(!Killer->HasStateFlag(PANIC)){ + Killer->DeActivateTemporaryState(FEARLESS); + Killer->GainIntrinsic(PANIC); + return true; + } + + // Revenge, grant it will stop: + game::GetCurrentLevel()->Explosion( + game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); + + ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); + Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); + + return true; } #endif @@ -1782,22 +1819,28 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) if(bCursedDeveloper){ game::DrawEverything(); - HP=1; //only enough to continue testing normal gameplay - BuffPlayerKiller((character*)Killer); //to spice it up + if(HP<=0) + HP=1; //only enough to continue testing normal gameplay + bool bStay = BuffAndDebuffPlayerKiller((character*)Killer); //to spice it up + if(!bStay) + ((character*)Killer)->SetAssignedName(Killer->GetAssignedName()+"!"); //player killed count for(int c = 0; c < BodyParts; ++c){ //to be able to do something at least if(!GetBodyPart(c) && CanCreateBodyPart(c)){ CreateBodyPart(c); - GetBodyPart(c)->SetHP(1); + if(GetBodyPart(c)->GetHP()<=0) + GetBodyPart(c)->SetHP(1); } } if(GetNP() < HUNGER_LEVEL) SetNP(HUNGER_LEVEL); //to avoid endless sleeping if(GetAction()) GetAction()->Terminate(false); //just to avoid messing any action - if(Killer && !game::IsInWilderness()) + + game::SetMapNote(GetLSquareUnder(),"You almost died here."); + if(!bStay && Killer && !game::IsInWilderness()) Move(GetLevel()->GetRandomSquare(this), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn - ADD_MESSAGE("But no! You are cursed! You are forbidden to rest! And your doings will be forever forgotten..."); + ADD_MESSAGE("But wait... you are cursed, therefore forbidden to R.I.P... and your doings will be forever forgotten..."); return; } #endif diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index b557529ed..ec48293bb 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1267,11 +1267,15 @@ truth game::OnScreen(v2 Pos) && Pos.X < GetCamera().X + GetScreenXSize() && Pos.Y < GetCamera().Y + GetScreenYSize(); } +/** + * prepend + */ void game::SetMapNote(lsquare* lsqrN,festring What) { festring finalWhat; finalWhat << game::MapNoteToken(); finalWhat << What; + if(lsqrN->GetEngraved())finalWhat << " " << lsqrN->GetEngraved(); lsqrN->Engrave(finalWhat); } From af35eddc376c8a2dcdf0d5c157878d80b818416a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 15:38:16 -0300 Subject: [PATCH 145/235] WIP-CURSEDDEVELOPER: fixing and improving usability; --- Main/Source/char.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 1bc29fcc7..4d1dfe7ac 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1819,26 +1819,43 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) if(bCursedDeveloper){ game::DrawEverything(); - if(HP<=0) - HP=1; //only enough to continue testing normal gameplay bool bStay = BuffAndDebuffPlayerKiller((character*)Killer); //to spice it up if(!bStay) ((character*)Killer)->SetAssignedName(Killer->GetAssignedName()+"!"); //player killed count - for(int c = 0; c < BodyParts; ++c){ //to be able to do something at least - if(!GetBodyPart(c) && CanCreateBodyPart(c)){ - CreateBodyPart(c); - if(GetBodyPart(c)->GetHP()<=0) + + // save life but just a little bit + for(int c = 0; c < BodyParts; ++c){ //only enough to continue testing normal gameplay + if(GetBodyPart(c)){ + if(GetBodyPart(c)->GetHP()>GetBodyPart(c)->GetMaxHP()){ //TODO how it happens??? + DBG4(c,GetBodyPart(c)->GetHP(),GetBodyPart(c)->GetMaxHP(),GetBodyPart(c)->GetBodyPartName().CStr()); + GetBodyPart(c)->SetHP(GetBodyPart(c)->GetMaxHP()); + } + }else{ + if(CanCreateBodyPart(c)){ + CreateBodyPart(c); GetBodyPart(c)->SetHP(1); + } } + DBG4(c,GetBodyPart(c)->GetHP(),GetBodyPart(c)->GetMaxHP(),GetBodyPart(c)->GetBodyPartName().CStr()); } + CalculateBodyPartMaxHPs(0); + DBG2(HP,MaxHP); + if(HP>MaxHP) // it MUST be ok here!!! + ABORT("HP>MaxHP %d>%d",HP,MaxHP); + if(GetNP() < HUNGER_LEVEL) SetNP(HUNGER_LEVEL); //to avoid endless sleeping + + if(HasStateFlag(PANIC)) + DeActivateTemporaryState(PANIC); //to be able to do something + if(GetAction()) GetAction()->Terminate(false); //just to avoid messing any action - game::SetMapNote(GetLSquareUnder(),"You almost died here."); - if(!bStay && Killer && !game::IsInWilderness()) + if(!bStay && Killer && !game::IsInWilderness()){ + game::SetMapNote(GetLSquareUnder(),"Your cursed life was saved here."); Move(GetLevel()->GetRandomSquare(this), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn + } ADD_MESSAGE("But wait... you are cursed, therefore forbidden to R.I.P... and your doings will be forever forgotten..."); return; @@ -3847,7 +3864,8 @@ void character::GetPlayerCommand() BeginTemporaryState(PANIC, 500 + RAND_N(500)); } - game::AskForKeyPress(CONST_S("You are horrified by your situation! [press any key to continue]")); + if(!bCursedDeveloper) + game::AskForKeyPress(CONST_S("You are horrified by your situation! [press any key to continue]")); } else if(ivanconfig::GetWarnAboutDanger()) { From 9b6d4aed416fc18bde1207a4cec2e7d65261a6d5 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 16:50:11 -0300 Subject: [PATCH 146/235] WIP-CURSEDDEVELOPER: fixing and improving usability; --- Main/Source/char.cpp | 58 ++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 4d1dfe7ac..9fe979131 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1726,40 +1726,45 @@ bool bCursedDeveloper = false; * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? * @return if player should stay (true) or teleport (false) */ -truth BuffAndDebuffPlayerKiller(character* Killer) +truth BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev) { if(!bCursedDeveloper)return true; if(!Killer)return true; + riBuff=0; + riDebuff=0; + rbRev=false; + // BUFFs, every death makes it harder to player: if(!Killer->HasStateFlag(ESP)){Killer->GainIntrinsic(ESP);return false;} - + riBuff++; if(!Killer->HasStateFlag(INFRA_VISION)){Killer->GainIntrinsic(INFRA_VISION);return false;} - + riBuff++; if(!Killer->HasStateFlag(VAMPIRISM)){Killer->GainIntrinsic(VAMPIRISM);return false;} - + riBuff++; if(!Killer->HasStateFlag(PANIC)) if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return false;} - + riBuff++; if(!Killer->HasStateFlag(SLOW)) if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return false;} - + riBuff++; if(!Killer->HasStateFlag(HICCUPS)) if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return false;} - + riBuff++; if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return false;} - + riBuff++; if(!Killer->HasStateFlag(ETHEREAL_MOVING)){Killer->GainIntrinsic(ETHEREAL_MOVING);return false;} - + riBuff++; if(!Killer->HasStateFlag(REGENERATION)){Killer->GainIntrinsic(REGENERATION);return false;} - + riBuff++; if(!Killer->HasStateFlag(LEVITATION)){Killer->GainIntrinsic(LEVITATION);return false;} - + riBuff++; if(!Killer->HasStateFlag(GAS_IMMUNITY)){Killer->GainIntrinsic(GAS_IMMUNITY);return false;} - + riBuff++; if(!Killer->HasStateFlag(TELEPORT_LOCK)){Killer->GainIntrinsic(TELEPORT_LOCK);return false;} - + riBuff++; if(!Killer->HasStateFlag(POLYMORPH_LOCK)){Killer->GainIntrinsic(POLYMORPH_LOCK);return false;} + riBuff++; // DEBUFFs, after player has taken too much it is time to make it stop, but slowly: if(!Killer->HasStateFlag(HICCUPS)){ @@ -1767,28 +1772,31 @@ truth BuffAndDebuffPlayerKiller(character* Killer) Killer->GainIntrinsic(HICCUPS); return false; } + riDebuff++; if(!Killer->HasStateFlag(SLOW)){ Killer->DeActivateTemporaryState(HASTE); Killer->GainIntrinsic(SLOW); return false; } + riDebuff++; if(!Killer->HasStateFlag(PARASITE_TAPE_WORM)){Killer->GainIntrinsic(PARASITE_TAPE_WORM);return false;} - + riDebuff++; if(!Killer->HasStateFlag(CONFUSED)){Killer->GainIntrinsic(CONFUSED);return false;} - + riDebuff++; if(!Killer->HasStateFlag(LEPROSY)){Killer->GainIntrinsic(LEPROSY);return false;} - + riDebuff++; if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} - + riDebuff++; if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} - + riDebuff++; if(!Killer->HasStateFlag(PANIC)){ Killer->DeActivateTemporaryState(FEARLESS); Killer->GainIntrinsic(PANIC); return true; } + riDebuff++; // Revenge, grant it will stop: game::GetCurrentLevel()->Explosion( @@ -1797,6 +1805,8 @@ truth BuffAndDebuffPlayerKiller(character* Killer) ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); + rbRev=true; + return true; } #endif @@ -1819,9 +1829,11 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) if(bCursedDeveloper){ game::DrawEverything(); - bool bStay = BuffAndDebuffPlayerKiller((character*)Killer); //to spice it up + int iBuff,iDebuff; + bool bRev; + bool bStay = BuffAndDebuffPlayerKiller((character*)Killer,iBuff,iDebuff,bRev); //to spice it up if(!bStay) - ((character*)Killer)->SetAssignedName(Killer->GetAssignedName()+"!"); //player killed count + ((character*)Killer)->SetAssignedName(festring()+"[B"+iBuff+"D"+iDebuff+(bRev?"R":"")+"]"); //player killed count // save life but just a little bit for(int c = 0; c < BodyParts; ++c){ //only enough to continue testing normal gameplay @@ -1830,6 +1842,10 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) DBG4(c,GetBodyPart(c)->GetHP(),GetBodyPart(c)->GetMaxHP(),GetBodyPart(c)->GetBodyPartName().CStr()); GetBodyPart(c)->SetHP(GetBodyPart(c)->GetMaxHP()); } + if(GetBodyPart(c)->GetHP()<1){ + DBG4(c,GetBodyPart(c)->GetHP(),GetBodyPart(c)->GetMaxHP(),GetBodyPart(c)->GetBodyPartName().CStr()); + GetBodyPart(c)->SetHP(1); + } }else{ if(CanCreateBodyPart(c)){ CreateBodyPart(c); @@ -5480,7 +5496,7 @@ int character::ReceiveBodyPartDamage(character* Damager, int Damage, int Type, i else if(IsPlayer() || CanBeSeenByPlayer()) ADD_MESSAGE("It vanishes."); - if(IsPlayer()) + if(IsPlayer() && !bCursedDeveloper) game::AskForKeyPress(CONST_S("Bodypart severed! [press any key to continue]")); } From a695233c6d7af9e689431dc33a971b39454d951d Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 20:06:31 -0300 Subject: [PATCH 147/235] sfx regex fix for "die" message; WIP-CURSEDDEVELOPER: fixing and improving usability; --- Main/Source/char.cpp | 47 ++++++++++++++++++++++++++++++------------ Sound/SoundEffects.cfg | 2 +- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 9fe979131..7a5e61136 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -26,6 +26,7 @@ #include "hiteffect.h" //TODO move to charsset.cpp? #include "lterras.h" #include "gods.h" +#include "fluid.h" //#define DBGMSG_V2 #include "dbgmsgproj.h" @@ -1787,8 +1788,9 @@ truth BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool riDebuff++; if(!Killer->HasStateFlag(LEPROSY)){Killer->GainIntrinsic(LEPROSY);return false;} riDebuff++; - if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} - riDebuff++; +// this is too much as adds worm mobs on the dungeon... +// if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} +// riDebuff++; if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} riDebuff++; if(!Killer->HasStateFlag(PANIC)){ @@ -1837,24 +1839,43 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) // save life but just a little bit for(int c = 0; c < BodyParts; ++c){ //only enough to continue testing normal gameplay - if(GetBodyPart(c)){ - if(GetBodyPart(c)->GetHP()>GetBodyPart(c)->GetMaxHP()){ //TODO how it happens??? - DBG4(c,GetBodyPart(c)->GetHP(),GetBodyPart(c)->GetMaxHP(),GetBodyPart(c)->GetBodyPartName().CStr()); - GetBodyPart(c)->SetHP(GetBodyPart(c)->GetMaxHP()); + bodypart* bp = GetBodyPart(c); + if(bp){ + if(bp->GetHP()>bp->GetMaxHP()){ //TODO how it happens??? + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + bp->SetHP(bp->GetMaxHP()); } - if(GetBodyPart(c)->GetHP()<1){ - DBG4(c,GetBodyPart(c)->GetHP(),GetBodyPart(c)->GetMaxHP(),GetBodyPart(c)->GetBodyPartName().CStr()); - GetBodyPart(c)->SetHP(1); + if(bp->GetHP()<1){ + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + if(GetBodyPart(TORSO_INDEX)==bp || GetBodyPart(GROIN_INDEX)==bp){ +// fluidvector fv; +// bp->FillFluidVector(fv); +// for(int i=0;iGetLiquid()?fv[i]->GetLiquid()->GetName().CStr():"", fv[i]->IsDangerous(this)); +// bp->RemoveFluid(fv[i]); +// } +// bp->FastRestoreHP(); + /** + * How to prevent endless die loop? + * Clear the bad effects? better not, let them continue working. + * A bit more of HP to the core body parts may suffice (funny head is not one lol). + */ + static int iHpMinOk=10; //this is to fight mustard gas + bp->SetHP(GetMaxHP()>iHpMinOk ? iHpMinOk : GetMaxHP()); + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + }else{ + bp->SetHP(1); + } } }else{ if(CanCreateBodyPart(c)){ - CreateBodyPart(c); - GetBodyPart(c)->SetHP(1); + bp=CreateBodyPart(c); + bp->SetHP(1); } } - DBG4(c,GetBodyPart(c)->GetHP(),GetBodyPart(c)->GetMaxHP(),GetBodyPart(c)->GetBodyPartName().CStr()); + DBGEXEC(if(bp)DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr())); } - CalculateBodyPartMaxHPs(0); + CalculateBodyPartMaxHPs(0); //this also calculates the overall current HP DBG2(HP,MaxHP); if(HP>MaxHP) // it MUST be ok here!!! ABORT("HP>MaxHP %d>%d",HP,MaxHP); diff --git a/Sound/SoundEffects.cfg b/Sound/SoundEffects.cfg index 31066850a..9a268f90e 100644 --- a/Sound/SoundEffects.cfg +++ b/Sound/SoundEffects.cfg @@ -120,7 +120,7 @@ web; teardown1.wav, teardown2.wav, teardown3.wav;.*tear the web down.* bleedsbadly; heartbeat.wav;.*bleeds very badly.* severed; severed.wav;.*is severed.* -die; die.wav, die2.wav, die3.wav, die4.wav, die5.wav;.*You.* die.* +die; die.wav, die2.wav, die3.wav, die4.wav, die5.wav;.*(You die[.]| you died[.]|, you die).* ###################### ### Player Actions ### From d813d5014498a9c6e97911abc3d4462bf6a8c6db Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 21:08:20 -0300 Subject: [PATCH 148/235] curseddeveloper.cpp (and .h) new file (unbloating char.cpp), and also set at CMakeLists.txt; charset.cpp sorted includes worked fine; --- .../nbproject/configurations.xml | 6 + Main/CMakeLists.txt | 1 + Main/Include/char.h | 1 + Main/Include/curseddeveloper.h | 25 +++ Main/Include/fluid.h | 4 +- Main/Source/char.cpp | 185 +---------------- Main/Source/charset.cpp | 43 ++-- Main/Source/curseddeveloper.cpp | 194 ++++++++++++++++++ Main/Source/fluid.cpp | 5 + 9 files changed, 265 insertions(+), 199 deletions(-) create mode 100644 Main/Include/curseddeveloper.h create mode 100644 Main/Source/curseddeveloper.cpp diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 653d045b5..24cf718b9 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -19,6 +19,7 @@ namegen.cc + curseddeveloper.h dungeon.h @@ -67,6 +68,7 @@ command.cpp cont.cpp coreset.cpp + curseddeveloper.cpp database.cpp dataset.cpp definesvalidator.cpp @@ -245,6 +247,8 @@ + + @@ -277,6 +281,8 @@ + + diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index 7bd570658..083f76f1a 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -30,6 +30,7 @@ set_source_files_properties( Source/wsquare.cpp Source/wterra.cpp Source/wterras.cpp Source/hiteffect.cpp Source/cmdcraft.cpp Source/cmdcraftfilters.cpp Source/cmdswapweap.cpp + Source/curseddeveloper.cpp PROPERTIES HEADER_FILE_ONLY TRUE) add_executable(ivan ${IVAN_SOURCES} Resource/Ivan.rc) diff --git a/Main/Include/char.h b/Main/Include/char.h index 55c4a28dd..c01cbffe8 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -287,6 +287,7 @@ class character : public entity, public id public: friend class databasecreator; friend class corpse; + friend class cursedDeveloper; typedef characterprototype prototype; typedef characterdatabase database; character(); diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h new file mode 100644 index 000000000..f23b2d19e --- /dev/null +++ b/Main/Include/curseddeveloper.h @@ -0,0 +1,25 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __CURSEDDEVELOPER_H__ +#define __CURSEDDEVELOPER_H__ + +class cursedDeveloper { + public: + static bool BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev); + static bool LifeSaveJustABit(character* Killer); + static bool IsCursedDeveloper(){return bCursedDeveloper;}; + private: + static bool bCursedDeveloper; +}; + +#endif //__CURSEDDEVELOPER_H__ diff --git a/Main/Include/fluid.h b/Main/Include/fluid.h index 26059fe29..91a3b41d4 100644 --- a/Main/Include/fluid.h +++ b/Main/Include/fluid.h @@ -20,6 +20,8 @@ class bitmap; typedef truth (rawbitmap::*pixelpredicate)(v2) const; +class liquid; + class fluid : public entity { public: @@ -56,7 +58,7 @@ class fluid : public entity cfestring& GetLocationName() const { return LocationName; } truth IsInside() const { return Flags & FLUID_INSIDE; } truth UseImage() const; - virtual int GetTrapType() const { return Liquid->GetType() | FLUID_TRAP; } + virtual int GetTrapType() const; virtual ulong GetTrapID() const { return TrapData.TrapID; } virtual ulong GetVictimID() const { return TrapData.VictimID; } virtual void AddTrapName(festring&, int) const; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 7a5e61136..6962773de 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -23,11 +23,6 @@ * These flags can be found in ivandef.h. RANDOMIZABLE sets all source * & duration flags at once. */ -#include "hiteffect.h" //TODO move to charsset.cpp? -#include "lterras.h" -#include "gods.h" -#include "fluid.h" - //#define DBGMSG_V2 #include "dbgmsgproj.h" #include @@ -1712,107 +1707,6 @@ void character::AutoPlayAITeleport(bool bDeathCountBased) Move(GetLevel()->GetRandomSquare(this), true); //not using teleport function to avoid prompts, but this code is from there } -/** - * This is a developer environment variable to test the game without wizard mode. - */ -#ifdef CURSEDDEVELOPER -bool bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); -#else -bool bCursedDeveloper = false; -#endif - -#ifdef CURSEDDEVELOPER -/** - * this will make the NPC that kills the player more challenging for every kill - * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? - * @return if player should stay (true) or teleport (false) - */ -truth BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev) -{ - if(!bCursedDeveloper)return true; - if(!Killer)return true; - - riBuff=0; - riDebuff=0; - rbRev=false; - - // BUFFs, every death makes it harder to player: - if(!Killer->HasStateFlag(ESP)){Killer->GainIntrinsic(ESP);return false;} - riBuff++; - if(!Killer->HasStateFlag(INFRA_VISION)){Killer->GainIntrinsic(INFRA_VISION);return false;} - riBuff++; - if(!Killer->HasStateFlag(VAMPIRISM)){Killer->GainIntrinsic(VAMPIRISM);return false;} - riBuff++; - if(!Killer->HasStateFlag(PANIC)) - if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return false;} - riBuff++; - if(!Killer->HasStateFlag(SLOW)) - if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return false;} - riBuff++; - if(!Killer->HasStateFlag(HICCUPS)) - if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return false;} - riBuff++; - if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return false;} - riBuff++; - if(!Killer->HasStateFlag(ETHEREAL_MOVING)){Killer->GainIntrinsic(ETHEREAL_MOVING);return false;} - riBuff++; - if(!Killer->HasStateFlag(REGENERATION)){Killer->GainIntrinsic(REGENERATION);return false;} - riBuff++; - if(!Killer->HasStateFlag(LEVITATION)){Killer->GainIntrinsic(LEVITATION);return false;} - riBuff++; - if(!Killer->HasStateFlag(GAS_IMMUNITY)){Killer->GainIntrinsic(GAS_IMMUNITY);return false;} - riBuff++; - if(!Killer->HasStateFlag(TELEPORT_LOCK)){Killer->GainIntrinsic(TELEPORT_LOCK);return false;} - riBuff++; - if(!Killer->HasStateFlag(POLYMORPH_LOCK)){Killer->GainIntrinsic(POLYMORPH_LOCK);return false;} - riBuff++; - - // DEBUFFs, after player has taken too much it is time to make it stop, but slowly: - if(!Killer->HasStateFlag(HICCUPS)){ - Killer->DeActivateTemporaryState(INVISIBLE); - Killer->GainIntrinsic(HICCUPS); - return false; - } - riDebuff++; - - if(!Killer->HasStateFlag(SLOW)){ - Killer->DeActivateTemporaryState(HASTE); - Killer->GainIntrinsic(SLOW); - return false; - } - riDebuff++; - - if(!Killer->HasStateFlag(PARASITE_TAPE_WORM)){Killer->GainIntrinsic(PARASITE_TAPE_WORM);return false;} - riDebuff++; - if(!Killer->HasStateFlag(CONFUSED)){Killer->GainIntrinsic(CONFUSED);return false;} - riDebuff++; - if(!Killer->HasStateFlag(LEPROSY)){Killer->GainIntrinsic(LEPROSY);return false;} - riDebuff++; -// this is too much as adds worm mobs on the dungeon... -// if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} -// riDebuff++; - if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} - riDebuff++; - if(!Killer->HasStateFlag(PANIC)){ - Killer->DeActivateTemporaryState(FEARLESS); - Killer->GainIntrinsic(PANIC); - return true; - } - riDebuff++; - - // Revenge, grant it will stop: - game::GetCurrentLevel()->Explosion( - game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); - - ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); - Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); - - rbRev=true; - - return true; -} -#endif - void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) { /* Note: This function musn't delete any objects, since one of these may be @@ -1827,77 +1721,8 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) { ADD_MESSAGE("You die."); -#ifdef CURSEDDEVELOPER // so that this code wont be compiled to non developer players - if(bCursedDeveloper){ - game::DrawEverything(); - - int iBuff,iDebuff; - bool bRev; - bool bStay = BuffAndDebuffPlayerKiller((character*)Killer,iBuff,iDebuff,bRev); //to spice it up - if(!bStay) - ((character*)Killer)->SetAssignedName(festring()+"[B"+iBuff+"D"+iDebuff+(bRev?"R":"")+"]"); //player killed count - - // save life but just a little bit - for(int c = 0; c < BodyParts; ++c){ //only enough to continue testing normal gameplay - bodypart* bp = GetBodyPart(c); - if(bp){ - if(bp->GetHP()>bp->GetMaxHP()){ //TODO how it happens??? - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - bp->SetHP(bp->GetMaxHP()); - } - if(bp->GetHP()<1){ - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - if(GetBodyPart(TORSO_INDEX)==bp || GetBodyPart(GROIN_INDEX)==bp){ -// fluidvector fv; -// bp->FillFluidVector(fv); -// for(int i=0;iGetLiquid()?fv[i]->GetLiquid()->GetName().CStr():"", fv[i]->IsDangerous(this)); -// bp->RemoveFluid(fv[i]); -// } -// bp->FastRestoreHP(); - /** - * How to prevent endless die loop? - * Clear the bad effects? better not, let them continue working. - * A bit more of HP to the core body parts may suffice (funny head is not one lol). - */ - static int iHpMinOk=10; //this is to fight mustard gas - bp->SetHP(GetMaxHP()>iHpMinOk ? iHpMinOk : GetMaxHP()); - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - }else{ - bp->SetHP(1); - } - } - }else{ - if(CanCreateBodyPart(c)){ - bp=CreateBodyPart(c); - bp->SetHP(1); - } - } - DBGEXEC(if(bp)DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr())); - } - CalculateBodyPartMaxHPs(0); //this also calculates the overall current HP - DBG2(HP,MaxHP); - if(HP>MaxHP) // it MUST be ok here!!! - ABORT("HP>MaxHP %d>%d",HP,MaxHP); - - if(GetNP() < HUNGER_LEVEL) - SetNP(HUNGER_LEVEL); //to avoid endless sleeping - - if(HasStateFlag(PANIC)) - DeActivateTemporaryState(PANIC); //to be able to do something - - if(GetAction()) - GetAction()->Terminate(false); //just to avoid messing any action - - if(!bStay && Killer && !game::IsInWilderness()){ - game::SetMapNote(GetLSquareUnder(),"Your cursed life was saved here."); - Move(GetLevel()->GetRandomSquare(this), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn - } - - ADD_MESSAGE("But wait... you are cursed, therefore forbidden to R.I.P... and your doings will be forever forgotten..."); + if(cursedDeveloper::LifeSaveJustABit((character*)Killer)) return; - } -#endif if(game::WizardModeIsActive()) { @@ -2712,7 +2537,7 @@ void character::AddScoreEntry(cfestring& Description, double Multiplier, truth A { if(game::WizardModeIsReallyActive()) return; - if(bCursedDeveloper) + if(cursedDeveloper::IsCursedDeveloper()) return; highscore HScore(GetUserDataDir() + HIGH_SCORE_FILENAME); @@ -3901,7 +3726,7 @@ void character::GetPlayerCommand() BeginTemporaryState(PANIC, 500 + RAND_N(500)); } - if(!bCursedDeveloper) + if(!cursedDeveloper::IsCursedDeveloper()) game::AskForKeyPress(CONST_S("You are horrified by your situation! [press any key to continue]")); } else if(ivanconfig::GetWarnAboutDanger()) @@ -5517,7 +5342,7 @@ int character::ReceiveBodyPartDamage(character* Damager, int Damage, int Type, i else if(IsPlayer() || CanBeSeenByPlayer()) ADD_MESSAGE("It vanishes."); - if(IsPlayer() && !bCursedDeveloper) + if(IsPlayer() && !cursedDeveloper::IsCursedDeveloper()) game::AskForKeyPress(CONST_S("Bodypart severed! [press any key to continue]")); } @@ -9284,7 +9109,7 @@ festring character::GetPanelName() const festring PanelName; if(!game::IsInWilderness()){ #ifdef CURSEDDEVELOPER - if(bCursedDeveloper) + if(cursedDeveloper::IsCursedDeveloper()) PanelName << "[Cursed Developer!] "; #endif PanelName << Name; diff --git a/Main/Source/charset.cpp b/Main/Source/charset.cpp index 5e15fc68b..b97bffa43 100644 --- a/Main/Source/charset.cpp +++ b/Main/Source/charset.cpp @@ -27,30 +27,37 @@ EXTENDED_SYSTEM_SPECIALIZATIONS(character)(0, 0, 0, "character"); #include #include -#include "team.h" -#include "error.h" -#include "game.h" -#include "message.h" -#include "save.h" -#include "stack.h" -#include "wsquare.h" #include "actions.h" -#include "iconf.h" -#include "whandler.h" -#include "hscore.h" -#include "god.h" +#include "balance.h" +#include "bitmap.h" #include "command.h" -#include "materias.h" -#include "room.h" +#include "confdef.h" +#include "curseddeveloper.h" +#include "error.h" #include "felist.h" +#include "fluid.h" +#include "game.h" +#include "god.h" +#include "gods.h" #include "graphics.h" -#include "bitmap.h" -#include "rawbit.h" +#include "hiteffect.h" +#include "hscore.h" +#include "iconf.h" +#include "iloops.h" +#include "ivandef.h" +#include "lterras.h" +#include "materias.h" +#include "message.h" #include "miscitem.h" -#include "confdef.h" +#include "rawbit.h" +#include "room.h" +#include "save.h" +#include "stack.h" +#include "team.h" #include "traps.h" -#include "iloops.h" -#include "balance.h" +#include "whandler.h" +#include "wsquare.h" #include "team.cpp" #include "char.cpp" +#include "curseddeveloper.cpp" diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp new file mode 100644 index 000000000..26acfa29d --- /dev/null +++ b/Main/Source/curseddeveloper.cpp @@ -0,0 +1,194 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "dbgmsgproj.h" + +#include +#include + +/** + * This is a developer environment variable to test the game without wizard mode. + */ +#ifdef CURSEDDEVELOPER +bool cursedDeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); +#else +bool cursedDeveloper::bCursedDeveloper = false; +#endif + +#ifndef CURSEDDEVELOPER +bool cursedDeveloper::LifeSaveJustABit(character* Killer){return false;} +#else +bool cursedDeveloper::LifeSaveJustABit(character* Killer) +{ + if(!bCursedDeveloper) + return false; + + character* P = game::GetPlayer(); + game::DrawEverything(); + + int iBuff,iDebuff; + bool bRev; + bool bStay = BuffAndDebuffPlayerKiller(Killer,iBuff,iDebuff,bRev); //to spice it up + if(!bStay) + Killer->SetAssignedName(festring()+"[B"+iBuff+"D"+iDebuff+(bRev?"R":"")+"]"); //player killed count + + // save life but just a little bit + for(int c = 0; c < P->BodyParts; ++c){ //only enough to continue testing normal gameplay + bodypart* bp = P->GetBodyPart(c); + if(bp){ + if(bp->GetHP()>bp->GetMaxHP()){ //TODO how it happens??? + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + bp->SetHP(bp->GetMaxHP()); + } + if(bp->GetHP()<1){ + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + if(P->GetBodyPart(TORSO_INDEX)==bp || P->GetBodyPart(GROIN_INDEX)==bp){ +// fluidvector fv; +// bp->FillFluidVector(fv); +// for(int i=0;iGetLiquid()?fv[i]->GetLiquid()->GetName().CStr():"", fv[i]->IsDangerous(this)); +// bp->RemoveFluid(fv[i]); +// } +// bp->FastRestoreHP(); + /** + * How to prevent endless die loop? + * Clear the bad effects? better not, let them continue working. + * A bit more of HP to the core body parts may suffice (funny head is not one lol). + */ + static int iHpMinOk=10; //this is to fight mustard gas + bp->SetHP(P->GetMaxHP()>iHpMinOk ? iHpMinOk : P->GetMaxHP()); + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + }else{ + bp->SetHP(1); + } + } + }else{ + if(P->CanCreateBodyPart(c)){ + bp=P->CreateBodyPart(c); + bp->SetHP(1); + } + } + DBGEXEC(if(bp)DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr())); + } + P->CalculateBodyPartMaxHPs(0); //this also calculates the overall current HP + DBG2(P->HP,P->MaxHP); + if(P->HP>P->MaxHP) // it MUST be ok here!!! + ABORT("HP>MaxHP %d>%d",P->HP,P->MaxHP); + + if(P->GetNP() < HUNGER_LEVEL) + P->SetNP(HUNGER_LEVEL); //to avoid endless sleeping + + if(P->HasStateFlag(PANIC)) + P->DeActivateTemporaryState(PANIC); //to be able to do something + + if(P->GetAction()) + P->GetAction()->Terminate(false); //just to avoid messing any action + + if(!bStay && Killer && !game::IsInWilderness()){ + game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here."); + P->Move(P->GetLevel()->GetRandomSquare(P), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn + } + + ADD_MESSAGE("But wait... you are cursed, therefore forbidden to R.I.P... and your doings will be forever forgotten..."); + return true; +} + +/** + * this will make the NPC that kills the player more challenging for every kill + * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? + * @return if player should stay (true) or teleport (false) + */ +bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev) +{ + if(!bCursedDeveloper)return true; + if(!Killer)return true; + + riBuff=0; + riDebuff=0; + rbRev=false; + + // BUFFs, every death makes it harder to player: + if(!Killer->HasStateFlag(ESP)){Killer->GainIntrinsic(ESP);return false;} + riBuff++; + if(!Killer->HasStateFlag(INFRA_VISION)){Killer->GainIntrinsic(INFRA_VISION);return false;} + riBuff++; + if(!Killer->HasStateFlag(VAMPIRISM)){Killer->GainIntrinsic(VAMPIRISM);return false;} + riBuff++; + if(!Killer->HasStateFlag(PANIC)) + if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return false;} + riBuff++; + if(!Killer->HasStateFlag(SLOW)) + if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return false;} + riBuff++; + if(!Killer->HasStateFlag(HICCUPS)) + if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return false;} + riBuff++; + if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return false;} + riBuff++; + if(!Killer->HasStateFlag(ETHEREAL_MOVING)){Killer->GainIntrinsic(ETHEREAL_MOVING);return false;} + riBuff++; + if(!Killer->HasStateFlag(REGENERATION)){Killer->GainIntrinsic(REGENERATION);return false;} + riBuff++; + if(!Killer->HasStateFlag(LEVITATION)){Killer->GainIntrinsic(LEVITATION);return false;} + riBuff++; + if(!Killer->HasStateFlag(GAS_IMMUNITY)){Killer->GainIntrinsic(GAS_IMMUNITY);return false;} + riBuff++; + if(!Killer->HasStateFlag(TELEPORT_LOCK)){Killer->GainIntrinsic(TELEPORT_LOCK);return false;} + riBuff++; + if(!Killer->HasStateFlag(POLYMORPH_LOCK)){Killer->GainIntrinsic(POLYMORPH_LOCK);return false;} + riBuff++; + + // DEBUFFs, after player has taken too much it is time to make it stop, but slowly: + if(!Killer->HasStateFlag(HICCUPS)){ + Killer->DeActivateTemporaryState(INVISIBLE); + Killer->GainIntrinsic(HICCUPS); + return false; + } + riDebuff++; + + if(!Killer->HasStateFlag(SLOW)){ + Killer->DeActivateTemporaryState(HASTE); + Killer->GainIntrinsic(SLOW); + return false; + } + riDebuff++; + + if(!Killer->HasStateFlag(PARASITE_TAPE_WORM)){Killer->GainIntrinsic(PARASITE_TAPE_WORM);return false;} + riDebuff++; + if(!Killer->HasStateFlag(CONFUSED)){Killer->GainIntrinsic(CONFUSED);return false;} + riDebuff++; + if(!Killer->HasStateFlag(LEPROSY)){Killer->GainIntrinsic(LEPROSY);return false;} + riDebuff++; +// this is too much as adds worm mobs on the dungeon... +// if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} +// riDebuff++; + if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} + riDebuff++; + if(!Killer->HasStateFlag(PANIC)){ + Killer->DeActivateTemporaryState(FEARLESS); + Killer->GainIntrinsic(PANIC); + return true; + } + riDebuff++; + + // Revenge, grant it will stop: + game::GetCurrentLevel()->Explosion( + game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); + + ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); + Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); + + rbRev=true; + + return true; +} +#endif //CURSEDDEVELOPER diff --git a/Main/Source/fluid.cpp b/Main/Source/fluid.cpp index bec487267..883ab39f4 100644 --- a/Main/Source/fluid.cpp +++ b/Main/Source/fluid.cpp @@ -675,6 +675,11 @@ void fluid::Destroy() SendToHell(); } +int fluid::GetTrapType() const +{ + return Liquid->GetType() | FLUID_TRAP; +} + truth fluid::UseImage() const { return !(Flags & FLUID_INSIDE) From 94fd9e89fd4a4f64a96c96137227ef1b3b394af9 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 20 Apr 2020 22:03:08 -0300 Subject: [PATCH 149/235] WIP-cursedDeveloper: improving, just usable bodyparts; --- Main/Source/charset.cpp | 1 + Main/Source/curseddeveloper.cpp | 61 +++++++++++++++------------------ 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/Main/Source/charset.cpp b/Main/Source/charset.cpp index b97bffa43..dd0c34b9e 100644 --- a/Main/Source/charset.cpp +++ b/Main/Source/charset.cpp @@ -30,6 +30,7 @@ EXTENDED_SYSTEM_SPECIALIZATIONS(character)(0, 0, 0, "character"); #include "actions.h" #include "balance.h" #include "bitmap.h" +#include "bodypart.h" #include "command.h" #include "confdef.h" #include "curseddeveloper.h" diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 26acfa29d..a1c9afef2 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -44,40 +44,35 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) // save life but just a little bit for(int c = 0; c < P->BodyParts; ++c){ //only enough to continue testing normal gameplay bodypart* bp = P->GetBodyPart(c); - if(bp){ - if(bp->GetHP()>bp->GetMaxHP()){ //TODO how it happens??? - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - bp->SetHP(bp->GetMaxHP()); - } - if(bp->GetHP()<1){ - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - if(P->GetBodyPart(TORSO_INDEX)==bp || P->GetBodyPart(GROIN_INDEX)==bp){ -// fluidvector fv; -// bp->FillFluidVector(fv); -// for(int i=0;iGetLiquid()?fv[i]->GetLiquid()->GetName().CStr():"", fv[i]->IsDangerous(this)); -// bp->RemoveFluid(fv[i]); -// } -// bp->FastRestoreHP(); - /** - * How to prevent endless die loop? - * Clear the bad effects? better not, let them continue working. - * A bit more of HP to the core body parts may suffice (funny head is not one lol). - */ - static int iHpMinOk=10; //this is to fight mustard gas - bp->SetHP(P->GetMaxHP()>iHpMinOk ? iHpMinOk : P->GetMaxHP()); - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - }else{ - bp->SetHP(1); - } - } - }else{ - if(P->CanCreateBodyPart(c)){ - bp=P->CreateBodyPart(c); - bp->SetHP(1); - } + if(!bp && P->CanCreateBodyPart(c)) + bp=P->CreateBodyPart(c); + if(!bp)continue; + + if(bp->GetHP() > bp->GetMaxHP()){ //TODO how does this happens??? + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + bp->SetHP(-1); } - DBGEXEC(if(bp)DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr())); + + /** + * How to prevent endless die loop? + * Clear the bad effects? better not, let them continue working. + * A bit more of HP to the core body parts may suffice (funny head is not one lol). + */ + static int iTorsoHpMinOk=10; //this is to fight mustard gas + if(P->GetBodyPart(TORSO_INDEX)==bp && bp->GetHP() < iTorsoHpMinOk){ + bp->SetHP(P->GetMaxHP()>iTorsoHpMinOk ? iTorsoHpMinOk : P->GetMaxHP()); + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + continue; + } + + int iHpMinUsable = bp->GetMaxHP()/3 + (bp->GetMaxHP()%3>0 ? 1 : 0); //ceil + if(bp->GetHP() < iHpMinUsable){ + DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + bp->SetHP(iHpMinUsable); + bp->SignalPossibleUsabilityChange(); + } + + DBG5(c,iHpMinUsable,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); } P->CalculateBodyPartMaxHPs(0); //this also calculates the overall current HP DBG2(P->HP,P->MaxHP); From e71ae368a0d6553d03ea2950505fe327a71d23fa Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 21 Apr 2020 22:40:01 -0300 Subject: [PATCH 150/235] new file wizautoplay.cpp (and .h) to unbloat mainly char.cpp) --- .../nbproject/configurations.xml | 6 + Main/CMakeLists.txt | 5 +- Main/Include/char.h | 20 +- Main/Include/game.h | 12 - Main/Include/human.h | 1 - Main/Include/wizautoplay.h | 66 + Main/Source/char.cpp | 1081 +------------ Main/Source/charset.cpp | 2 + Main/Source/cmdcraft.cpp | 12 + Main/Source/command.cpp | 7 +- Main/Source/curseddeveloper.cpp | 7 +- Main/Source/dungeon.cpp | 20 +- Main/Source/game.cpp | 70 +- Main/Source/gear.cpp | 2 +- Main/Source/human.cpp | 219 --- Main/Source/itemset.cpp | 30 +- Main/Source/miscitem.cpp | 6 +- Main/Source/wizautoplay.cpp | 1365 +++++++++++++++++ 18 files changed, 1508 insertions(+), 1423 deletions(-) create mode 100644 Main/Include/wizautoplay.h create mode 100644 Main/Source/wizautoplay.cpp diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 24cf718b9..e2e35875a 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -21,6 +21,7 @@ curseddeveloper.h dungeon.h + wizautoplay.h bitmap.h @@ -105,6 +106,7 @@ trap.cpp traps.cpp trapset.cpp + wizautoplay.cpp wmapset.cpp worldmap.cpp wskill.cpp @@ -251,6 +253,8 @@ + + @@ -355,6 +359,8 @@ + + diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index 083f76f1a..391f42547 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -28,9 +28,8 @@ set_source_files_properties( Source/smoke.cpp Source/square.cpp Source/stack.cpp Source/team.cpp Source/terra.cpp Source/trap.cpp Source/traps.cpp Source/worldmap.cpp Source/wsquare.cpp Source/wterra.cpp Source/wterras.cpp Source/hiteffect.cpp - Source/cmdcraft.cpp Source/cmdcraftfilters.cpp - Source/cmdswapweap.cpp - Source/curseddeveloper.cpp + Source/cmdcraft.cpp Source/cmdcraftfilters.cpp Source/cmdswapweap.cpp + Source/curseddeveloper.cpp Source/wizautoplay.cpp PROPERTIES HEADER_FILE_ONLY TRUE) add_executable(ivan ${IVAN_SOURCES} Resource/Ivan.rc) diff --git a/Main/Include/char.h b/Main/Include/char.h index c01cbffe8..2ea26bbda 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -288,6 +288,7 @@ class character : public entity, public id friend class databasecreator; friend class corpse; friend class cursedDeveloper; + friend class wizautoplay; typedef characterprototype prototype; typedef characterdatabase database; character(); @@ -1187,7 +1188,6 @@ class character : public entity, public id void SignalBurn(); void Extinguish(truth); truth IsBurnt() const; - truth IsPlayerAutoPlay(); truth CheckAIZapOpportunity(); int GetAdjustedStaminaCost(int, int); truth TryToStealFromShop(character*, item*); @@ -1224,24 +1224,6 @@ class character : public entity, public id void StandIdleAI(); virtual void CreateCorpse(lsquare*); void GetPlayerCommand(); - - truth AutoPlayAICommand(int&); - truth AutoPlayAIPray(); - bool AutoPlayAIChkInconsistency(); - static void AutoPlayAIDebugDrawSquareRect(v2 v2SqrPos, col16 color, int iPrintIndex=-1, bool bWide=false, bool bKeepColor=false); - static void AutoPlayAIDebugDrawOverlay(); - static bool AutoPlayAICheckAreaLevelChangedAndReset(); - truth AutoPlayAIcanApply(item* it); - truth AutoPlayAIDropThings(); - bool IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern); - truth AutoPlayAIEquipAndPickup(bool bPlayerHasLantern); - int AutoPlayAIFindWalkDist(v2 v2To); - truth AutoPlayAITestValidPathTo(v2 v2To); - truth AutoPlayAINavigateDungeon(bool bPlayerHasLantern); - truth AutoPlayAISetAndValidateKeepGoingTo(v2 v2KGTo); - void AutoPlayAITeleport(bool bDeathCountBased); - void AutoPlayAIReset(bool bFailedToo); - virtual void GetAICommand(); truth MoveTowardsTarget(truth); virtual cchar* FirstPersonUnarmedHitVerb() const; diff --git a/Main/Include/game.h b/Main/Include/game.h index ed69ec00b..872e08c8e 100644 --- a/Main/Include/game.h +++ b/Main/Include/game.h @@ -168,12 +168,6 @@ class areachangerequest { }; typedef void (*dbgdrawoverlay)(); -#define AUTOPLAYMODE_DISABLED 0 -#define AUTOPLAYMODE_NOTIMEOUT 1 -#define AUTOPLAYMODE_SLOW 2 -#define AUTOPLAYMODE_FAST 3 -#define AUTOPLAYMODE_FRENZY 4 - #define AUTOSAVE_SUFFIX ".AutoSave" #define CUSTOM_KEYS_FILENAME "CustomCommandKeys.cfg" class game @@ -409,10 +403,6 @@ class game #ifdef WIZARD static void ActivateWizardMode() { WizardMode = true; } static truth WizardModeIsActive() { return WizardMode; } - static void IncAutoPlayMode(); - static int GetAutoPlayMode() { return AutoPlayMode; } - static void AutoPlayModeApply(); - static void DisableAutoPlayMode() {AutoPlayMode=AUTOPLAYMODE_DISABLED;AutoPlayModeApply();} static void SeeWholeMap(); static int GetSeeWholeMapCheatMode() { return SeeWholeMapCheatMode; } static truth GoThroughWallsCheatIsActive() { return GoThroughWallsCheat; } @@ -421,7 +411,6 @@ class game static truth WizardModeIsActive() { return false; } static int GetSeeWholeMapCheatMode() { return 0; } static truth GoThroughWallsCheatIsActive() { return false; } - static int GetAutoPlayMode() { return AUTOPLAYMODE_DISABLED; } #endif static truth WizardModeIsReallyActive() { return WizardMode; } @@ -593,7 +582,6 @@ class game static long PetMassacreAmount; static long MiscMassacreAmount; static truth WizardMode; - static int AutoPlayMode; static int SeeWholeMapCheatMode; static truth GoThroughWallsCheat; static int QuestMonstersFound; diff --git a/Main/Include/human.h b/Main/Include/human.h index 1ea5af57e..1e7a181cb 100644 --- a/Main/Include/human.h +++ b/Main/Include/human.h @@ -172,7 +172,6 @@ CHARACTER(humanoid, character) truth HasSadistWeapon() const; truth CheckAIZapOpportunity(); virtual truth HasSadistAttackMode() const; - truth AutoPlayAIequipConsumeZapReadApply(); static v2 GetSilhouetteWhere(){return SilhouetteWhere;} static v2 GetSilhouetteWhereDefault(){return SilhouetteWhereDefault;} static void SetSilhouetteWhere(v2 pos){SilhouetteWhere=pos;} diff --git a/Main/Include/wizautoplay.h b/Main/Include/wizautoplay.h new file mode 100644 index 000000000..bb95b23cf --- /dev/null +++ b/Main/Include/wizautoplay.h @@ -0,0 +1,66 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __WIZAUTOPLAY_H__ +#define __WIZAUTOPLAY_H__ + +#define AUTOPLAYMODE_DISABLED 0 +#define AUTOPLAYMODE_NOTIMEOUT 1 +#define AUTOPLAYMODE_SLOW 2 +#define AUTOPLAYMODE_FAST 3 +#define AUTOPLAYMODE_FRENZY 4 + +class wizautoplay +{ + public: + static void AutoPlayCommandKey(character* C,int& Key,truth& HasActed,truth& ValidKeyPressed); + static truth AutoPlayAICommand(int&); + static truth AutoPlayAIPray(); + static bool AutoPlayAIChkInconsistency(); + static void AutoPlayAIDebugDrawSquareRect(v2 v2SqrPos, col16 color, int iPrintIndex=-1, bool bWide=false, bool bKeepColor=false); + static void AutoPlayAIDebugDrawOverlay(); + static bool AutoPlayAICheckAreaLevelChangedAndReset(); + static truth AutoPlayAIcanApply(item* it); + static truth AutoPlayAIDropThings(); + static bool IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern); + static truth AutoPlayAIEquipAndPickup(bool bPlayerHasLantern); + static int AutoPlayAIFindWalkDist(v2 v2To); + static truth AutoPlayAITestValidPathTo(v2 v2To); + static truth AutoPlayAINavigateDungeon(bool bPlayerHasLantern); + static truth AutoPlayAISetAndValidateKeepGoingTo(v2 v2KGTo); + static void AutoPlayAITeleport(bool bDeathCountBased); + static void AutoPlayAIReset(bool bFailedToo); + static int GetMaxValueless(){return iMaxValueless;} + static truth AutoPlayAIequipConsumeZapReadApply(); + static truth IsPlayerAutoPlay(character* C); + +#ifdef WIZARD + static void IncAutoPlayMode(); + static int GetAutoPlayMode() { return AutoPlayMode; } + static void AutoPlayModeApply(); + static void DisableAutoPlayMode() {AutoPlayMode=AUTOPLAYMODE_DISABLED;AutoPlayModeApply();} +#else + static int GetAutoPlayMode() { return AUTOPLAYMODE_DISABLED; } +#endif + + private: + static truth IsPlayerAutoPlay(){return IsPlayerAutoPlay(P);}; + /** + * 5 seems good, broken cheap weapons, stones, very cheap weapons non broken etc + * btw, lantern price is currently 10. + */ + static int iMaxValueless; + static character* P; + static int AutoPlayMode; +}; + +#endif //__WIZAUTOPLAY_H__ diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 6962773de..9333f6c59 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1220,7 +1220,7 @@ void character::Move(v2 MoveTo, truth TeleportMove, truth Run) void character::GetAICommand() { - if(!IsPlayerAutoPlay()){ + if(!wizautoplay::IsPlayerAutoPlay(this)){ SeekLeader(GetLeader()); if(FollowLeader(GetLeader())) @@ -1512,7 +1512,7 @@ truth character::TryMove(v2 MoveVector, truth Important, truth Run, truth* pbWai { /* not sure if this is better than "the door is locked", but I guess it _might_ be slightly better */ ADD_MESSAGE("The %s is locked.", Terrain->GetNameSingular().CStr()); - if(!IsPlayerAutoPlay())return false; + if(!wizautoplay::IsPlayerAutoPlay(this))return false; } if(Important && CheckKick()) @@ -1679,34 +1679,6 @@ void character::CreateCorpse(lsquare* Square) SendToHell(); } -bool bSafePrayOnce=false; -void character::AutoPlayAITeleport(bool bDeathCountBased) -{ - bool bTeleportNow=false; - - if(bDeathCountBased){ // this is good to prevent autoplay AI getting stuck endless dieing - static int iDieMax=10; - static int iDieTeleportCountDown=iDieMax; - if(iDieTeleportCountDown==0){ //this helps on defeating not so strong enemies in spot - if(IsPlayerAutoPlay()) - bTeleportNow=true; - iDieTeleportCountDown=iDieMax; - bSafePrayOnce=true; - }else{ - static v2 v2DiePos(0,0); - if(v2DiePos==GetPos()){ - iDieTeleportCountDown--; - }else{ - v2DiePos=GetPos(); - iDieTeleportCountDown=iDieMax; - } - } - } - - if(bTeleportNow) - Move(GetLevel()->GetRandomSquare(this), true); //not using teleport function to avoid prompts, but this code is from there -} - void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) { /* Note: This function musn't delete any objects, since one of these may be @@ -1729,14 +1701,14 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) game::DrawEverything(); bool bInstaResurrect=false; - if(!bInstaResurrect && IsPlayerAutoPlay())bInstaResurrect=true; + if(!bInstaResurrect && wizautoplay::IsPlayerAutoPlay(this))bInstaResurrect=true; if(!bInstaResurrect && !game::TruthQuestion(CONST_S("Do you want to do this, cheater? [y/n]"), REQUIRES_ANSWER))bInstaResurrect=true; if(bInstaResurrect) { SaveLifeBase(); - if(IsPlayerAutoPlay()) - AutoPlayAITeleport(true); + if(wizautoplay::IsPlayerAutoPlay(this)) + wizautoplay::AutoPlayAITeleport(true); return; } @@ -2708,1004 +2680,6 @@ truth character::DodgesFlyingItem(item* Item, double ToHitValue) return !Item->EffectIsGood() && RAND() % int(100 + ToHitValue / DodgeValue * 100) < 100; } -character* AutoPlayLastChar=NULL; -const int iMaxWanderTurns=20; -const int iMinWanderTurns=3; - -/** - * 5 seems good, broken cheap weapons, stones, very cheap weapons non broken etc - * btw, lantern price is currently 10. - */ -static int iMaxValueless = 5; - -v2 v2KeepGoingTo=v2(0,0); -v2 v2TravelingToAnotherDungeon=v2(0,0); -int iWanderTurns=iMinWanderTurns; -bool bAutoPlayUseRandomNavTargetOnce=false; -std::vector vv2DebugDrawSqrPrevious; -v2 v2LastDropPlayerWasAt=v2(0,0); -std::vector vv2FailTravelToTargets; -std::vector vv2WrongGoingTo; - -void character::AutoPlayAIReset(bool bFailedToo) -{ DBG7(bFailedToo,iWanderTurns,DBGAV2(v2KeepGoingTo),DBGAV2(v2TravelingToAnotherDungeon),DBGAV2(v2LastDropPlayerWasAt),vv2FailTravelToTargets.size(),vv2DebugDrawSqrPrevious.size()); - v2KeepGoingTo=v2(0,0); //will retry - v2TravelingToAnotherDungeon=v2(0,0); - iWanderTurns=0; // warning: this other code was messing the logic ---> if(iWanderTurnsTerminateGoingTo(); - - if(bFailedToo){ - vv2FailTravelToTargets.clear(); - vv2WrongGoingTo.clear(); - } -} -truth character::AutoPlayAISetAndValidateKeepGoingTo(v2 v2KGTo) -{ - v2KeepGoingTo=v2KGTo; - - bool bOk=true; - - if(bOk){ - lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(v2KeepGoingTo); - if(!CanTheoreticallyMoveOn(lsqr)) - bOk=false; -// olterrain* olt = game::GetCurrentLevel()->GetLSquare(v2KeepGoingTo)->GetOLTerrain(); -// if(olt){ -// if(bOk && !CanMoveOn(olt)){ -// DBG4(DBGAV2(v2KeepGoingTo),"olterrain? fixing it...",olt->GetNameSingular().CStr(),PLAYER->GetPanelName().CStr()); -// bOk=false; -// } -// -// /**** -// * keep these commented for awhile, may be useful later -// * -// if(bOk && olt->IsWall()){ //TODO this may be unnecessary cuz of above test -// //TODO is this a bug in the CanMoveOn() code? navigation AI is disabled when player is ghost TODO confirm about ethereal state, ammy of phasing -// DBG4(DBGAV2(v2KeepGoingTo),"walls? fixing it...",olt->GetNameSingular().CStr(),PLAYER->GetPanelName().CStr()); -// bOk=false; -// } -// -// if(bOk && (olt->GetWalkability() & ETHEREAL)){ //TODO this may be too much unnecessary test -// bOk=false; -// } -// */ -// } - } - - if(bOk){ - SetGoingTo(v2KeepGoingTo); DBG3(DBGAV2(GetPos()),DBGAV2(GoingTo),DBGAV2(v2KeepGoingTo)); - CreateRoute(); - if(Route.empty()){ - TerminateGoingTo(); //redundant? - bOk=false; - } - } - - if(!bOk){ - DBG1("RouteCreationFailed"); - vv2FailTravelToTargets.push_back(v2KeepGoingTo); DBG3("BlockGoToDestination",DBGAV2(v2KeepGoingTo),vv2FailTravelToTargets.size()); - bAutoPlayUseRandomNavTargetOnce=true; - - AutoPlayAIReset(false); //v2KeepGoingTo is reset here too - } - - return bOk; -} - -void character::AutoPlayAIDebugDrawSquareRect(v2 v2SqrPos, col16 color, int iPrintIndex, bool bWide, bool bKeepColor) -{ - static v2 v2ScrPos=v2(0,0); //static to avoid instancing - static int iAddPos;iAddPos=bWide?2:1; - static int iSubBorder;iSubBorder=bWide?3:2; - if(game::OnScreen(v2SqrPos)){ - v2ScrPos=game::CalculateScreenCoordinates(v2SqrPos); - - DOUBLE_BUFFER->DrawRectangle( - v2ScrPos.X+iAddPos, v2ScrPos.Y+iAddPos, - v2ScrPos.X+TILE_SIZE-iSubBorder, v2ScrPos.Y+TILE_SIZE-iSubBorder, - color, bWide); - - if(iPrintIndex>-1) - FONT->Printf(DOUBLE_BUFFER, v2(v2ScrPos.X+1,v2ScrPos.Y+5), DARK_GRAY, "%d", iPrintIndex); - - if(!bKeepColor) - vv2DebugDrawSqrPrevious.push_back(v2SqrPos); - } -} - -const int iVisitAgainMax=10; -int iVisitAgainCount=iVisitAgainMax; -std::vector vv2AllDungeonSquares; -bool character::AutoPlayAICheckAreaLevelChangedAndReset() -{ - static area* areaPrevious=NULL; - area* Area = game::GetCurrentArea(); - if(Area != areaPrevious){ - areaPrevious=Area; - - iVisitAgainCount=iVisitAgainMax; - - vv2DebugDrawSqrPrevious.clear(); - - vv2AllDungeonSquares.clear(); - if(!game::IsInWilderness()) - for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ - vv2AllDungeonSquares.push_back(game::GetCurrentLevel()->GetLSquare(iX, iY)); - }} - - return true; - } - - return false; -} - -void character::AutoPlayAIDebugDrawOverlay() -{ - if(!game::WizardModeIsActive())return; - - AutoPlayAICheckAreaLevelChangedAndReset(); - - // redraw previous to clean them - area* Area = game::GetCurrentArea(); //got the Area to draw in the wilderness too and TODO navigate there one day - std::vector vv2DebugDrawSqrPreviousCopy(vv2DebugDrawSqrPrevious); - for(int i=0;iGetSquare(vv2DebugDrawSqrPrevious[i])->SendNewDrawRequest(); -// square* sqr = Area->GetSquare(vv2DebugDrawSqrPrevious[i]); -// if(sqr)sqr->SendStrongNewDrawRequest(); //TODO sqr NULL? - AutoPlayAIDebugDrawSquareRect(vv2DebugDrawSqrPreviousCopy[i],DARK_GRAY); - } - - // draw new ones - vv2DebugDrawSqrPrevious.clear(); //empty before fillup below - - for(int i=0;iRoute.empty()) - for(int i=0;iRoute.size();i++) - AutoPlayAIDebugDrawSquareRect(PLAYER->Route[i],GREEN); - - if(!v2KeepGoingTo.Is0()) - AutoPlayAIDebugDrawSquareRect(v2KeepGoingTo,BLUE,PLAYER->Route.size(),true); - else if(iWanderTurns>0) - AutoPlayAIDebugDrawSquareRect(PLAYER->GetPos(),YELLOW,iWanderTurns); - - for(int i=0;iIsAppliable(this))return false; - if(it->IsZappable(this))return false; //not here, see zap section - if(dynamic_cast(it))return false; // too complex to make it auto work - if(dynamic_cast(it))return false; // too complex to make it auto work - return true; -} - -truth character::AutoPlayAIDropThings() -{ -// level* lvl = game::GetCurrentLevel(); DBG1(lvl); -// area* Area = game::GetCurrentArea(); - - /** - * unburden - */ - bool bDropSomething = false; - static item* eqDropChk=NULL; - item* eqBroken=NULL; - for(int i=0;iIsBroken()){ DBG2("chkDropBroken",eqDropChk); - eqBroken=eqDropChk; - bDropSomething=true; - break; - } - } - - if(!bDropSomething && GetBurdenState() == STRESSED){ - if(clock()%100<5){ //5% chance to drop something weighty randomly every turn - bDropSomething=true; DBGLN; - } - } - - if(!bDropSomething && GetBurdenState() == OVER_LOADED){ - bDropSomething=true; - } - - if(bDropSomething){ DBG1("DropSomething"); - item* dropMe=NULL; - if(eqBroken!=NULL)dropMe=eqBroken; - - item* heaviest=NULL; - item* cheapest=NULL; - -// bool bFound=false; -// for(int k=0;k<2;k++){ -// if(dropMe!=NULL)break; -// static item* eqDropChk=NULL; -// for(int i=0;iIsBroken()){ -// dropMe=eqDropChk; -// break; -// } -// } - - if(dropMe==NULL){ - static itemvector vit;vit.clear();GetStack()->FillItemVector(vit); - for(int i=0;iGetName(DEFINITE).CStr(),vit[i]->GetTruePrice(),vit[i]->GetWeight()); - if(vit[i]->IsEncryptedScroll())continue; -// if(!bPlayerHasLantern && dynamic_cast(vit[i])!=NULL){ -// bPlayerHasLantern=true; //will keep only the 1st lantern -// continue; -// } - - if(vit[i]->IsBroken()){ //TODO use repair scroll? - dropMe=vit[i]; - break; - } - - if(heaviest==NULL)heaviest=vit[i]; - if(cheapest==NULL)cheapest=vit[i]; - -// switch(k){ -// case 0: //better not implement this as a user function as that will remove the doubt about items values what is another fun challenge :) - if(vit[i]->GetTruePrice() < cheapest->GetTruePrice()) //cheapest - cheapest=vit[i]; -// break; -// case 1: //this could be added as user function to avoid browsing the drop list, but may not be that good... - if(vit[i]->GetWeight() > heaviest->GetWeight()) //heaviest - heaviest=vit[i]; -// break; -// } - } - } - - if(heaviest!=NULL && cheapest!=NULL){ - if(dropMe==NULL && heaviest==cheapest) - dropMe=heaviest; - - if(dropMe==NULL && cheapest->GetTruePrice()<=iMaxValueless){ DBG2("DropValueless",cheapest->GetName(DEFINITE).CStr()); - dropMe=cheapest; - } - - if(dropMe==NULL){ - // the worst price VS weight will be dropped - float fC = cheapest ->GetTruePrice()/(float)cheapest ->GetWeight(); - float fW = heaviest->GetTruePrice()/(float)heaviest->GetWeight(); DBG3("PriceVsWeightRatio",fC,fW); - if(fC < fW){ - dropMe = cheapest; - }else{ - dropMe = heaviest; - } - } - - if(dropMe==NULL) - dropMe = clock()%2==0 ? heaviest : cheapest; - } - - // chose a throw direction - if(dropMe!=NULL){ - static std::vector vv2DirBase;static bool bDummyInit = [](){for(int i=0;i<8;i++)vv2DirBase.push_back(i);return true;}(); - std::vector vv2Dir(vv2DirBase); - int iDirOk=-1; - v2 v2DropAt(0,0); - lsquare* lsqrDropAt=NULL; - for(int i=0;i<8;i++){ - int k = clock()%vv2Dir.size(); //random chose from remaining TODO could be where there is NPC foe - int iDir = vv2Dir[k]; //collect direction value - vv2Dir.erase(vv2Dir.begin() + k); //remove using the chosen index to prepare next random choice - - v2 v2Dir = game::GetMoveVector(iDir); - v2 v2Chk = GetPos() + v2Dir; - if(game::GetCurrentLevel()->IsValidPos(v2Chk)){ - lsquare* lsqrChk=game::GetCurrentLevel()->GetLSquare(v2Chk); - if(lsqrChk->IsFlyable()){ - iDirOk = iDir; - v2DropAt = v2Chk; - lsqrDropAt=lsqrChk; - break; - } - } - };DBGLN; - - if(iDirOk==-1){iDirOk=clock()%8;DBG2("RandomDir",iDirOk);}DBGLN; //TODO should just drop may be? unless hitting w/e is there could help - - bool bApplyDropped=false; //or vanished - if(AutoPlayAIcanApply(dropMe) && dropMe->Apply(this)){ - static itemvector ivChkDrop;ivChkDrop.clear(); - GetStack()->FillItemVector(ivChkDrop); - bApplyDropped=true; - for(int i6=0;i6-1){DBG2("KickOrThrow",iDirOk); - static itemcontainer* itc;itc = dynamic_cast(dropMe);DBGLN; - static humanoid* h;h = dynamic_cast(this);DBGLN; - DBG8("CanKickLockedChest",lsqrDropAt,itc,itc?itc->IsLocked():-1,CanKick(),h,h?h->GetLeftLeg():0,h?h->GetRightLeg():0); - if(lsqrDropAt && itc && itc->IsLocked() && CanKick() && h && h->GetLeftLeg() && h->GetRightLeg()){DBGLN; - dropMe->MoveTo(lsqrDropAt->GetStack());DBGLN; //drop in front.. - Kick(lsqrDropAt,iDirOk,true);DBGLN; // ..to kick it - }else{DBGLN; - ThrowItem(iDirOk, dropMe); DBG5("DropThrow",iDirOk,dropMe->GetName(DEFINITE).CStr(),dropMe->GetTruePrice(),dropMe->GetWeight()); - } - }else{DBGLN; - dropMe->MoveTo(GetLSquareUnder()->GetStack());DBGLN; //just drop - } - - v2LastDropPlayerWasAt=GetPos();DBGSV2(v2LastDropPlayerWasAt); - } - - return true; - } - - DBG1("AutoPlayNeedsImprovement:DropItem"); - ADD_MESSAGE("%s says \"I need more intelligence to drop trash...\"", CHAR_NAME(DEFINITE)); // improve the dropping AI - //TODO stop autoplay mode? if not, something random may happen some time and wont reach here ex.: spoil, fire, etc.. - } - - return false; -} - -bool character::IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern) -{ - if(!it->CanBeSeenBy(this))return false; - ValidateTrapData(); - if(!it->IsPickable(this))return false; - if(it->GetSquaresUnder()!=1)return false; //avoid big corpses 2x2 - - if(!bPlayerHasLantern && (it->IsOnFire(this) || it->GetEmitation()>0)){ - //pickup priority - }else{ - if(it->IsBroken())return false; - if(it->GetTruePrice()<=iMaxValueless)return false; //mainly to avoid all rocks from broken walls - if(clock()%3!=0 && it->GetSpoilLevel()>0)return false; //some spoiled may be consumed to randomly test diseases flows - } - - return true; -} - -truth character::AutoPlayAIEquipAndPickup(bool bPlayerHasLantern) -{ - static humanoid* h;h = dynamic_cast(this); - if(h==NULL)return false; - // other invalid equippers - if(dynamic_cast(this) != NULL)return false; - if(dynamic_cast(this) != NULL)return false; - - if(h->AutoPlayAIequipConsumeZapReadApply()) - return true; - - if(GetBurdenState()!=OVER_LOADED){ DBG4(CommandFlags&DONT_CHANGE_EQUIPMENT,this,GetNameSingular().CStr(),GetSquareUnder()); - if(v2LastDropPlayerWasAt!=GetPos()){ - static bool bHoarder=true; //TODO wizard autoplay AI config exclusive felist - - if(CheckForUsefulItemsOnGround(false)) - if(!bHoarder) - return true; - - //just pick up any useful stuff - static itemvector vit;vit.clear();GetStackUnder()->FillItemVector(vit); - for(uint c = 0; c < vit.size(); ++c){ - if(!IsAutoplayAICanPickup(vit[c],bPlayerHasLantern))continue; - - static itemcontainer* itc;itc = dynamic_cast(vit[c]); - if(itc && !itc->IsLocked()){ //get items from unlocked container - static itemvector vitItc;vitItc.clear();itc->GetContained()->FillItemVector(vitItc); - for(uint d = 0; d < vitItc.size(); ++d) - vitItc[d]->MoveTo(itc->GetLSquareUnder()->GetStack()); - continue; - } - - vit[c]->MoveTo(GetStack()); DBG2("pickup",vit[c]->GetNameSingular().CStr()); -// if(GetBurdenState()==OVER_LOADED)ThrowItem(clock()%8,ItemVector[c]); -// return true; - if(!bHoarder) - return true; - } - } - } - - return false; -} - -static const int iMoreThanMaxDist=10000000; //TODO should be max integer but this will do for now in 2018 :) -truth character::AutoPlayAITestValidPathTo(v2 v2To) -{ - return AutoPlayAIFindWalkDist(v2To) < iMoreThanMaxDist; -} - -int character::AutoPlayAIFindWalkDist(v2 v2To) -{ - static bool bUseSimpleDirectDist=false; //very bad navigation this is - if(bUseSimpleDirectDist)return (v2To - GetPos()).GetLengthSquare(); - - static v2 GoingToBkp;GoingToBkp = GoingTo; //IsGoingSomeWhere() ? GoingTo : v2(0,0); - - SetGoingTo(v2To); - CreateRoute(); - static int iDist;iDist=Route.size(); - TerminateGoingTo(); - - if(GoingToBkp!=ERROR_V2){ DBG2("Warning:WrongUsage:ShouldBeGoingNoWhere",DBGAV2(GoingToBkp)); - SetGoingTo(GoingToBkp); - CreateRoute(); - } - - return iDist>0?iDist:iMoreThanMaxDist; -} - -const int iDesperateResetCountDownDefault=5; -const int iDesperateEarthQuakeCountDownDefault=iDesperateResetCountDownDefault*2; -const int iAutoPlayAIResetCountDownDefault = iDesperateEarthQuakeCountDownDefault*2; -int iAutoPlayAIResetCountDown = iAutoPlayAIResetCountDownDefault; -truth character::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) -{ - /** - * navigate the unknown dungeon - */ - festring fsDL;fsDL<GetIndex()<GetIndex(); - festring fsStayOnDL;{const char* pc = std::getenv("IVAN_DebugStayOnDungeonLevel");if(pc!=NULL)fsStayOnDL< v2Exits; - std::vector v2Altars; - if(v2KeepGoingTo.Is0()){ DBG1("TryNewMoveTarget"); - // target undiscovered squares to explore - v2 v2PreferedTarget(0,0); - - int iNearestLanterOnFloorDist = iMoreThanMaxDist; - v2 v2PreferedLanternOnFloorTarget(0,0); - - v2 v2NearestUndiscovered(0,0); - int iNearestUndiscoveredDist=iMoreThanMaxDist; - std::vector vv2UndiscoveredValidPathSquares; - - lsquare* lsqrNearestSquareWithWallLantern=NULL; - lsquare* lsqrNearestDropWallLanternAt=NULL; - stack* stkNearestDropWallLanternAt = NULL; - int iNearestSquareWithWallLanternDist=iMoreThanMaxDist; - item* itNearestWallLantern=NULL; - - /*************************************************************** - * scan whole dungeon squares - */ - for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ - lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(iX,iY); - - olterrain* olt = lsqr->GetOLTerrain(); - if(olt && (olt->GetConfig() == STAIRS_UP || olt->GetConfig() == STAIRS_DOWN)){ - if(fsDL!=fsStayOnDL){ - v2Exits.push_back(v2(lsqr->GetPos())); - DBGSV2(v2Exits[v2Exits.size()-1]); - } - } - - altar* Altar = dynamic_cast(olt); - if(olt && Altar){ - if(!Altar->GetMasterGod()->IsKnown()) - v2Altars.push_back(v2(lsqr->GetPos())); - } - - stack* stkSqr = lsqr->GetStack(); - static itemvector vit;vit.clear();stkSqr->FillItemVector(vit); - bool bAddValidTargetSquare=true; - - // find nearest wall lantern - if(!bPlayerHasLantern && olt && olt->IsWall()){ - for(int n=0;nIsLanternOnWall() && !vit[n]->IsBroken()){ - static stack* stkDropWallLanternAt;stkDropWallLanternAt = lsqr->GetStackOfAdjacentSquare(vit[n]->GetSquarePosition()); - static lsquare* lsqrDropWallLanternAt;lsqrDropWallLanternAt = - stkDropWallLanternAt?stkDropWallLanternAt->GetLSquareUnder():NULL; - - if(stkDropWallLanternAt && lsqrDropWallLanternAt && CanTheoreticallyMoveOn(lsqrDropWallLanternAt)){ - int iDist = AutoPlayAIFindWalkDist(lsqrDropWallLanternAt->GetPos()); //(lsqr->GetPos() - GetPos()).GetLengthSquare(); - if(lsqrNearestSquareWithWallLantern==NULL || iDistGetPos()),DBGAV2(GetPos())); - lsqrNearestDropWallLanternAt=lsqrDropWallLanternAt; - stkNearestDropWallLanternAt=stkDropWallLanternAt; - } - } - - break; - } - } - } - - if(bAddValidTargetSquare && !CanTheoreticallyMoveOn(lsqr)) - bAddValidTargetSquare=false; - - bool bIsFailToTravelSquare=false; - if(bAddValidTargetSquare){ - for(int j=0;jGetPos()){ - bAddValidTargetSquare=false; - bIsFailToTravelSquare=true; - break; - } - } - - if(!bIsFailToTravelSquare){ - -// if(bAddValidTargetSquare && v2PreferedTarget.Is0() && (lsqr->HasBeenSeen() || !bPlayerHasLantern)){ - if(bAddValidTargetSquare && (lsqr->HasBeenSeen() || !bPlayerHasLantern)){ - bool bVisitAgain=false; - if(iVisitAgainCount>0 || !bPlayerHasLantern){ - if(stkSqr!=NULL && stkSqr->GetItems()>0){ - for(int n=0;nGetID());DBG1(vit[n]->GetType());DBG3("VisitAgain:ChkItem",vit[n]->GetNameSingular().CStr(),vit.size()); - if(vit[n]->IsBroken())continue; DBGLN; - - static bool bIsLanternOnFloor;bIsLanternOnFloor = dynamic_cast(vit[n])!=NULL;// || vit[n]->IsOnFire(this); DBGLN; - - if( // if is useful to the AutoPlay AI endless tests - vit[n]->IsShield (this) || - vit[n]->IsWeapon (this) || - vit[n]->IsArmor (this) || - vit[n]->IsAmulet (this) || - vit[n]->IsZappable(this) || //wands - vit[n]->IsAppliable(this) || //mines, beartraps etc - vit[n]->IsRing (this) || - vit[n]->IsReadable(this) || //books and scrolls - vit[n]->IsDrinkable(this)|| //potions and vials - bIsLanternOnFloor - ) - if(IsAutoplayAICanPickup(vit[n],bPlayerHasLantern)) - { - bVisitAgain=true; - - if(bIsLanternOnFloor && !bPlayerHasLantern){ - static int iDist;iDist = AutoPlayAIFindWalkDist(lsqr->GetPos()); //(lsqr->GetPos() - GetPos()).GetLengthSquare(); - if(iDistGetPos(); DBG2("PreferLanternAt",DBGAV2(lsqr->GetPos())) - } - }else{ - iVisitAgainCount--; - } - - DBG4(bVisitAgain,DBGAV2(lsqr->GetPos()),iVisitAgainCount,bIsLanternOnFloor); - break; - } - } - } - } - - if(!bVisitAgain)bAddValidTargetSquare=false; - } - - } - - if(bAddValidTargetSquare) - if(!CanTheoreticallyMoveOn(lsqr)) //if(olt && !CanMoveOn(olt)) - bAddValidTargetSquare=false; - - if(bAddValidTargetSquare){ DBG2("addValidSqr",DBGAV2(lsqr->GetPos())); - static int iDist;iDist=AutoPlayAIFindWalkDist(lsqr->GetPos()); //(lsqr->GetPos() - GetPos()).GetLengthSquare(); - - if(iDistGetPos()); - - if(iDistGetPos(); DBG3(iNearestUndiscoveredDist,DBGAV2(lsqr->GetPos()),DBGAV2(GetPos())); - } - } - }} DBG2(DBGAV2(v2PreferedTarget),vv2UndiscoveredValidPathSquares.size()); - - /*************************************************************** - * define prefered navigation target - */ - if(!bPlayerHasLantern && v2PreferedTarget.Is0()){ - bool bUseWallLantern=false; - if(!v2PreferedLanternOnFloorTarget.Is0() && lsqrNearestSquareWithWallLantern!=NULL){ - if(iNearestLanterOnFloorDist <= iNearestSquareWithWallLanternDist){ - v2PreferedTarget=v2PreferedLanternOnFloorTarget; - }else{ - bUseWallLantern=true; - } - }else if(!v2PreferedLanternOnFloorTarget.Is0()){ - v2PreferedTarget=v2PreferedLanternOnFloorTarget; - }else if(lsqrNearestSquareWithWallLantern!=NULL){ - bUseWallLantern=true; - } - - if(bUseWallLantern){ - /** - * target to nearest wall lantern - * check for lanterns on walls of adjacent squares if none found on floors - */ - itNearestWallLantern->MoveTo(stkNearestDropWallLanternAt); // the AI is prepared to get things from the floor only so "magically" drop it :) - v2PreferedTarget = lsqrNearestDropWallLanternAt->GetPos(); DBG2("PreferWallLanternAt",DBGAV2(lsqrNearestDropWallLanternAt->GetPos())) - } - - } - - /*************************************************************** - * validate and set new navigation target - */ -// DBG9("AllNavigatePossibilities",DBGAV2(v2PreferedTarget),DBGAV2(v2PreferedLanternOnFloorTarget),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2()); - v2 v2NewKGTo=v2(0,0); - - if(v2NewKGTo.Is0()){ - //TODO if(!v2PreferedTarget.Is0){ // how can this not be compiled? error: cannot convert ‘v2::Is0’ from type ‘truth (v2::)() const {aka bool (v2::)() const}’ to type ‘bool’ - if(v2PreferedTarget.GetLengthSquare()>0) - if(AutoPlayAITestValidPathTo(v2PreferedTarget)) - v2NewKGTo=v2PreferedTarget; DBGSV2(v2PreferedTarget); - } - - if(v2NewKGTo.Is0()){ - if(bAutoPlayUseRandomNavTargetOnce){ //these targets were already path validated and are safe to use! - v2NewKGTo=vv2UndiscoveredValidPathSquares[clock()%vv2UndiscoveredValidPathSquares.size()]; DBG2("RandomTarget",DBGAV2(v2NewKGTo)); - bAutoPlayUseRandomNavTargetOnce=false; - }else{ //find nearest - if(!v2NearestUndiscovered.Is0()){ - v2NewKGTo=v2NearestUndiscovered; DBGSV2(v2NearestUndiscovered); - } - } - } - - if(v2NewKGTo.Is0()){ //no new destination: fully explored - if(v2Altars.size()>0){ - for(int i=0;i0){ - if(game::GetCurrentDungeonTurnsCount()==0){ DBG1("Dungeon:FullyExplored:FirstTurn"); - iWanderTurns=100+clock()%300; DBG2("WanderALotOnFullyExploredLevel",iWanderTurns); //just move around a lot, some NPC may spawn - }else{ - // travel between dungeons if current fully explored - v2 v2Try; - for(int i=0;i<10;i++){ - v2Try = v2Exits[clock()%v2Exits.size()]; - if(v2Try!=GetPos())break; - } - if(AutoPlayAITestValidPathTo(v2Try) || iAutoPlayAIResetCountDown==0) - v2NewKGTo = v2TravelingToAnotherDungeon = v2Try; DBGSV2(v2TravelingToAnotherDungeon); - } - }else{ - DBG1("AutoPlayNeedsImprovement:Navigation") - ADD_MESSAGE("%s says \"I need more intelligence to move around...\"", CHAR_NAME(DEFINITE)); // improve the dropping AI - //TODO stop autoplay mode? - } - } - - if(v2NewKGTo.Is0()){ DBG1("Desperately:TryAnyRandomTargetNavWithValidPath"); - std::vector vlsqrChk(vv2AllDungeonSquares); - - while(vlsqrChk.size()>0){ - static int i;i=clock()%vlsqrChk.size(); - static v2 v2Chk; v2Chk = vlsqrChk[i]->GetPos(); - - if(!AutoPlayAITestValidPathTo(v2Chk)){ - vlsqrChk.erase(vlsqrChk.begin()+i); - }else{ - v2NewKGTo=v2Chk; - break; - } - } - } - - if(!v2NewKGTo.Is0()){ - AutoPlayAISetAndValidateKeepGoingTo(v2NewKGTo); - }else{ - DBG1("TODO:too complex paths are failing... improve CreateRoute()?"); - } - } - - if(!v2KeepGoingTo.Is0()){ - if(v2KeepGoingTo==GetPos()){ DBG3("ReachedDestination",DBGAV2(v2KeepGoingTo),DBGAV2(GoingTo)); - //wander a bit before following new target destination - iWanderTurns=(clock()%iMaxWanderTurns)+iMinWanderTurns; DBG2("WanderAroundAtReachedDestination",iWanderTurns); - -// v2KeepGoingTo=v2(0,0); -// TerminateGoingTo(); - AutoPlayAIReset(false); - return true; - } - -// CheckForUsefulItemsOnGround(false); DBGSV2(GoingTo); -// CheckForEnemies(false, true, false, false); DBGSV2(GoingTo); - -// if(!IsGoingSomeWhere() || v2KeepGoingTo!=GoingTo){ DBG3("ForceKeepGoingTo",DBGAV2(v2KeepGoingTo),DBGAV2(GoingTo)); -// SetGoingTo(v2KeepGoingTo); -// } - static int iForceGoingToCountDown=10; - static v2 v2GoingToBkp;v2GoingToBkp=GoingTo; - if(!v2KeepGoingTo.IsAdjacent(GoingTo)){ - if(iForceGoingToCountDown==0){ - DBG4("ForceKeepGoingTo",DBGAV2(v2KeepGoingTo),DBGAV2(GoingTo),DBGAV2(GetPos())); - - if(!AutoPlayAISetAndValidateKeepGoingTo(v2KeepGoingTo)){ - static int iSetFailTeleportCountDown=10; - iSetFailTeleportCountDown--; - vv2WrongGoingTo.push_back(v2GoingToBkp); - if(iSetFailTeleportCountDown==0){ - AutoPlayAITeleport(false); - AutoPlayAIReset(true); //refresh to test/try it all again - iSetFailTeleportCountDown=10; - } - } - DBGSV2(GoingTo); - return true; - }else{ - iForceGoingToCountDown--; DBG1(iForceGoingToCountDown); - } - }else{ - iForceGoingToCountDown=10; - } - - /** - * Determinedly blindly moves towards target, the goal is to Navigate! - * - * this has several possible status if returning false... - * so better do not decide anything based on it? - */ - MoveTowardsTarget(false); - -// if(!MoveTowardsTarget(false)){ DBG3("OrFailedGoingTo,OrReachedDestination...",DBGAV2(GoingTo),DBGAV2(GetPos())); // MoveTowardsTarget may break the GoingTo EVEN if it succeeds????? -// TerminateGoingTo(); -// v2KeepGoingTo=v2(0,0); //reset only this one to try again -// GetAICommand(); //wander once for randomicity -// } - - return true; - } - - return false; -} - -bool character::AutoPlayAIChkInconsistency() -{ - if(GetSquareUnder()==NULL){ - DBG9(this,GetNameSingular().CStr(),IsPolymorphed(),IsHuman(),IsHumanoid(),IsPolymorphable(),IsPlayerKind(),IsTemporary(),IsPet()); - DBG6("GetSquareUnderIsNULLhow?",IsHeadless(),IsPlayer(),game::GetAutoPlayMode(),IsPlayerAutoPlay(),GetName(DEFINITE).CStr()); - return true; //to just ignore this turn expecting on next it will be ok. - } - return false; -} - -truth character::AutoPlayAIPray() -{ - bool bSPO = bSafePrayOnce; - bSafePrayOnce=false; - - if(bSPO){} - else if(StateIsActivated(PANIC) && clock()%10==0){ - iWanderTurns=1; DBG1("Wandering:InPanic"); // to regain control as soon it is a ghost anymore as it can break navigation when inside walls - }else return false; - - // check for known gods - int aiKGods[GODS]; - int iKGTot=0; - int aiKGodsP[GODS]; - int iKGTotP=0; - static int iPleased=50; //see god::PrintRelation() - for(int c = 1; c <= GODS; ++c){ - if(!game::GetGod(c)->IsKnown())continue; - // even known, praying to these extreme ones will be messy if Relation<1000 - if(dynamic_cast(game::GetGod(c))!=NULL && game::GetGod(c)->GetRelation()<1000)continue; - if(dynamic_cast(game::GetGod(c))!=NULL && game::GetGod(c)->GetRelation()<1000)continue; - - aiKGods[iKGTot++]=c; - - if(game::GetGod(c)->GetRelation() > iPleased){ -// //TODO could this help? -// switch(game::GetGod(c)->GetBasicAlignment()){ //game::GetGod(c)->GetAlignment(); -// case GOOD: -// if(game::GetPlayerAlignment()>=2){}else continue; -// break; -// case NEUTRAL: -// if(game::GetPlayerAlignment()<2 && game::GetPlayerAlignment()>-2){}else continue; -// break; -// case EVIL: -// if(game::GetPlayerAlignment()<=-2){}else continue; -// break; -// } - aiKGodsP[iKGTotP++] = c; - } - } - if(iKGTot==0){ - if(clock()%10==0) - for(int c = 1; c <= GODS; ++c) - game::LearnAbout(game::GetGod(c)); - return false; - } -// if(bSPO && iKGTotP==0)return false; - - // chose and pray to one god - god* g = NULL; - if(iKGTotP>0 && (bSPO || clock()%10!=0)) - g = game::GetGod(aiKGodsP[clock()%iKGTotP]); //only good effect - else - g = game::GetGod(aiKGods[clock()%iKGTot]); //can have bad effect too - - if(bSPO || clock()%10!=0){ //it may not recover some times to let pray unsafely - int iRecover=0; - if(iKGTotP==0){ - if(iRecover==0 && g->GetRelation()==-1000)iRecover=1000; //to test all relation range - if(iRecover==0 && g->GetRelation() <= iPleased)iRecover=iPleased; //to alternate tests on many with low good relation - } - if(iRecover>0) - g->SetRelation(iRecover); - - g->AdjustTimer(-1000000000); //TODO filter gods using timer too instead of this reset? - } - - g->Pray(); DBG2("PrayingTo",g->GetName()); - - return true; -} - -truth character::AutoPlayAICommand(int& rKey) -{ - DBGLN;if(AutoPlayAIChkInconsistency())return true; - DBGSV2(GetPos()); - - if(AutoPlayLastChar!=this){ - AutoPlayAIReset(true); - AutoPlayLastChar=this; - } - - DBGLN;if(AutoPlayAIChkInconsistency())return true; - if(AutoPlayAICheckAreaLevelChangedAndReset()) - AutoPlayAIReset(true); - - static bool bDummy_initDbg = [](){game::AddDebugDrawOverlayFunction(&AutoPlayAIDebugDrawOverlay);return true;}(); - - truth bPlayerHasLantern=false; - static itemvector vit;vit.clear();GetStack()->FillItemVector(vit); - for(uint i=0;i(vit[i])!=NULL || vit[i]->IsOnFire(this) || vit[i]->GetEmitation()>0){ - bPlayerHasLantern=true; //will keep only the 1st lantern - break; - } - } - - DBGLN;if(AutoPlayAIChkInconsistency())return true; - AutoPlayAIPray(); - - //TODO this doesnt work??? -> if(IsPolymorphed()){ //to avoid some issues TODO but could just check if is a ghost -// if(dynamic_cast(this) == NULL){ //this avoid some issues TODO but could just check if is a ghost -// if(StateIsActivated(ETHEREAL_MOVING)){ //this avoid many issues - static bool bPreviousTurnWasGhost=false; - if(dynamic_cast(this) != NULL){ DBG1("Wandering:Ghost"); //this avoid many issues mainly related to navigation - iWanderTurns=1; // to regain control as soon it is a ghost anymore as it can break navigation when inside walls - bPreviousTurnWasGhost=true; - }else{ - if(bPreviousTurnWasGhost){ - AutoPlayAIReset(true); //this may help on navigation - bPreviousTurnWasGhost=false; - return true; - } - } - - DBGLN;if(AutoPlayAIChkInconsistency())return true; - if(AutoPlayAIDropThings()) - return true; - - DBGLN;if(AutoPlayAIChkInconsistency())return true; - if(AutoPlayAIEquipAndPickup(bPlayerHasLantern)) - return true; - - if(iWanderTurns>0){ - if(!IsPlayer() || game::GetAutoPlayMode()==AUTOPLAYMODE_DISABLED || !IsPlayerAutoPlay()){ //redundancy: yep - DBG9(this,GetNameSingular().CStr(),IsPolymorphed(),IsHuman(),IsHumanoid(),IsPolymorphable(),IsPlayerKind(),IsTemporary(),IsPet()); - DBG5(IsHeadless(),IsPlayer(),game::GetAutoPlayMode(),IsPlayerAutoPlay(),GetName(DEFINITE).CStr()); - ABORT("autoplay is inconsistent %d %d %d %d %d %s %d %s %d %d %d %d %d", - IsPolymorphed(),IsHuman(),IsHumanoid(),IsPolymorphable(),IsPlayerKind(), - GetNameSingular().CStr(),game::GetAutoPlayMode(),GetName(DEFINITE).CStr(), - IsTemporary(),IsPet(),IsHeadless(),IsPlayer(),IsPlayerAutoPlay()); - } - GetAICommand(); DBG2("Wandering",iWanderTurns); //fallback to default TODO never reached? - iWanderTurns--; - return true; - } - - /*************************************************************************************************** - * WANDER above here - * NAVIGATE below here - ***************************************************************************************************/ - - /** - * travel between dungeons - */ - bool bTBD = false; - if(!v2TravelingToAnotherDungeon.Is0()){ - if(GetPos() == v2TravelingToAnotherDungeon) - bTBD=true; - else - if(iAutoPlayAIResetCountDown==0){ - Move(v2TravelingToAnotherDungeon,true); - iAutoPlayAIResetCountDown=iAutoPlayAIResetCountDownDefault; - bTBD=true; - } - } - if(bTBD){ - bool bTravel=false; - lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(v2TravelingToAnotherDungeon); -// square* sqr = Area->GetSquare(v2TravelingToAnotherDungeon); - olterrain* ot = lsqr->GetOLTerrain(); -// oterrain* ot = sqr->GetOTerrain(); - if(ot){ - if(ot->GetConfig() == STAIRS_UP){ - rKey='<'; - bTravel=true; - } - - if(ot->GetConfig() == STAIRS_DOWN){ - rKey='>'; - bTravel=true; - } - } - - if(bTravel){ DBG3("travel",DBGAV2(v2TravelingToAnotherDungeon),rKey); - AutoPlayAIReset(true); - return false; //so the new/changed key will be used as command, otherwise it would be ignored - } - } - - static int iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; - if(AutoPlayAINavigateDungeon(bPlayerHasLantern)){ - iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; - return true; - }else{ - if(iDesperateEarthQuakeCountDown==0){ - iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; - /** - * this changes the dungeon level paths, - * so applying pickaxe or using fireballs etc are not required! - */ - scrollofearthquake::Spawn()->FinishReading(this); - DBG1("UsingTerribleEarthquakeSolution"); // xD - }else{ - iDesperateEarthQuakeCountDown--; - DBG1(iDesperateEarthQuakeCountDown); - } - } - - /**************************************** - * Twighlight zone - */ - - ADD_MESSAGE("%s says \"I need more intelligence to do things by myself...\"", CHAR_NAME(DEFINITE)); DBG1("TODO: AI needs improvement"); - - static int iDesperateResetCountDown=iDesperateResetCountDownDefault; - if(iDesperateResetCountDown==0){ - iDesperateResetCountDown=iDesperateResetCountDownDefault; - - AutoPlayAIReset(true); - iAutoPlayAIResetCountDown--; - - // AFTER THE RESET!!! - iWanderTurns=iMaxWanderTurns; DBG2("DesperateResetToSeeIfAIWorksAgain",iWanderTurns); - }else{ - GetAICommand(); DBG2("WanderingDesperatelyNotKnowingWhatToDo",iDesperateResetCountDown); // :) - iDesperateResetCountDown--; - } - - return true; -} - void character::GetPlayerCommand() { truth HasActed = false; @@ -3751,29 +2725,7 @@ void character::GetPlayerCommand() int c; #ifdef WIZARD - if(IsPlayerAutoPlay()){ - bool bForceStop = false; - if(game::GetAutoPlayMode()>=AUTOPLAYMODE_SLOW) - bForceStop = globalwindowhandler::IsKeyPressed(SDL_SCANCODE_ESCAPE); - - if(!bForceStop && Key=='.'){ // pressed or simulated - if(game::IsInWilderness()){ - Key='>'; //blindly tries to go back to the dungeon safety :) TODO target and move to other dungeons/towns in the wilderness - }else{ - HasActed = AutoPlayAICommand(Key); DBG2("Simulated",Key); - if(HasActed)ValidKeyPressed = true; //valid simulated action - } - }else{ - /** - * if the user hits any key during the autoplay mode that runs by itself, it will be disabled. - * at non auto mode, can be moved around but cannot rest or will move by itself - */ - if(game::GetAutoPlayMode()>=AUTOPLAYMODE_SLOW && (Key!='~' || bForceStop)){ - game::DisableAutoPlayMode(); - AutoPlayAIReset(true); // this will help on re-randomizing things, mainly paths - } - } - } + wizautoplay::AutoPlayCommandKey(this,Key,HasActed,ValidKeyPressed); #endif if(!HasActed){ @@ -4673,10 +3625,10 @@ truth character::IsAboveUsefulItem() ) ) || (bTooCheap && - (vit[i]->GetTruePrice() > iMaxValueless) + (vit[i]->GetTruePrice() > wizautoplay::GetMaxValueless()) ) || (bEncumbering && //calc in float price vs weight - (vit[i]->GetTruePrice()/(vit[i]->GetWeight()/1000.0)) > (iMaxValueless*2) + (vit[i]->GetTruePrice()/(vit[i]->GetWeight()/1000.0)) > (wizautoplay::GetMaxValueless()*2) ) ){ return true; @@ -5056,8 +4008,8 @@ void character::TeleportRandomly(truth Intentional) if(GetAction() && GetAction()->IsVoluntary()) GetAction()->Terminate(false); - if(IsPlayerAutoPlay()) - AutoPlayAIReset(true); + if(wizautoplay::IsPlayerAutoPlay(this)) + wizautoplay::AutoPlayAIReset(true); // There's a small chance that some warp gas/fluid is left behind. if(FromSquare->IsFlyable() && !RAND_N(1000)) @@ -5069,14 +4021,9 @@ void character::TeleportRandomly(truth Intentional) } } -truth character::IsPlayerAutoPlay() -{ - return IsPlayer() && game::GetAutoPlayMode()>0; -} - void character::DoDetecting() { - if(IsPlayerAutoPlay() || !IsPlayer()) + if(wizautoplay::IsPlayerAutoPlay(this) || !IsPlayer()) return; material* TempMaterial; @@ -6909,7 +5856,7 @@ void character::LycanthropyHandler() void character::SaveLifeBase() { - if(IsPlayer() && !IsPlayerAutoPlay()) + if(IsPlayer() && !wizautoplay::IsPlayerAutoPlay(this)) game::AskForKeyPress(CONST_S("Life saved! [press any key to continue]")); RestoreBodyParts(); @@ -6987,7 +5934,7 @@ character* character::PolymorphRandomly(int MinDanger, int MaxDanger, int Time) if(StateIsActivated(POLYMORPH_CONTROL)) {DBGLN; - if(IsPlayer() && !IsPlayerAutoPlay()) + if(IsPlayer() && !wizautoplay::IsPlayerAutoPlay(this)) {DBGLN; if(!GetNewFormForPolymorphWithControl(NewForm)){DBG1(NewForm); return NULL; @@ -9386,7 +8333,7 @@ void character::ResetStates() && TemporaryStateIsActivated(1 << c) && - (IsPlayerAutoPlay() || TemporaryStateCounter[c] != PERMANENT) //autoplay will be messed if not removing some things like leprosy or worms + (wizautoplay::IsPlayerAutoPlay(this) || TemporaryStateCounter[c] != PERMANENT) //autoplay will be messed if not removing some things like leprosy or worms ){ TemporaryState &= ~(1 << c); diff --git a/Main/Source/charset.cpp b/Main/Source/charset.cpp index dd0c34b9e..d3ecf9571 100644 --- a/Main/Source/charset.cpp +++ b/Main/Source/charset.cpp @@ -57,8 +57,10 @@ EXTENDED_SYSTEM_SPECIALIZATIONS(character)(0, 0, 0, "character"); #include "team.h" #include "traps.h" #include "whandler.h" +#include "wizautoplay.h" #include "wsquare.h" #include "team.cpp" #include "char.cpp" #include "curseddeveloper.cpp" +#include "wizautoplay.cpp" diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index a8e90449c..a90dbff88 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2020,8 +2020,20 @@ struct srpInspect : public recipe{ //TODO this is instantaneous, should take tim fs<GetName(UNARTICLED); } fs<<"."; + if(matM||matS){ ADD_MESSAGE("%s",fs.CStr()); + itemvector v; + rpd.rc.H()->GetStack()->FillItemVector(v); + if(!it0->HasTag('m')){ //material info transfered to item from + for(int i=0;i(v[i])){ + it0->SetTag('m'); + it0->SetLabel(it0->GetLabel()+"s"+matM->GetStrengthValue()+"f"+matM->GetFlexibility()); + } + break; + } + } craftcore::CraftSkillAdvance(rpd); }else{ ADD_MESSAGE("You can't inspect %s.",it0->GetName(INDEFINITE).CStr()); diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 462212a2e..e29ce39b8 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -38,6 +38,7 @@ #include "stack.h" #include "team.h" #include "whandler.h" +#include "wizautoplay.h" #include "worldmap.h" #include "wsquare.h" #include "wterras.h" @@ -868,8 +869,8 @@ truth commandsystem::Read(character* Char) #ifdef WIZARD // stops auto question timeout that was preventing reading at all - if(Item && game::GetAutoPlayMode()) - game::DisableAutoPlayMode(); + if(Item && wizautoplay::GetAutoPlayMode()) + wizautoplay::DisableAutoPlayMode(); #endif return Item && Char->ReadItem(Item); @@ -2126,7 +2127,7 @@ truth commandsystem::WizardMode(character* Char) truth commandsystem::AutoPlay(character* Char) { - game::IncAutoPlayMode(); + wizautoplay::IncAutoPlayMode(); return false; } diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index a1c9afef2..817221e90 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -35,7 +35,7 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) character* P = game::GetPlayer(); game::DrawEverything(); - int iBuff,iDebuff; + int iBuff=0,iDebuff=0; bool bRev; bool bStay = BuffAndDebuffPlayerKiller(Killer,iBuff,iDebuff,bRev); //to spice it up if(!bStay) @@ -107,7 +107,7 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in if(!bCursedDeveloper)return true; if(!Killer)return true; - riBuff=0; + riBuff=1; riDebuff=0; rbRev=false; @@ -140,9 +140,9 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in if(!Killer->HasStateFlag(TELEPORT_LOCK)){Killer->GainIntrinsic(TELEPORT_LOCK);return false;} riBuff++; if(!Killer->HasStateFlag(POLYMORPH_LOCK)){Killer->GainIntrinsic(POLYMORPH_LOCK);return false;} - riBuff++; // DEBUFFs, after player has taken too much it is time to make it stop, but slowly: + riDebuff=1; if(!Killer->HasStateFlag(HICCUPS)){ Killer->DeActivateTemporaryState(INVISIBLE); Killer->GainIntrinsic(HICCUPS); @@ -173,7 +173,6 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in Killer->GainIntrinsic(PANIC); return true; } - riDebuff++; // Revenge, grant it will stop: game::GetCurrentLevel()->Explosion( diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp index 63d68ab1c..1234ed66d 100644 --- a/Main/Source/dungeon.cpp +++ b/Main/Source/dungeon.cpp @@ -10,19 +10,21 @@ * */ +#include "audio.h" +#include "bitmap.h" +#include "database.h" +#include "dbgmsgproj.h" #include "dungeon.h" -#include "level.h" -#include "script.h" #include "error.h" -#include "game.h" -#include "save.h" #include "femath.h" -#include "bitmap.h" +#include "game.h" #include "graphics.h" +#include "level.h" #include "message.h" -#include "audio.h" -#include "database.h" -#include "dbgmsgproj.h" +#include "save.h" +#include "script.h" +#include "wizautoplay.h" + dungeon::dungeon(int Index) : Index(Index) { @@ -125,7 +127,7 @@ truth dungeon::PrepareLevel(int Index, truth Visual) true, &game::BusyAnimation); game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + CONST_S("...\n\nPress any key to continue."), - Displacement, WHITE, game::GetAutoPlayMode()CHAR_NAME(DEFINITE)); - - globalwindowhandler::SetPlayInBackground(bPlayInBackground); - - if(!ivanconfig::IsXBRZScale()){ - /** - * TODO - * This is an horrible gum solution... - * I still have no idea why this happens. - * Autoplay will timeout 2 times slower if xBRZ is disabled! why!??!?!? - * But the debug log shows the correct timeouts :(, clueless for now... - */ - iTimeout/=2; - } - - globalwindowhandler::SetKeyTimeout(iTimeout,'.');//,'~'); -} - -void game::IncAutoPlayMode() { -// if(!globalwindowhandler::IsKeyTimeoutEnabled()){ -// if(AutoPlayMode>=2){ -// AutoPlayMode=0; // TIMEOUT was disabled there at window handler! so reset here. -// AutoPlayModeApply(); -// } -// } - - ++AutoPlayMode; - if(AutoPlayMode>AUTOPLAYMODE_FRENZY)AutoPlayMode=AUTOPLAYMODE_DISABLED; - - AutoPlayModeApply(); -} - void game::SeeWholeMap() { if(SeeWholeMapCheatMode < 2) @@ -5631,7 +5568,6 @@ void game::SeeWholeMap() GetCurrentArea()->SendNewDrawRequest(); } - #endif void game::CreateBone() @@ -6485,7 +6421,7 @@ ulong game::IncreaseSquarePartEmitationTicks() int game::Wish(character* Wisher, cchar* MsgSingle, cchar* MsgPair, truth AllowExit) { - if(Wisher->IsPlayerAutoPlay())return ABORTED; + if(wizautoplay::IsPlayerAutoPlay(Wisher))return ABORTED; for(;;) { diff --git a/Main/Source/gear.cpp b/Main/Source/gear.cpp index 612ad18bb..680f139b5 100644 --- a/Main/Source/gear.cpp +++ b/Main/Source/gear.cpp @@ -170,7 +170,7 @@ truth pickaxe::Apply(character* User) } int Dir = game::DirectionQuestion(CONST_S("What direction do you want to dig? [press a direction key]"), false); - if(User->IsPlayerAutoPlay())Dir = clock()%8; + if(wizautoplay::IsPlayerAutoPlay(User))Dir = clock()%8; if(Dir == DIR_ERROR) return false; diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index f46fd452b..b60a5d6f6 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -3670,225 +3670,6 @@ long skeleton::GetBodyPartVolume(int I) const return 0; } -truth humanoid::AutoPlayAIequipConsumeZapReadApply() -{ - bool bDidSomething=false; - - ///////////////////////////////// WIELD - - item* iL = GetEquipment(LEFT_WIELDED_INDEX); - item* iR = GetEquipment(RIGHT_WIELDED_INDEX); - - //every X turns remove all equipments - bool bTryWieldNow=false; - static int iLastReEquipAllTurn=-1; - if(game::GetTurn()>(iLastReEquipAllTurn+100)){ DBG2(game::GetTurn(),iLastReEquipAllTurn); - iLastReEquipAllTurn=game::GetTurn(); - DBG1("UnequipAll"); - for(int i=0;iMoveTo(GetStack());SetEquipment(i,NULL);} //eq is moved to end of stack! - if(iL==eq)iL=NULL; - if(iR==eq)iR=NULL; - } -// if(iL!=NULL){iL->MoveTo(GetStack());iL=NULL;SetEquipment(LEFT_WIELDED_INDEX ,NULL);} -// if(iR!=NULL){iR->MoveTo(GetStack());iR=NULL;SetEquipment(RIGHT_WIELDED_INDEX,NULL);} - bTryWieldNow=true; - } - - //wield some weapon from the inventory as the NPC AI is not working for the player TODO why? - //every X turns try to wield - static int iLastTryToWieldTurn=-1; - if(bTryWieldNow || game::GetTurn()>(iLastTryToWieldTurn+10)){ DBG2(game::GetTurn(),iLastTryToWieldTurn); - iLastTryToWieldTurn=game::GetTurn(); - bool bDoneLR=false; - bool bL2H = iL && iL->IsTwoHanded(); - bool bR2H = iR && iR->IsTwoHanded(); - - //2handed - static int iTryWieldWhat=0; iTryWieldWhat++; DBG1(iTryWieldWhat); - if(iTryWieldWhat%2==0){ //will try 2handed first, alternating. If player has only 2handeds, the 1handeds will not be wielded and it will use punches, what is good too for tests. - if( !bDoneLR && - iL==NULL && GetBodyPartOfEquipment(LEFT_WIELDED_INDEX )!=NULL && - iR==NULL && GetBodyPartOfEquipment(RIGHT_WIELDED_INDEX)!=NULL - ){ - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); - for(uint c = 0; c < vitEqW.size(); ++c){ - if(vitEqW[c]->IsWeapon(this) && vitEqW[c]->IsTwoHanded()){ DBG1(vitEqW[c]->GetNameSingular().CStr()); - vitEqW[c]->RemoveFromSlot(); - DBG2("Wield2hd",vitEqW[c]->GetNameSingular().CStr()); - SetEquipment(clock()%2==0 ? LEFT_WIELDED_INDEX : RIGHT_WIELDED_INDEX, vitEqW[c]); //DBG3("Wield",iEqIndex,vitEqW[c]->GetName(DEFINITE).CStr()); - bDoneLR=true; - break; - } - } - } - } - - //dual 1handed (if not 2hd already) - if(!bDoneLR){ - for(int i=0;i<2;i++){ - int iChk=-1; - if(i==0)iChk=LEFT_WIELDED_INDEX; - if(i==1)iChk=RIGHT_WIELDED_INDEX; - - if( - !bDoneLR && - ( - (iChk==LEFT_WIELDED_INDEX && iL==NULL && GetBodyPartOfEquipment(LEFT_WIELDED_INDEX ) && !bR2H) - || - (iChk==RIGHT_WIELDED_INDEX && iR==NULL && GetBodyPartOfEquipment(RIGHT_WIELDED_INDEX) && !bL2H) - ) - ){ - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); - for(uint c = 0; c < vitEqW.size(); ++c){ - if( - (vitEqW[c]->IsWeapon(this) && !vitEqW[c]->IsTwoHanded()) - || - vitEqW[c]->IsShield(this) - ){ - DBG2("WieldDual",vitEqW[c]->GetNameSingular().CStr()); - vitEqW[c]->RemoveFromSlot(); - SetEquipment(iChk, vitEqW[c]); - bDoneLR=true; - break; - } - } - } - } - } - - } - - //every X turns try to use stuff from inv - static int iLastTryToUseInvTurn=-1; - if(game::GetTurn()>(iLastTryToUseInvTurn+5)){ - DBG2(game::GetTurn(),iLastTryToUseInvTurn); - iLastTryToUseInvTurn=game::GetTurn(); - - //////////////////////////////// consume food/drink - { //TODO let this happen for non-human too? - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); - for(uint c = 0; c < vitEqW.size(); ++c){ - if(clock()%3!=0 && GetHungerState() >= BLOATED)break; //randomly let it vomit and activate all related flows *eew* xD - - //if(TryToConsume(vitEqW[c])) - material* ConsumeMaterial = vitEqW[c]->GetConsumeMaterial(this); - if( - ConsumeMaterial!=NULL && - vitEqW[c]->IsConsumable() && - !HasHadBodyPart(vitEqW[c]) && //this avoids a slow interactive question - ConsumeItem(vitEqW[c], vitEqW[c]->GetConsumeMaterial(this)->GetConsumeVerb()) - ){ - DBG2("AutoPlayConsumed",vitEqW[c]->GetNameSingular().CStr()); - bDidSomething=true; - break; - } - } - } - - //////////////////////////////// equip armor ring amulet etc - { - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); - for(uint c = 0; c < vitEqW.size(); ++c){ - if(TryToEquip(vitEqW[c],true)){ - DBG2("EquipItem",vitEqW[c]->GetNameSingular().CStr()); - bDidSomething=true; - break; - }else{ - vitEqW[c]->MoveTo(GetStack()); //was dropped, get back, will be in the end of the stack! :) - } - } - } - - //////////////////////////////// zap - static int iLastZapTurn=-1; - if(game::GetTurn()>(iLastZapTurn+30)){ - DBG2(game::GetTurn(),iLastZapTurn); //every X turns try to use stuff from inv - iLastZapTurn=game::GetTurn(); - - int iDir=clock()%(8+1); // index 8 is the macro YOURSELF already... if(iDir==8)iDir=YOURSELF; - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); - for(uint c = 0; c < vitEqW.size(); ++c){ - if(!vitEqW[c]->IsZappable(this))continue; - - if(vitEqW[c]->IsZapWorthy(this)){ - if(vitEqW[c]->Zap(this, GetPos(), iDir)){ - DBG2(iLastZapTurn,vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs - bDidSomething=true; - break; - } - }else{ - if(vitEqW[c]->Apply(this)){ - DBG2(iLastZapTurn,vitEqW[c]->GetNameSingular().CStr()); - bDidSomething=true; - break; - } - } - } - } - - //////////////////////////////// read books and scrolls - static int iLastReadTurn=-1; - if(game::GetTurn()>(iLastReadTurn+15)){ DBG2(game::GetTurn(),iLastReadTurn); //every X turns try to use stuff from inv - iLastReadTurn=game::GetTurn(); - - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); - for(uint c = 0; c < vitEqW.size(); ++c){ - if(!vitEqW[c]->IsReadable(this))continue; - - static holybook* hb;hb = dynamic_cast(vitEqW[c]); - if(hb){ - if(vitEqW[c]->Read(this)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs - DBG2(iLastReadTurn,vitEqW[c]->GetNameSingular().CStr()); - bDidSomething=true; - break; - } - } - - static scroll* Scroll; Scroll = dynamic_cast(vitEqW[c]); - if( //some are simple (just read to work imediately) - dynamic_cast(Scroll) || - dynamic_cast(Scroll) || - dynamic_cast(Scroll) || - false //dummy - ){ - DBG2(iLastReadTurn,vitEqW[c]->GetNameSingular().CStr()); - Scroll->Read(this); - bDidSomething=true; - break; - } - } - } - - //////////////////////////////// apply things - static int iLastApplyTurn=-1; - if(game::GetTurn()>(iLastApplyTurn+40)){ - DBG2(game::GetTurn(),iLastApplyTurn); //every X turns try to use stuff from inv - iLastApplyTurn=game::GetTurn(); - - static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW); - static itemvector vitA;vitA.clear(); - if(GetLeftWielded())vitEqW.push_back(GetLeftWielded()); - if(GetRightWielded())vitEqW.push_back(GetRightWielded()); - for(uint c = 0; c < vitEqW.size(); ++c){ - if(AutoPlayAIcanApply(vitEqW[c])) - vitA.push_back(vitEqW[c]); - } - - if(vitA.size()){ - item* itA = vitA[clock()%vitA.size()]; - DBG2(iLastApplyTurn,itA->GetNameSingular().CStr()); - itA->Apply(this); - bDidSomething=true; - } - } - - } - - return bDidSomething; -} - truth humanoid::CheckIfEquipmentIsNotUsable(int I) const { return (I == RIGHT_WIELDED_INDEX && GetRightArm()->CheckIfWeaponTooHeavy("this item")) diff --git a/Main/Source/itemset.cpp b/Main/Source/itemset.cpp index 7385afd7f..a18b5d9be 100644 --- a/Main/Source/itemset.cpp +++ b/Main/Source/itemset.cpp @@ -28,28 +28,28 @@ EXTENDED_SYSTEM_SPECIALIZATIONS(item)(0, 0, 0, "item"); #include #include +#include "balance.h" +#include "bitmap.h" #include "char.h" -#include "message.h" -#include "stack.h" -#include "lterras.h" -#include "felist.h" #include "confdef.h" -#include "room.h" +#include "felist.h" +#include "fluid.h" #include "game.h" -#include "materias.h" +#include "god.h" #include "human.h" +#include "iconf.h" +#include "lterras.h" +#include "materias.h" +#include "message.h" #include "nonhuman.h" -#include "team.h" -#include "god.h" -#include "team.h" -#include "smoke.h" +#include "rawbit.h" +#include "room.h" #include "save.h" +#include "smoke.h" +#include "stack.h" +#include "team.h" #include "whandler.h" -#include "bitmap.h" -#include "fluid.h" -#include "rawbit.h" -#include "balance.h" -#include "iconf.h" +#include "wizautoplay.h" #include "worldmap.h" #include "wterras.h" diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index bfab84a42..31f878075 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -1672,7 +1672,7 @@ truth itemcontainer::ContentsCanBeSeenBy(ccharacter* Viewer) const truth mine::Apply(character* User) { - if(User->IsPlayer() && (!User->IsPlayerAutoPlay()) && + if(User->IsPlayer() && (!wizautoplay::IsPlayerAutoPlay(User)) && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) return false; @@ -1707,7 +1707,7 @@ truth beartrap::Apply(character* User) return false; } - if(User->IsPlayer() && (!User->IsPlayerAutoPlay()) && + if(User->IsPlayer() && (!wizautoplay::IsPlayerAutoPlay(User)) && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) return false; @@ -4069,7 +4069,7 @@ truth gastrap::Apply(character* User) return false; } - if(User->IsPlayer() && (!User->IsPlayerAutoPlay()) && + if(User->IsPlayer() && (!wizautoplay::IsPlayerAutoPlay(User)) && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) return false; diff --git a/Main/Source/wizautoplay.cpp b/Main/Source/wizautoplay.cpp new file mode 100644 index 000000000..3d11d4043 --- /dev/null +++ b/Main/Source/wizautoplay.cpp @@ -0,0 +1,1365 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through charset.cpp */ + +#include "dbgmsgproj.h" + +int wizautoplay::AutoPlayMode=AUTOPLAYMODE_DISABLED; +character* wizautoplay::P=NULL; + +bool bSafePrayOnce=false; +void wizautoplay::AutoPlayAITeleport(bool bDeathCountBased) +{ + bool bTeleportNow=false; + + if(bDeathCountBased){ // this is good to prevent autoplay AI getting stuck endless dieing + static int iDieMax=10; + static int iDieTeleportCountDown=iDieMax; + if(iDieTeleportCountDown==0){ //this helps on defeating not so strong enemies in spot + if(IsPlayerAutoPlay()) + bTeleportNow=true; + iDieTeleportCountDown=iDieMax; + bSafePrayOnce=true; + }else{ + static v2 v2DiePos(0,0); + if(v2DiePos==P->GetPos()){ + iDieTeleportCountDown--; + }else{ + v2DiePos=P->GetPos(); + iDieTeleportCountDown=iDieMax; + } + } + } + + if(bTeleportNow) + P->Move(P->GetLevel()->GetRandomSquare(P), true); //not using teleport function to avoid prompts, but this code is from there +} + +character* AutoPlayLastChar=NULL; +const int iMaxWanderTurns=20; +const int iMinWanderTurns=3; + +/** + * 5 seems good, broken cheap weapons, stones, very cheap weapons non broken etc + * btw, lantern price is currently 10. + */ +int wizautoplay::iMaxValueless = 5; + +v2 v2KeepGoingTo=v2(0,0); +v2 v2TravelingToAnotherDungeon=v2(0,0); +int iWanderTurns=iMinWanderTurns; +bool bAutoPlayUseRandomNavTargetOnce=false; +std::vector vv2DebugDrawSqrPrevious; +v2 v2LastDropPlayerWasAt=v2(0,0); +std::vector vv2FailTravelToTargets; +std::vector vv2WrongGoingTo; + +void wizautoplay::AutoPlayAIReset(bool bFailedToo) +{ DBG7(bFailedToo,iWanderTurns,DBGAV2(v2KeepGoingTo),DBGAV2(v2TravelingToAnotherDungeon),DBGAV2(v2LastDropPlayerWasAt),vv2FailTravelToTargets.size(),vv2DebugDrawSqrPrevious.size()); + v2KeepGoingTo=v2(0,0); //will retry + v2TravelingToAnotherDungeon=v2(0,0); + iWanderTurns=0; // warning: this other code was messing the logic ---> if(iWanderTurnsTerminateGoingTo(); + + if(bFailedToo){ + vv2FailTravelToTargets.clear(); + vv2WrongGoingTo.clear(); + } +} +truth wizautoplay::AutoPlayAISetAndValidateKeepGoingTo(v2 v2KGTo) +{ + v2KeepGoingTo=v2KGTo; + + bool bOk=true; + + if(bOk){ + lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(v2KeepGoingTo); + if(!P->CanTheoreticallyMoveOn(lsqr)) + bOk=false; +// olterrain* olt = game::GetCurrentLevel()->GetLSquare(v2KeepGoingTo)->GetOLTerrain(); +// if(olt){ +// if(bOk && !CanMoveOn(olt)){ +// DBG4(DBGAV2(v2KeepGoingTo),"olterrain? fixing it...",olt->GetNameSingular().CStr(),PLAYER->GetPanelName().CStr()); +// bOk=false; +// } +// +// /**** +// * keep these commented for awhile, may be useful later +// * +// if(bOk && olt->IsWall()){ //TODO this may be unnecessary cuz of above test +// //TODO is this a bug in the CanMoveOn() code? navigation AI is disabled when player is ghost TODO confirm about ethereal state, ammy of phasing +// DBG4(DBGAV2(v2KeepGoingTo),"walls? fixing it...",olt->GetNameSingular().CStr(),PLAYER->GetPanelName().CStr()); +// bOk=false; +// } +// +// if(bOk && (olt->GetWalkability() & ETHEREAL)){ //TODO this may be too much unnecessary test +// bOk=false; +// } +// */ +// } + } + + if(bOk){ + P->SetGoingTo(v2KeepGoingTo); DBG3(DBGAV2(P->GetPos()),DBGAV2(P->GoingTo),DBGAV2(v2KeepGoingTo)); + P->CreateRoute(); + if(P->Route.empty()){ + P->TerminateGoingTo(); //redundant? + bOk=false; + } + } + + if(!bOk){ + DBG1("RouteCreationFailed"); + vv2FailTravelToTargets.push_back(v2KeepGoingTo); DBG3("BlockGoToDestination",DBGAV2(v2KeepGoingTo),vv2FailTravelToTargets.size()); + bAutoPlayUseRandomNavTargetOnce=true; + + AutoPlayAIReset(false); //v2KeepGoingTo is reset here too + } + + return bOk; +} + +void wizautoplay::AutoPlayAIDebugDrawSquareRect(v2 v2SqrPos, col16 color, int iPrintIndex, bool bWide, bool bKeepColor) +{ + static v2 v2ScrPos=v2(0,0); //static to avoid instancing + static int iAddPos;iAddPos=bWide?2:1; + static int iSubBorder;iSubBorder=bWide?3:2; + if(game::OnScreen(v2SqrPos)){ + v2ScrPos=game::CalculateScreenCoordinates(v2SqrPos); + + DOUBLE_BUFFER->DrawRectangle( + v2ScrPos.X+iAddPos, v2ScrPos.Y+iAddPos, + v2ScrPos.X+TILE_SIZE-iSubBorder, v2ScrPos.Y+TILE_SIZE-iSubBorder, + color, bWide); + + if(iPrintIndex>-1) + FONT->Printf(DOUBLE_BUFFER, v2(v2ScrPos.X+1,v2ScrPos.Y+5), DARK_GRAY, "%d", iPrintIndex); + + if(!bKeepColor) + vv2DebugDrawSqrPrevious.push_back(v2SqrPos); + } +} + +const int iVisitAgainMax=10; +int iVisitAgainCount=iVisitAgainMax; +std::vector vv2AllDungeonSquares; +bool wizautoplay::AutoPlayAICheckAreaLevelChangedAndReset() +{ + static area* areaPrevious=NULL; + area* Area = game::GetCurrentArea(); + if(Area != areaPrevious){ + areaPrevious=Area; + + iVisitAgainCount=iVisitAgainMax; + + vv2DebugDrawSqrPrevious.clear(); + + vv2AllDungeonSquares.clear(); + if(!game::IsInWilderness()) + for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ + vv2AllDungeonSquares.push_back(game::GetCurrentLevel()->GetLSquare(iX, iY)); + }} + + return true; + } + + return false; +} + +void wizautoplay::AutoPlayAIDebugDrawOverlay() +{ + if(!game::WizardModeIsActive())return; + + AutoPlayAICheckAreaLevelChangedAndReset(); + + // redraw previous to clean them + area* Area = game::GetCurrentArea(); //got the Area to draw in the wilderness too and TODO navigate there one day + std::vector vv2DebugDrawSqrPreviousCopy(vv2DebugDrawSqrPrevious); + for(int i=0;iGetSquare(vv2DebugDrawSqrPrevious[i])->SendNewDrawRequest(); +// square* sqr = Area->GetSquare(vv2DebugDrawSqrPrevious[i]); +// if(sqr)sqr->SendStrongNewDrawRequest(); //TODO sqr NULL? + AutoPlayAIDebugDrawSquareRect(vv2DebugDrawSqrPreviousCopy[i],DARK_GRAY); + } + + // draw new ones + vv2DebugDrawSqrPrevious.clear(); //empty before fillup below + + for(int i=0;iRoute.empty()) + for(int i=0;iRoute.size();i++) + AutoPlayAIDebugDrawSquareRect(PLAYER->Route[i],GREEN); + + if(!v2KeepGoingTo.Is0()) + AutoPlayAIDebugDrawSquareRect(v2KeepGoingTo,BLUE,PLAYER->Route.size(),true); + else if(iWanderTurns>0) + AutoPlayAIDebugDrawSquareRect(PLAYER->GetPos(),YELLOW,iWanderTurns); + + for(int i=0;iIsAppliable(P))return false; + if(it->IsZappable(P))return false; //not here, see zap section + if(dynamic_cast(it))return false; // too complex to make it auto work + if(dynamic_cast(it))return false; // too complex to make it auto work + return true; +} + +truth wizautoplay::AutoPlayAIDropThings() +{ +// level* lvl = game::GetCurrentLevel(); DBG1(lvl); +// area* Area = game::GetCurrentArea(); + + /** + * unburden + */ + bool bDropSomething = false; + static item* eqDropChk=NULL; + item* eqBroken=NULL; + for(int i=0;iGetEquipments();i++){ + eqDropChk=P->GetEquipment(i); + if(eqDropChk!=NULL && eqDropChk->IsBroken()){ DBG2("chkDropBroken",eqDropChk); + eqBroken=eqDropChk; + bDropSomething=true; + break; + } + } + + if(!bDropSomething && P->GetBurdenState() == STRESSED){ + if(clock()%100<5){ //5% chance to drop something weighty randomly every turn + bDropSomething=true; DBGLN; + } + } + + if(!bDropSomething && P->GetBurdenState() == OVER_LOADED){ + bDropSomething=true; + } + + if(bDropSomething){ DBG1("DropSomething"); + item* dropMe=NULL; + if(eqBroken!=NULL)dropMe=eqBroken; + + item* heaviest=NULL; + item* cheapest=NULL; + +// bool bFound=false; +// for(int k=0;k<2;k++){ +// if(dropMe!=NULL)break; +// static item* eqDropChk=NULL; +// for(int i=0;iIsBroken()){ +// dropMe=eqDropChk; +// break; +// } +// } + + if(dropMe==NULL){ + static itemvector vit;vit.clear();P->GetStack()->FillItemVector(vit); + for(int i=0;iGetName(DEFINITE).CStr(),vit[i]->GetTruePrice(),vit[i]->GetWeight()); + if(vit[i]->IsEncryptedScroll())continue; +// if(!bPlayerHasLantern && dynamic_cast(vit[i])!=NULL){ +// bPlayerHasLantern=true; //will keep only the 1st lantern +// continue; +// } + + if(vit[i]->IsBroken()){ //TODO use repair scroll? + dropMe=vit[i]; + break; + } + + if(heaviest==NULL)heaviest=vit[i]; + if(cheapest==NULL)cheapest=vit[i]; + +// switch(k){ +// case 0: //better not implement this as a user function as that will remove the doubt about items values what is another fun challenge :) + if(vit[i]->GetTruePrice() < cheapest->GetTruePrice()) //cheapest + cheapest=vit[i]; +// break; +// case 1: //this could be added as user function to avoid browsing the drop list, but may not be that good... + if(vit[i]->GetWeight() > heaviest->GetWeight()) //heaviest + heaviest=vit[i]; +// break; +// } + } + } + + if(heaviest!=NULL && cheapest!=NULL){ + if(dropMe==NULL && heaviest==cheapest) + dropMe=heaviest; + + if(dropMe==NULL && cheapest->GetTruePrice()<=iMaxValueless){ DBG2("DropValueless",cheapest->GetName(DEFINITE).CStr()); + dropMe=cheapest; + } + + if(dropMe==NULL){ + // the worst price VS weight will be dropped + float fC = cheapest ->GetTruePrice()/(float)cheapest ->GetWeight(); + float fW = heaviest->GetTruePrice()/(float)heaviest->GetWeight(); DBG3("PriceVsWeightRatio",fC,fW); + if(fC < fW){ + dropMe = cheapest; + }else{ + dropMe = heaviest; + } + } + + if(dropMe==NULL) + dropMe = clock()%2==0 ? heaviest : cheapest; + } + + // chose a throw direction + if(dropMe!=NULL){ + static std::vector vv2DirBase;static bool bDummyInit = [](){for(int i=0;i<8;i++)vv2DirBase.push_back(i);return true;}(); + std::vector vv2Dir(vv2DirBase); + int iDirOk=-1; + v2 v2DropAt(0,0); + lsquare* lsqrDropAt=NULL; + for(int i=0;i<8;i++){ + int k = clock()%vv2Dir.size(); //random chose from remaining TODO could be where there is NPC foe + int iDir = vv2Dir[k]; //collect direction value + vv2Dir.erase(vv2Dir.begin() + k); //remove using the chosen index to prepare next random choice + + v2 v2Dir = game::GetMoveVector(iDir); + v2 v2Chk = P->GetPos() + v2Dir; + if(game::GetCurrentLevel()->IsValidPos(v2Chk)){ + lsquare* lsqrChk=game::GetCurrentLevel()->GetLSquare(v2Chk); + if(lsqrChk->IsFlyable()){ + iDirOk = iDir; + v2DropAt = v2Chk; + lsqrDropAt=lsqrChk; + break; + } + } + };DBGLN; + + if(iDirOk==-1){iDirOk=clock()%8;DBG2("RandomDir",iDirOk);}DBGLN; //TODO should just drop may be? unless hitting w/e is there could help + + bool bApplyDropped=false; //or vanished + if(AutoPlayAIcanApply(dropMe) && dropMe->Apply(P)){ + static itemvector ivChkDrop;ivChkDrop.clear(); + P->GetStack()->FillItemVector(ivChkDrop); + bApplyDropped=true; + for(int i6=0;i6-1){DBG2("KickOrThrow",iDirOk); + static itemcontainer* itc;itc = dynamic_cast(dropMe);DBGLN; + static humanoid* h;h = dynamic_cast(P);DBGLN; + DBG8("CanKickLockedChest",lsqrDropAt,itc,itc?itc->IsLocked():-1,P->CanKick(),h,h?h->GetLeftLeg():0,h?h->GetRightLeg():0); + if(lsqrDropAt && itc && itc->IsLocked() && P->CanKick() && h && h->GetLeftLeg() && h->GetRightLeg()){DBGLN; + dropMe->MoveTo(lsqrDropAt->GetStack());DBGLN; //drop in front.. + P->Kick(lsqrDropAt,iDirOk,true);DBGLN; // ..to kick it + }else{DBGLN; + P->ThrowItem(iDirOk, dropMe); DBG5("DropThrow",iDirOk,dropMe->GetName(DEFINITE).CStr(),dropMe->GetTruePrice(),dropMe->GetWeight()); + } + }else{DBGLN; + dropMe->MoveTo(P->GetLSquareUnder()->GetStack());DBGLN; //just drop + } + + v2LastDropPlayerWasAt=P->GetPos();DBGSV2(v2LastDropPlayerWasAt); + } + + return true; + } + + DBG1("AutoPlayNeedsImprovement:DropItem"); + ADD_MESSAGE("%s says \"I need more intelligence to drop trash...\"", P->GetName(DEFINITE).CStr()); // improve the dropping AI + //TODO stop autoplay mode? if not, something random may happen some time and wont reach here ex.: spoil, fire, etc.. + } + + return false; +} + +bool wizautoplay::IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern) +{ + if(!it->CanBeSeenBy(P))return false; + P->ValidateTrapData(); + if(!it->IsPickable(P))return false; + if(it->GetSquaresUnder()!=1)return false; //avoid big corpses 2x2 + + if(!bPlayerHasLantern && (it->IsOnFire(P) || it->GetEmitation()>0)){ + //pickup priority + }else{ + if(it->IsBroken())return false; + if(it->GetTruePrice()<=iMaxValueless)return false; //mainly to avoid all rocks from broken walls + if(clock()%3!=0 && it->GetSpoilLevel()>0)return false; //some spoiled may be consumed to randomly test diseases flows + } + + return true; +} + +truth wizautoplay::AutoPlayAIEquipAndPickup(bool bPlayerHasLantern) +{ + static humanoid* h;h = dynamic_cast(P); + if(h==NULL)return false; + // other invalid equippers + if(dynamic_cast(P) != NULL)return false; + if(dynamic_cast(P) != NULL)return false; + + if(AutoPlayAIequipConsumeZapReadApply()) + return true; + + if(P->GetBurdenState()!=OVER_LOADED){ DBG4(P->CommandFlags&DONT_CHANGE_EQUIPMENT,P,P->GetNameSingular().CStr(),P->GetSquareUnder()); + if(v2LastDropPlayerWasAt!=P->GetPos()){ + static bool bHoarder=true; //TODO wizard autoplay AI config exclusive felist + + if(P->CheckForUsefulItemsOnGround(false)) + if(!bHoarder) + return true; + + //just pick up any useful stuff + static itemvector vit;vit.clear();P->GetStackUnder()->FillItemVector(vit); + for(uint c = 0; c < vit.size(); ++c){ + if(!IsAutoplayAICanPickup(vit[c],bPlayerHasLantern))continue; + + static itemcontainer* itc;itc = dynamic_cast(vit[c]); + if(itc && !itc->IsLocked()){ //get items from unlocked container + static itemvector vitItc;vitItc.clear();itc->GetContained()->FillItemVector(vitItc); + for(uint d = 0; d < vitItc.size(); ++d) + vitItc[d]->MoveTo(itc->GetLSquareUnder()->GetStack()); + continue; + } + + vit[c]->MoveTo(P->GetStack()); DBG2("pickup",vit[c]->GetNameSingular().CStr()); +// if(GetBurdenState()==OVER_LOADED)ThrowItem(clock()%8,ItemVector[c]); +// return true; + if(!bHoarder) + return true; + } + } + } + + return false; +} + +static const int iMoreThanMaxDist=10000000; //TODO should be max integer but this will do for now in 2018 :) +truth wizautoplay::AutoPlayAITestValidPathTo(v2 v2To) +{ + return AutoPlayAIFindWalkDist(v2To) < iMoreThanMaxDist; +} + +int wizautoplay::AutoPlayAIFindWalkDist(v2 v2To) +{ + static bool bUseSimpleDirectDist=false; //very bad navigation this is + if(bUseSimpleDirectDist)return (v2To - P->GetPos()).GetLengthSquare(); + + static v2 GoingToBkp;GoingToBkp = P->GoingTo; //IsGoingSomeWhere() ? GoingTo : v2(0,0); + + P->SetGoingTo(v2To); + P->CreateRoute(); + static int iDist;iDist=P->Route.size(); + P->TerminateGoingTo(); + + if(GoingToBkp!=ERROR_V2){ DBG2("Warning:WrongUsage:ShouldBeGoingNoWhere",DBGAV2(GoingToBkp)); + P->SetGoingTo(GoingToBkp); + P->CreateRoute(); + } + + return iDist>0?iDist:iMoreThanMaxDist; +} + +const int iDesperateResetCountDownDefault=5; +const int iDesperateEarthQuakeCountDownDefault=iDesperateResetCountDownDefault*2; +const int iAutoPlayAIResetCountDownDefault = iDesperateEarthQuakeCountDownDefault*2; +int iAutoPlayAIResetCountDown = iAutoPlayAIResetCountDownDefault; +truth wizautoplay::AutoPlayAINavigateDungeon(bool bPlayerHasLantern) +{ + /** + * navigate the unknown dungeon + */ + festring fsDL;fsDL<GetDungeon()->GetIndex()<GetLevel()->GetIndex(); + festring fsStayOnDL;{const char* pc = std::getenv("IVAN_DebugStayOnDungeonLevel");if(pc!=NULL)fsStayOnDL< v2Exits; + std::vector v2Altars; + if(v2KeepGoingTo.Is0()){ DBG1("TryNewMoveTarget"); + // target undiscovered squares to explore + v2 v2PreferedTarget(0,0); + + int iNearestLanterOnFloorDist = iMoreThanMaxDist; + v2 v2PreferedLanternOnFloorTarget(0,0); + + v2 v2NearestUndiscovered(0,0); + int iNearestUndiscoveredDist=iMoreThanMaxDist; + std::vector vv2UndiscoveredValidPathSquares; + + lsquare* lsqrNearestSquareWithWallLantern=NULL; + lsquare* lsqrNearestDropWallLanternAt=NULL; + stack* stkNearestDropWallLanternAt = NULL; + int iNearestSquareWithWallLanternDist=iMoreThanMaxDist; + item* itNearestWallLantern=NULL; + + /*************************************************************** + * scan whole dungeon squares + */ + for(int iY=0;iYGetYSize();iY++){ for(int iX=0;iXGetXSize();iX++){ + lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(iX,iY); + + olterrain* olt = lsqr->GetOLTerrain(); + if(olt && (olt->GetConfig() == STAIRS_UP || olt->GetConfig() == STAIRS_DOWN)){ + if(fsDL!=fsStayOnDL){ + v2Exits.push_back(v2(lsqr->GetPos())); + DBGSV2(v2Exits[v2Exits.size()-1]); + } + } + + altar* Altar = dynamic_cast(olt); + if(olt && Altar){ + if(!Altar->GetMasterGod()->IsKnown()) + v2Altars.push_back(v2(lsqr->GetPos())); + } + + stack* stkSqr = lsqr->GetStack(); + static itemvector vit;vit.clear();stkSqr->FillItemVector(vit); + bool bAddValidTargetSquare=true; + + // find nearest wall lantern + if(!bPlayerHasLantern && olt && olt->IsWall()){ + for(int n=0;nIsLanternOnWall() && !vit[n]->IsBroken()){ + static stack* stkDropWallLanternAt;stkDropWallLanternAt = lsqr->GetStackOfAdjacentSquare(vit[n]->GetSquarePosition()); + static lsquare* lsqrDropWallLanternAt;lsqrDropWallLanternAt = + stkDropWallLanternAt?stkDropWallLanternAt->GetLSquareUnder():NULL; + + if(stkDropWallLanternAt && lsqrDropWallLanternAt && P->CanTheoreticallyMoveOn(lsqrDropWallLanternAt)){ + int iDist = AutoPlayAIFindWalkDist(lsqrDropWallLanternAt->GetPos()); //(lsqr->GetPos() - GetPos()).GetLengthSquare(); + if(lsqrNearestSquareWithWallLantern==NULL || iDistGetPos()),DBGAV2(P->GetPos())); + lsqrNearestDropWallLanternAt=lsqrDropWallLanternAt; + stkNearestDropWallLanternAt=stkDropWallLanternAt; + } + } + + break; + } + } + } + + if(bAddValidTargetSquare && !P->CanTheoreticallyMoveOn(lsqr)) + bAddValidTargetSquare=false; + + bool bIsFailToTravelSquare=false; + if(bAddValidTargetSquare){ + for(int j=0;jGetPos()){ + bAddValidTargetSquare=false; + bIsFailToTravelSquare=true; + break; + } + } + + if(!bIsFailToTravelSquare){ + +// if(bAddValidTargetSquare && v2PreferedTarget.Is0() && (lsqr->HasBeenSeen() || !bPlayerHasLantern)){ + if(bAddValidTargetSquare && (lsqr->HasBeenSeen() || !bPlayerHasLantern)){ + bool bVisitAgain=false; + if(iVisitAgainCount>0 || !bPlayerHasLantern){ + if(stkSqr!=NULL && stkSqr->GetItems()>0){ + for(int n=0;nGetID());DBG1(vit[n]->GetType());DBG3("VisitAgain:ChkItem",vit[n]->GetNameSingular().CStr(),vit.size()); + if(vit[n]->IsBroken())continue; DBGLN; + + static bool bIsLanternOnFloor;bIsLanternOnFloor = dynamic_cast(vit[n])!=NULL;// || vit[n]->IsOnFire(P); DBGLN; + + if( // if is useful to the AutoPlay AI endless tests + vit[n]->IsShield (P) || + vit[n]->IsWeapon (P) || + vit[n]->IsArmor (P) || + vit[n]->IsAmulet (P) || + vit[n]->IsZappable(P) || //wands + vit[n]->IsAppliable(P) || //mines, beartraps etc + vit[n]->IsRing (P) || + vit[n]->IsReadable(P) || //books and scrolls + vit[n]->IsDrinkable(P)|| //potions and vials + bIsLanternOnFloor + ) + if(IsAutoplayAICanPickup(vit[n],bPlayerHasLantern)) + { + bVisitAgain=true; + + if(bIsLanternOnFloor && !bPlayerHasLantern){ + static int iDist;iDist = AutoPlayAIFindWalkDist(lsqr->GetPos()); //(lsqr->GetPos() - GetPos()).GetLengthSquare(); + if(iDistGetPos(); DBG2("PreferLanternAt",DBGAV2(lsqr->GetPos())) + } + }else{ + iVisitAgainCount--; + } + + DBG4(bVisitAgain,DBGAV2(lsqr->GetPos()),iVisitAgainCount,bIsLanternOnFloor); + break; + } + } + } + } + + if(!bVisitAgain)bAddValidTargetSquare=false; + } + + } + + if(bAddValidTargetSquare) + if(!P->CanTheoreticallyMoveOn(lsqr)) //if(olt && !CanMoveOn(olt)) + bAddValidTargetSquare=false; + + if(bAddValidTargetSquare){ DBG2("addValidSqr",DBGAV2(lsqr->GetPos())); + static int iDist;iDist=AutoPlayAIFindWalkDist(lsqr->GetPos()); //(lsqr->GetPos() - GetPos()).GetLengthSquare(); + + if(iDistGetPos()); + + if(iDistGetPos(); DBG3(iNearestUndiscoveredDist,DBGAV2(lsqr->GetPos()),DBGAV2(P->GetPos())); + } + } + }} DBG2(DBGAV2(v2PreferedTarget),vv2UndiscoveredValidPathSquares.size()); + + /*************************************************************** + * define prefered navigation target + */ + if(!bPlayerHasLantern && v2PreferedTarget.Is0()){ + bool bUseWallLantern=false; + if(!v2PreferedLanternOnFloorTarget.Is0() && lsqrNearestSquareWithWallLantern!=NULL){ + if(iNearestLanterOnFloorDist <= iNearestSquareWithWallLanternDist){ + v2PreferedTarget=v2PreferedLanternOnFloorTarget; + }else{ + bUseWallLantern=true; + } + }else if(!v2PreferedLanternOnFloorTarget.Is0()){ + v2PreferedTarget=v2PreferedLanternOnFloorTarget; + }else if(lsqrNearestSquareWithWallLantern!=NULL){ + bUseWallLantern=true; + } + + if(bUseWallLantern){ + /** + * target to nearest wall lantern + * check for lanterns on walls of adjacent squares if none found on floors + */ + itNearestWallLantern->MoveTo(stkNearestDropWallLanternAt); // the AI is prepared to get things from the floor only so "magically" drop it :) + v2PreferedTarget = lsqrNearestDropWallLanternAt->GetPos(); DBG2("PreferWallLanternAt",DBGAV2(lsqrNearestDropWallLanternAt->GetPos())) + } + + } + + /*************************************************************** + * validate and set new navigation target + */ +// DBG9("AllNavigatePossibilities",DBGAV2(v2PreferedTarget),DBGAV2(v2PreferedLanternOnFloorTarget),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2(),DBGAV2()); + v2 v2NewKGTo=v2(0,0); + + if(v2NewKGTo.Is0()){ + //TODO if(!v2PreferedTarget.Is0){ // how can this not be compiled? error: cannot convert ‘v2::Is0’ from type ‘truth (v2::)() const {aka bool (v2::)() const}’ to type ‘bool’ + if(v2PreferedTarget.GetLengthSquare()>0) + if(AutoPlayAITestValidPathTo(v2PreferedTarget)) + v2NewKGTo=v2PreferedTarget; DBGSV2(v2PreferedTarget); + } + + if(v2NewKGTo.Is0()){ + if(bAutoPlayUseRandomNavTargetOnce){ //these targets were already path validated and are safe to use! + v2NewKGTo=vv2UndiscoveredValidPathSquares[clock()%vv2UndiscoveredValidPathSquares.size()]; DBG2("RandomTarget",DBGAV2(v2NewKGTo)); + bAutoPlayUseRandomNavTargetOnce=false; + }else{ //find nearest + if(!v2NearestUndiscovered.Is0()){ + v2NewKGTo=v2NearestUndiscovered; DBGSV2(v2NearestUndiscovered); + } + } + } + + if(v2NewKGTo.Is0()){ //no new destination: fully explored + if(v2Altars.size()>0){ + for(int i=0;i0){ + if(game::GetCurrentDungeonTurnsCount()==0){ DBG1("Dungeon:FullyExplored:FirstTurn"); + iWanderTurns=100+clock()%300; DBG2("WanderALotOnFullyExploredLevel",iWanderTurns); //just move around a lot, some NPC may spawn + }else{ + // travel between dungeons if current fully explored + v2 v2Try; + for(int i=0;i<10;i++){ + v2Try = v2Exits[clock()%v2Exits.size()]; + if(v2Try!=P->GetPos())break; + } + if(AutoPlayAITestValidPathTo(v2Try) || iAutoPlayAIResetCountDown==0) + v2NewKGTo = v2TravelingToAnotherDungeon = v2Try; DBGSV2(v2TravelingToAnotherDungeon); + } + }else{ + DBG1("AutoPlayNeedsImprovement:Navigation") + ADD_MESSAGE("%s says \"I need more intelligence to move around...\"", P->GetName(DEFINITE).CStr()); // improve the dropping AI + //TODO stop autoplay mode? + } + } + + if(v2NewKGTo.Is0()){ DBG1("Desperately:TryAnyRandomTargetNavWithValidPath"); + std::vector vlsqrChk(vv2AllDungeonSquares); + + while(vlsqrChk.size()>0){ + static int i;i=clock()%vlsqrChk.size(); + static v2 v2Chk; v2Chk = vlsqrChk[i]->GetPos(); + + if(!AutoPlayAITestValidPathTo(v2Chk)){ + vlsqrChk.erase(vlsqrChk.begin()+i); + }else{ + v2NewKGTo=v2Chk; + break; + } + } + } + + if(!v2NewKGTo.Is0()){ + AutoPlayAISetAndValidateKeepGoingTo(v2NewKGTo); + }else{ + DBG1("TODO:too complex paths are failing... improve CreateRoute()?"); + } + } + + if(!v2KeepGoingTo.Is0()){ + if(v2KeepGoingTo==P->GetPos()){ DBG3("ReachedDestination",DBGAV2(v2KeepGoingTo),DBGAV2(P->GoingTo)); + //wander a bit before following new target destination + iWanderTurns=(clock()%iMaxWanderTurns)+iMinWanderTurns; DBG2("WanderAroundAtReachedDestination",iWanderTurns); + +// v2KeepGoingTo=v2(0,0); +// TerminateGoingTo(); + AutoPlayAIReset(false); + return true; + } + +// CheckForUsefulItemsOnGround(false); DBGSV2(GoingTo); +// CheckForEnemies(false, true, false, false); DBGSV2(GoingTo); + +// if(!IsGoingSomeWhere() || v2KeepGoingTo!=GoingTo){ DBG3("ForceKeepGoingTo",DBGAV2(v2KeepGoingTo),DBGAV2(GoingTo)); +// SetGoingTo(v2KeepGoingTo); +// } + static int iForceGoingToCountDown=10; + static v2 v2GoingToBkp;v2GoingToBkp=P->GoingTo; + if(!v2KeepGoingTo.IsAdjacent(P->GoingTo)){ + if(iForceGoingToCountDown==0){ + DBG4("ForceKeepGoingTo",DBGAV2(v2KeepGoingTo),DBGAV2(P->GoingTo),DBGAV2(P->GetPos())); + + if(!AutoPlayAISetAndValidateKeepGoingTo(v2KeepGoingTo)){ + static int iSetFailTeleportCountDown=10; + iSetFailTeleportCountDown--; + vv2WrongGoingTo.push_back(v2GoingToBkp); + if(iSetFailTeleportCountDown==0){ + AutoPlayAITeleport(false); + AutoPlayAIReset(true); //refresh to test/try it all again + iSetFailTeleportCountDown=10; + } + } + DBGSV2(P->GoingTo); + return true; + }else{ + iForceGoingToCountDown--; DBG1(iForceGoingToCountDown); + } + }else{ + iForceGoingToCountDown=10; + } + + /** + * Determinedly blindly moves towards target, the goal is to Navigate! + * + * this has several possible status if returning false... + * so better do not decide anything based on it? + */ + P->MoveTowardsTarget(false); + +// if(!MoveTowardsTarget(false)){ DBG3("OrFailedGoingTo,OrReachedDestination...",DBGAV2(GoingTo),DBGAV2(GetPos())); // MoveTowardsTarget may break the GoingTo EVEN if it succeeds????? +// TerminateGoingTo(); +// v2KeepGoingTo=v2(0,0); //reset only this one to try again +// GetAICommand(); //wander once for randomicity +// } + + return true; + } + + return false; +} + +bool wizautoplay::AutoPlayAIChkInconsistency() +{ + if(P->GetSquareUnder()==NULL){ + DBG9(P,P->GetNameSingular().CStr(),P->IsPolymorphed(),P->IsHuman(),P->IsHumanoid(),P->IsPolymorphable(),P->IsPlayerKind(),P->IsTemporary(),P->IsPet()); + DBG6("GetSquareUnderIsNULLhow?",P->IsHeadless(),P->IsPlayer(),wizautoplay::GetAutoPlayMode(),IsPlayerAutoPlay(),P->GetName(DEFINITE).CStr()); + return true; //to just ignore this turn expecting on next it will be ok. + } + return false; +} + +truth wizautoplay::AutoPlayAIPray() +{ + bool bSPO = bSafePrayOnce; + bSafePrayOnce=false; + + if(bSPO){} + else if(P->StateIsActivated(PANIC) && clock()%10==0){ + iWanderTurns=1; DBG1("Wandering:InPanic"); // to regain control as soon it is a ghost anymore as it can break navigation when inside walls + }else return false; + + // check for known gods + int aiKGods[GODS]; + int iKGTot=0; + int aiKGodsP[GODS]; + int iKGTotP=0; + static int iPleased=50; //see god::PrintRelation() + for(int c = 1; c <= GODS; ++c){ + if(!game::GetGod(c)->IsKnown())continue; + // even known, praying to these extreme ones will be messy if Relation<1000 + if(dynamic_cast(game::GetGod(c))!=NULL && game::GetGod(c)->GetRelation()<1000)continue; + if(dynamic_cast(game::GetGod(c))!=NULL && game::GetGod(c)->GetRelation()<1000)continue; + + aiKGods[iKGTot++]=c; + + if(game::GetGod(c)->GetRelation() > iPleased){ +// //TODO could this help? +// switch(game::GetGod(c)->GetBasicAlignment()){ //game::GetGod(c)->GetAlignment(); +// case GOOD: +// if(game::GetPlayerAlignment()>=2){}else continue; +// break; +// case NEUTRAL: +// if(game::GetPlayerAlignment()<2 && game::GetPlayerAlignment()>-2){}else continue; +// break; +// case EVIL: +// if(game::GetPlayerAlignment()<=-2){}else continue; +// break; +// } + aiKGodsP[iKGTotP++] = c; + } + } + if(iKGTot==0){ + if(clock()%10==0) + for(int c = 1; c <= GODS; ++c) + game::LearnAbout(game::GetGod(c)); + return false; + } +// if(bSPO && iKGTotP==0)return false; + + // chose and pray to one god + god* g = NULL; + if(iKGTotP>0 && (bSPO || clock()%10!=0)) + g = game::GetGod(aiKGodsP[clock()%iKGTotP]); //only good effect + else + g = game::GetGod(aiKGods[clock()%iKGTot]); //can have bad effect too + + if(bSPO || clock()%10!=0){ //it may not recover some times to let pray unsafely + int iRecover=0; + if(iKGTotP==0){ + if(iRecover==0 && g->GetRelation()==-1000)iRecover=1000; //to test all relation range + if(iRecover==0 && g->GetRelation() <= iPleased)iRecover=iPleased; //to alternate tests on many with low good relation + } + if(iRecover>0) + g->SetRelation(iRecover); + + g->AdjustTimer(-1000000000); //TODO filter gods using timer too instead of this reset? + } + + g->Pray(); DBG2("PrayingTo",g->GetName()); + + return true; +} + +truth wizautoplay::AutoPlayAICommand(int& rKey) +{ + DBGLN;if(AutoPlayAIChkInconsistency())return true; + DBGSV2(P->GetPos()); + + if(AutoPlayLastChar!=P){ + AutoPlayAIReset(true); + AutoPlayLastChar=P; + } + + DBGLN;if(AutoPlayAIChkInconsistency())return true; + if(AutoPlayAICheckAreaLevelChangedAndReset()) + AutoPlayAIReset(true); + + static bool bDummy_initDbg = [](){game::AddDebugDrawOverlayFunction(&AutoPlayAIDebugDrawOverlay);return true;}(); + + truth bPlayerHasLantern=false; + static itemvector vit;vit.clear();P->GetStack()->FillItemVector(vit); + for(uint i=0;i(vit[i])!=NULL || vit[i]->IsOnFire(P) || vit[i]->GetEmitation()>0){ + bPlayerHasLantern=true; //will keep only the 1st lantern + break; + } + } + + DBGLN;if(AutoPlayAIChkInconsistency())return true; + AutoPlayAIPray(); + + //TODO this doesnt work??? -> if(IsPolymorphed()){ //to avoid some issues TODO but could just check if is a ghost +// if(dynamic_cast(P) == NULL){ //this avoid some issues TODO but could just check if is a ghost +// if(StateIsActivated(ETHEREAL_MOVING)){ //this avoid many issues + static bool bPreviousTurnWasGhost=false; + if(dynamic_cast(P) != NULL){ DBG1("Wandering:Ghost"); //this avoid many issues mainly related to navigation + iWanderTurns=1; // to regain control as soon it is a ghost anymore as it can break navigation when inside walls + bPreviousTurnWasGhost=true; + }else{ + if(bPreviousTurnWasGhost){ + AutoPlayAIReset(true); //this may help on navigation + bPreviousTurnWasGhost=false; + return true; + } + } + + DBGLN;if(AutoPlayAIChkInconsistency())return true; + if(AutoPlayAIDropThings()) + return true; + + DBGLN;if(AutoPlayAIChkInconsistency())return true; + if(AutoPlayAIEquipAndPickup(bPlayerHasLantern)) + return true; + + if(iWanderTurns>0){ + if(!P->IsPlayer() || wizautoplay::GetAutoPlayMode()==AUTOPLAYMODE_DISABLED || !IsPlayerAutoPlay()){ //redundancy: yep + DBG9(P,P->GetNameSingular().CStr(),P->IsPolymorphed(),P->IsHuman(),P->IsHumanoid(),P->IsPolymorphable(),P->IsPlayerKind(),P->IsTemporary(),P->IsPet()); + DBG5(P->IsHeadless(),P->IsPlayer(),wizautoplay::GetAutoPlayMode(),IsPlayerAutoPlay(),P->GetName(DEFINITE).CStr()); + ABORT("autoplay is inconsistent %d %d %d %d %d %s %d %s %d %d %d %d %d", + P->IsPolymorphed(),P->IsHuman(),P->IsHumanoid(),P->IsPolymorphable(),P->IsPlayerKind(), + P->GetNameSingular().CStr(),wizautoplay::GetAutoPlayMode(),P->GetName(DEFINITE).CStr(), + P->IsTemporary(),P->IsPet(),P->IsHeadless(),P->IsPlayer(),IsPlayerAutoPlay()); + } + P->GetAICommand(); DBG2("Wandering",iWanderTurns); //fallback to default TODO never reached? + iWanderTurns--; + return true; + } + + /*************************************************************************************************** + * WANDER above here + * NAVIGATE below here + ***************************************************************************************************/ + + /** + * travel between dungeons + */ + bool bTBD = false; + if(!v2TravelingToAnotherDungeon.Is0()){ + if(P->GetPos() == v2TravelingToAnotherDungeon) + bTBD=true; + else + if(iAutoPlayAIResetCountDown==0){ + P->Move(v2TravelingToAnotherDungeon,true); + iAutoPlayAIResetCountDown=iAutoPlayAIResetCountDownDefault; + bTBD=true; + } + } + if(bTBD){ + bool bTravel=false; + lsquare* lsqr = game::GetCurrentLevel()->GetLSquare(v2TravelingToAnotherDungeon); +// square* sqr = Area->GetSquare(v2TravelingToAnotherDungeon); + olterrain* ot = lsqr->GetOLTerrain(); +// oterrain* ot = sqr->GetOTerrain(); + if(ot){ + if(ot->GetConfig() == STAIRS_UP){ + rKey='<'; + bTravel=true; + } + + if(ot->GetConfig() == STAIRS_DOWN){ + rKey='>'; + bTravel=true; + } + } + + if(bTravel){ DBG3("travel",DBGAV2(v2TravelingToAnotherDungeon),rKey); + AutoPlayAIReset(true); + return false; //so the new/changed key will be used as command, otherwise it would be ignored + } + } + + static int iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; + if(AutoPlayAINavigateDungeon(bPlayerHasLantern)){ + iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; + return true; + }else{ + if(iDesperateEarthQuakeCountDown==0){ + iDesperateEarthQuakeCountDown=iDesperateEarthQuakeCountDownDefault; + /** + * this changes the dungeon level paths, + * so applying pickaxe or using fireballs etc are not required! + */ + scrollofearthquake::Spawn()->FinishReading(P); + DBG1("UsingTerribleEarthquakeSolution"); // xD + }else{ + iDesperateEarthQuakeCountDown--; + DBG1(iDesperateEarthQuakeCountDown); + } + } + + /**************************************** + * Twighlight zone + */ + + ADD_MESSAGE("%s says \"I need more intelligence to do things by myself...\"", P->GetName(DEFINITE).CStr()); DBG1("TODO: AI needs improvement"); + + static int iDesperateResetCountDown=iDesperateResetCountDownDefault; + if(iDesperateResetCountDown==0){ + iDesperateResetCountDown=iDesperateResetCountDownDefault; + + AutoPlayAIReset(true); + iAutoPlayAIResetCountDown--; + + // AFTER THE RESET!!! + iWanderTurns=iMaxWanderTurns; DBG2("DesperateResetToSeeIfAIWorksAgain",iWanderTurns); + }else{ + P->GetAICommand(); DBG2("WanderingDesperatelyNotKnowingWhatToDo",iDesperateResetCountDown); // :) + iDesperateResetCountDown--; + } + + return true; +} + +truth wizautoplay::IsPlayerAutoPlay(character* C) +{ + return C->IsPlayer() && wizautoplay::GetAutoPlayMode()>0; +} + +truth wizautoplay::AutoPlayAIequipConsumeZapReadApply() +{ + humanoid* H = dynamic_cast(P); + if(!H) + return false; + + bool bDidSomething=false; + + ///////////////////////////////// WIELD + + item* iL = H->GetEquipment(LEFT_WIELDED_INDEX); + item* iR = H->GetEquipment(RIGHT_WIELDED_INDEX); + + //every X turns remove all equipments + bool bTryWieldNow=false; + static int iLastReEquipAllTurn=-1; + if(game::GetTurn()>(iLastReEquipAllTurn+100)){ DBG2(game::GetTurn(),iLastReEquipAllTurn); + iLastReEquipAllTurn=game::GetTurn(); + DBG1("UnequipAll"); + for(int i=0;iGetEquipment(i); + if(eq){eq->MoveTo(H->GetStack());H->SetEquipment(i,NULL);} //eq is moved to end of stack! + if(iL==eq)iL=NULL; + if(iR==eq)iR=NULL; + } +// if(iL!=NULL){iL->MoveTo(GetStack());iL=NULL;SetEquipment(LEFT_WIELDED_INDEX ,NULL);} +// if(iR!=NULL){iR->MoveTo(GetStack());iR=NULL;SetEquipment(RIGHT_WIELDED_INDEX,NULL);} + bTryWieldNow=true; + } + + //wield some weapon from the inventory as the NPC AI is not working for the player TODO why? + //every X turns try to wield + static int iLastTryToWieldTurn=-1; + if(bTryWieldNow || game::GetTurn()>(iLastTryToWieldTurn+10)){ DBG2(game::GetTurn(),iLastTryToWieldTurn); + iLastTryToWieldTurn=game::GetTurn(); + bool bDoneLR=false; + bool bL2H = iL && iL->IsTwoHanded(); + bool bR2H = iR && iR->IsTwoHanded(); + + //2handed + static int iTryWieldWhat=0; iTryWieldWhat++; DBG1(iTryWieldWhat); + if(iTryWieldWhat%2==0){ //will try 2handed first, alternating. If player has only 2handeds, the 1handeds will not be wielded and it will use punches, what is good too for tests. + if( !bDoneLR && + iL==NULL && H->GetBodyPartOfEquipment(LEFT_WIELDED_INDEX )!=NULL && + iR==NULL && H->GetBodyPartOfEquipment(RIGHT_WIELDED_INDEX)!=NULL + ){ + static itemvector vitEqW;vitEqW.clear();H->GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(vitEqW[c]->IsWeapon(P) && vitEqW[c]->IsTwoHanded()){ DBG1(vitEqW[c]->GetNameSingular().CStr()); + vitEqW[c]->RemoveFromSlot(); + DBG2("Wield2hd",vitEqW[c]->GetNameSingular().CStr()); + H->SetEquipment(clock()%2==0 ? LEFT_WIELDED_INDEX : RIGHT_WIELDED_INDEX, vitEqW[c]); //DBG3("Wield",iEqIndex,vitEqW[c]->GetName(DEFINITE).CStr()); + bDoneLR=true; + break; + } + } + } + } + + //dual 1handed (if not 2hd already) + if(!bDoneLR){ + for(int i=0;i<2;i++){ + int iChk=-1; + if(i==0)iChk=LEFT_WIELDED_INDEX; + if(i==1)iChk=RIGHT_WIELDED_INDEX; + + if( + !bDoneLR && + ( + (iChk==LEFT_WIELDED_INDEX && iL==NULL && H->GetBodyPartOfEquipment(LEFT_WIELDED_INDEX ) && !bR2H) + || + (iChk==RIGHT_WIELDED_INDEX && iR==NULL && H->GetBodyPartOfEquipment(RIGHT_WIELDED_INDEX) && !bL2H) + ) + ){ + static itemvector vitEqW;vitEqW.clear();H->GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if( + (vitEqW[c]->IsWeapon(P) && !vitEqW[c]->IsTwoHanded()) + || + vitEqW[c]->IsShield(P) + ){ + DBG2("WieldDual",vitEqW[c]->GetNameSingular().CStr()); + vitEqW[c]->RemoveFromSlot(); + H->SetEquipment(iChk, vitEqW[c]); + bDoneLR=true; + break; + } + } + } + } + } + + } + + //every X turns try to use stuff from inv + static int iLastTryToUseInvTurn=-1; + if(game::GetTurn()>(iLastTryToUseInvTurn+5)){ + DBG2(game::GetTurn(),iLastTryToUseInvTurn); + iLastTryToUseInvTurn=game::GetTurn(); + + //////////////////////////////// consume food/drink + { //TODO let this happen for non-human too? + static itemvector vitEqW;vitEqW.clear();H->GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(clock()%3!=0 && H->GetHungerState() >= BLOATED)break; //randomly let it vomit and activate all related flows *eew* xD + + //if(TryToConsume(vitEqW[c])) + material* ConsumeMaterial = vitEqW[c]->GetConsumeMaterial(P); + if( + ConsumeMaterial!=NULL && + vitEqW[c]->IsConsumable() && + !H->HasHadBodyPart(vitEqW[c]) && //this avoids a slow interactive question + H->ConsumeItem(vitEqW[c], vitEqW[c]->GetConsumeMaterial(P)->GetConsumeVerb()) + ){ + DBG2("AutoPlayConsumed",vitEqW[c]->GetNameSingular().CStr()); + bDidSomething=true; + break; + } + } + } + + //////////////////////////////// equip armor ring amulet etc + { + static itemvector vitEqW;vitEqW.clear();H->GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(H->TryToEquip(vitEqW[c],true)){ + DBG2("EquipItem",vitEqW[c]->GetNameSingular().CStr()); + bDidSomething=true; + break; + }else{ + vitEqW[c]->MoveTo(H->GetStack()); //was dropped, get back, will be in the end of the stack! :) + } + } + } + + //////////////////////////////// zap + static int iLastZapTurn=-1; + if(game::GetTurn()>(iLastZapTurn+30)){ + DBG2(game::GetTurn(),iLastZapTurn); //every X turns try to use stuff from inv + iLastZapTurn=game::GetTurn(); + + int iDir=clock()%(8+1); // index 8 is the macro YOURSELF already... if(iDir==8)iDir=YOURSELF; + static itemvector vitEqW;vitEqW.clear();H->GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(!vitEqW[c]->IsZappable(P))continue; + + if(vitEqW[c]->IsZapWorthy(P)){ + if(vitEqW[c]->Zap(P, H->GetPos(), iDir)){ + DBG2(iLastZapTurn,vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs + bDidSomething=true; + break; + } + }else{ + if(vitEqW[c]->Apply(P)){ + DBG2(iLastZapTurn,vitEqW[c]->GetNameSingular().CStr()); + bDidSomething=true; + break; + } + } + } + } + + //////////////////////////////// read books and scrolls + static int iLastReadTurn=-1; + if(game::GetTurn()>(iLastReadTurn+15)){ DBG2(game::GetTurn(),iLastReadTurn); //every X turns try to use stuff from inv + iLastReadTurn=game::GetTurn(); + + static itemvector vitEqW;vitEqW.clear();H->GetStack()->FillItemVector(vitEqW); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(!vitEqW[c]->IsReadable(P))continue; + + static holybook* hb;hb = dynamic_cast(vitEqW[c]); + if(hb){ + if(vitEqW[c]->Read(P)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs + DBG2(iLastReadTurn,vitEqW[c]->GetNameSingular().CStr()); + bDidSomething=true; + break; + } + } + + static scroll* Scroll; Scroll = dynamic_cast(vitEqW[c]); + if( //some are simple (just read to work imediately) + dynamic_cast(Scroll) || + dynamic_cast(Scroll) || + dynamic_cast(Scroll) || + false //dummy + ){ + DBG2(iLastReadTurn,vitEqW[c]->GetNameSingular().CStr()); + Scroll->Read(P); + bDidSomething=true; + break; + } + } + } + + //////////////////////////////// apply things + static int iLastApplyTurn=-1; + if(game::GetTurn()>(iLastApplyTurn+40)){ + DBG2(game::GetTurn(),iLastApplyTurn); //every X turns try to use stuff from inv + iLastApplyTurn=game::GetTurn(); + + static itemvector vitEqW;vitEqW.clear();H->GetStack()->FillItemVector(vitEqW); + static itemvector vitA;vitA.clear(); + if(H->GetLeftWielded())vitEqW.push_back(H->GetLeftWielded()); + if(H->GetRightWielded())vitEqW.push_back(H->GetRightWielded()); + for(uint c = 0; c < vitEqW.size(); ++c){ + if(AutoPlayAIcanApply(vitEqW[c])) + vitA.push_back(vitEqW[c]); + } + + if(vitA.size()){ + item* itA = vitA[clock()%vitA.size()]; + DBG2(iLastApplyTurn,itA->GetNameSingular().CStr()); + itA->Apply(P); + bDidSomething=true; + } + } + + } + + return bDidSomething; +} + +void wizautoplay::IncAutoPlayMode() { +// if(!globalwindowhandler::IsKeyTimeoutEnabled()){ +// if(AutoPlayMode>=2){ +// AutoPlayMode=0; // TIMEOUT was disabled there at window handler! so reset here. +// AutoPlayModeApply(); +// } +// } + + ++AutoPlayMode; + if(AutoPlayMode>AUTOPLAYMODE_FRENZY)AutoPlayMode=AUTOPLAYMODE_DISABLED; + + AutoPlayModeApply(); +} + +#ifdef WIZARD +void wizautoplay::AutoPlayModeApply(){ + int iTimeout=0; + bool bPlayInBackground=false; + + const char* msg; + switch(wizautoplay::AutoPlayMode){ + case AUTOPLAYMODE_DISABLED: + // disabled + msg="%s says \"I can rest now.\""; + break; + case AUTOPLAYMODE_NOTIMEOUT: + // no timeout, user needs to hit '.' to it autoplay once, the behavior is controled by AutoPlayMode AND the timeout delay that if 0 will have no timeout but will still autoplay. + msg="%s says \"I won't rest!\""; + break; + case AUTOPLAYMODE_SLOW: // TIMEOUTs key press from here to below + msg="%s says \"I can't wait anymore!\""; + iTimeout=(1000); + bPlayInBackground=true; + break; + case AUTOPLAYMODE_FAST: + msg="%s says \"I am in a hurry!\""; + iTimeout=(1000/2); + bPlayInBackground=true; + break; + case AUTOPLAYMODE_FRENZY: + msg="%s says \"I... *frenzy* yeah! Try to follow me now! Hahaha!\""; + iTimeout=10;//min possible to be fastest //(1000/10); // like 10 FPS, so user has 100ms chance to disable it + bPlayInBackground=true; + break; + } + ADD_MESSAGE(msg, P->GetName(DEFINITE).CStr()); + + globalwindowhandler::SetPlayInBackground(bPlayInBackground); + + if(!ivanconfig::IsXBRZScale()){ + /** + * TODO + * This is an horrible gum solution... + * I still have no idea why this happens. + * Autoplay will timeout 2 times slower if xBRZ is disabled! why!??!?!? + * But the debug log shows the correct timeouts :(, clueless for now... + */ + iTimeout/=2; + } + + globalwindowhandler::SetKeyTimeout(iTimeout,'.');//,'~'); +} +#endif + +void wizautoplay::AutoPlayCommandKey(character* C,int& Key,truth& HasActed,truth& ValidKeyPressed) +{ + P = C; + + if(wizautoplay::IsPlayerAutoPlay()){ + bool bForceStop = false; + if(wizautoplay::GetAutoPlayMode()>=AUTOPLAYMODE_SLOW) + bForceStop = globalwindowhandler::IsKeyPressed(SDL_SCANCODE_ESCAPE); + + if(!bForceStop && Key=='.'){ // pressed or simulated + if(game::IsInWilderness()){ + Key='>'; //blindly tries to go back to the dungeon safety :) TODO target and move to other dungeons/towns in the wilderness + }else{ + HasActed = wizautoplay::AutoPlayAICommand(Key); DBG2("Simulated",Key); + if(HasActed)ValidKeyPressed = true; //valid simulated action + } + }else{ + /** + * if the user hits any key during the autoplay mode that runs by itself, it will be disabled. + * at non auto mode, can be moved around but cannot rest or will move by itself + */ + if(wizautoplay::GetAutoPlayMode()>=AUTOPLAYMODE_SLOW && (Key!='~' || bForceStop)){ + wizautoplay::DisableAutoPlayMode(); + AutoPlayAIReset(true); // this will help on re-randomizing things, mainly paths + } + } + } +} \ No newline at end of file From d21a7777e559fd8d1c84358ddcd19a5def29d901 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 21 Apr 2020 23:22:09 -0300 Subject: [PATCH 151/235] Having material manual at inventory, when using craft check strength or inspect, will auto inscribe book info on item. WIP-cursedDeveloper: improving; --- Main/Source/cmdcraft.cpp | 29 ++++++++++++++++++----------- Main/Source/curseddeveloper.cpp | 18 ++++++++++++++++-- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index a90dbff88..da351b8f9 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -1995,6 +1995,21 @@ struct srpDismantle : public recipe{ //TODO this is instantaneous, should take t } };srpDismantle rpDismantle; +void addMaterialInfo(character* C,item* it){ + itemvector v; + material* matM = it->GetMainMaterial(); + C->GetStack()->FillItemVector(v); + if(!it->HasTag('m')){ //material info transfered to item from + for(int i=0;i(v[i])){ + it->SetTag('m'); + it->SetLabel(it->GetLabel()+"s"+matM->GetStrengthValue()+"f"+matM->GetFlexibility()); + break; + } + } + } +} + struct srpInspect : public recipe{ //TODO this is instantaneous, should take time? virtual void fillInfo(){ init("inspect","item materials"); @@ -2023,17 +2038,7 @@ struct srpInspect : public recipe{ //TODO this is instantaneous, should take tim if(matM||matS){ ADD_MESSAGE("%s",fs.CStr()); - itemvector v; - rpd.rc.H()->GetStack()->FillItemVector(v); - if(!it0->HasTag('m')){ //material info transfered to item from - for(int i=0;i(v[i])){ - it0->SetTag('m'); - it0->SetLabel(it0->GetLabel()+"s"+matM->GetStrengthValue()+"f"+matM->GetFlexibility()); - } - break; - } - } + addMaterialInfo(rpd.rc.H(),it0); craftcore::CraftSkillAdvance(rpd); }else{ ADD_MESSAGE("You can't inspect %s.",it0->GetName(INDEFINITE).CStr()); @@ -2099,6 +2104,8 @@ struct srpResistanceVS : public recipe{ //TODO this is instantaneous, should tak } rpd.SetAlreadyExplained(); + addMaterialInfo(rpd.rc.H(),it0); + addMaterialInfo(rpd.rc.H(),it1); craftcore::CraftSkillAdvance(rpd); return true; diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 817221e90..56782bde4 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -87,11 +87,24 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) if(P->GetAction()) P->GetAction()->Terminate(false); //just to avoid messing any action - + + // at death spot + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(BLOOD, 30 * P->GetAttribute(ENDURANCE))); + if(!bStay && Killer && !game::IsInWilderness()){ game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here."); + + // teleport P->Move(P->GetLevel()->GetRandomSquare(P), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn + + // at resurrect spot + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(TELEPORT_FLUID, 30 * P->GetAttribute(MANA))); + if(iDebuff>0) + P->GetLSquareUnder()->AddSmoke(gas::Spawn(GOOD_WONDER_STAFF_VAPOUR, 100)); } + + // at resurrect spot + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); ADD_MESSAGE("But wait... you are cursed, therefore forbidden to R.I.P... and your doings will be forever forgotten..."); return true; @@ -180,7 +193,8 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); - + Killer->GetLSquareUnder()->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, 100)); + rbRev=true; return true; From 049625ffa11e78c5f99e687015730f25c8c25143 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 22 Apr 2020 00:57:41 -0300 Subject: [PATCH 152/235] Auto map note when constructing some OLTerrain. Reusable font size to position multi colored text better. WIP-cursedDeveloper: improving; --- FeLib/Include/rawbit.h | 2 ++ FeLib/Source/rawbit.cpp | 6 +++--- Main/Source/char.cpp | 14 +++++++++----- Main/Source/cmdcraft.cpp | 1 + Main/Source/curseddeveloper.cpp | 27 ++++++++++++++------------- Main/Source/game.cpp | 2 +- 6 files changed, 30 insertions(+), 22 deletions(-) diff --git a/FeLib/Include/rawbit.h b/FeLib/Include/rawbit.h index 5d1df428b..d1d1b46ab 100644 --- a/FeLib/Include/rawbit.h +++ b/FeLib/Include/rawbit.h @@ -49,6 +49,7 @@ class rawbitmap cpackalpha* = 0, cuchar* = 0, cuchar* = 0, truth = true) const; v2 GetSize() const { return Size; } + v2 GetFontSize() { return v2FontSize; } void AlterGradient(v2, v2, int, int, truth); void SwapColors(v2, v2, int, int); @@ -74,6 +75,7 @@ class rawbitmap uchar* Palette; paletteindex** PaletteBuffer; fontcache FontCache; + v2 v2FontSize=v2(8,8); //TODO everywhere using 8 to calculate font size should use this variable, so one day we can have dynamic size font, after that we can set this var to any other proper value }; #endif diff --git a/FeLib/Source/rawbit.cpp b/FeLib/Source/rawbit.cpp index eb07c6118..2b9db17db 100644 --- a/FeLib/Source/rawbit.cpp +++ b/FeLib/Source/rawbit.cpp @@ -436,8 +436,8 @@ void rawbitmap::Printf(bitmap* Bitmap, v2 Pos, packcol16 Color, cchar* Format, . { v2 F(((Buffer[c] - 0x20) & 0xF) << 4, (Buffer[c] - 0x20) & 0xF0); //printf("X=%4d -- Y=%d\n", F.X, F.Y); - MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3) + 1, Pos.Y + 1), v2(8, 8), &ShadeCol); - MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3), Pos.Y), v2(8, 8), &Color); + MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3) + 1, Pos.Y + 1), v2FontSize, &ShadeCol); + MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3), Pos.Y), v2FontSize, &Color); } } else @@ -451,7 +451,7 @@ void rawbitmap::Printf(bitmap* Bitmap, v2 Pos, packcol16 Color, cchar* Format, . TRANSPARENT_COLOR, 0 }; - for(int c = 0; Buffer[c]; ++c, B.Dest.X += 8) + for(int c = 0; Buffer[c]; ++c, B.Dest.X += v2FontSize.X) { B.Src.X = ((Buffer[c] - 0x20) & 0xF) << 4; B.Src.Y = (Buffer[c] - 0x20) & 0xF0; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 9333f6c59..6f72193e7 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -5201,7 +5201,15 @@ void character::DrawPanel(truth AnimationDraw) const v2(RES.X - 19 - (game::GetMaxScreenXSize() << 4), RES.Y)); igraph::BlitBackGround(v2(16, 45 + (game::GetMaxScreenYSize() << 4)), v2(game::GetMaxScreenXSize() << 4, 9)); - FONT->Printf(DOUBLE_BUFFER, v2(16, 45 + (game::GetMaxScreenYSize() << 4)), WHITE, "%s", GetPanelName().CStr()); + int iLeftPos=0; +#ifdef CURSEDDEVELOPER + if(cursedDeveloper::IsCursedDeveloper()){ + festring fsCD="(Cursed Developer) "; + iLeftPos+=fsCD.GetSize()*FONT->GetFontSize().X; + FONT->Printf(DOUBLE_BUFFER, v2(16, 45 + (game::GetMaxScreenYSize() << 4)), YELLOW, fsCD.CStr(), GetPanelName().CStr()); + } +#endif + FONT->Printf(DOUBLE_BUFFER, v2(16+iLeftPos, 45 + (game::GetMaxScreenYSize() << 4)), WHITE, "%s", GetPanelName().CStr()); game::UpdateAttributeMemory(); int PanelPosX = RES.X - 96; int PanelPosY = DrawStats(false); @@ -8055,10 +8063,6 @@ festring character::GetPanelName() const festring PanelName; if(!game::IsInWilderness()){ -#ifdef CURSEDDEVELOPER - if(cursedDeveloper::IsCursedDeveloper()) - PanelName << "[Cursed Developer!] "; -#endif PanelName << Name; if(ivanconfig::IsShowFullDungeonName()){ PanelName << " (at " << game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex(), true) << ')'; diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index da351b8f9..cfbcbf478 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -577,6 +577,7 @@ olterrain* crafthandle::SpawnTerrain(recipedata& rpd, festring& fsCreated){ switch(rpd.otSpawnType){ case CTT_FURNITURE: otSpawn=decoration::Spawn(rpd.otSpawnCfg); + game::CheckAddAutoMapNote(otSpawn->GetLSquareUnder()); break; case CTT_DOOR: otSpawn=door::Spawn(rpd.otSpawnCfg); diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 56782bde4..22019f89b 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -35,11 +35,11 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) character* P = game::GetPlayer(); game::DrawEverything(); - int iBuff=0,iDebuff=0; + int iKillerBuff=0,iKillerDebuff=0; bool bRev; - bool bStay = BuffAndDebuffPlayerKiller(Killer,iBuff,iDebuff,bRev); //to spice it up + bool bStay = BuffAndDebuffPlayerKiller(Killer,iKillerBuff,iKillerDebuff,bRev); //to spice it up if(!bStay) - Killer->SetAssignedName(festring()+"[B"+iBuff+"D"+iDebuff+(bRev?"R":"")+"]"); //player killed count + Killer->SetAssignedName(festring()+"[B"+iKillerBuff+"D"+iKillerDebuff+(bRev?"R":"")+"]"); //player killed count // save life but just a little bit for(int c = 0; c < P->BodyParts; ++c){ //only enough to continue testing normal gameplay @@ -94,19 +94,20 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) if(!bStay && Killer && !game::IsInWilderness()){ game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here."); - // teleport - P->Move(P->GetLevel()->GetRandomSquare(P), true); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn - - // at resurrect spot - P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(TELEPORT_FLUID, 30 * P->GetAttribute(MANA))); - if(iDebuff>0) - P->GetLSquareUnder()->AddSmoke(gas::Spawn(GOOD_WONDER_STAFF_VAPOUR, 100)); + //teleport is required to prevent death loop: killer keeps killing the player forever on every turn + P->TeleportRandomly(true); } // at resurrect spot - P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); - - ADD_MESSAGE("But wait... you are cursed, therefore forbidden to R.I.P... and your doings will be forever forgotten..."); + if(iKillerDebuff>0){ // if enemy got over powerful, buff the player randomly + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); + P->GetLSquareUnder()->AddSmoke(gas::Spawn(GOOD_WONDER_STAFF_VAPOUR, 100)); + } + + ADD_MESSAGE("But wait... you are cursed, forbidden to R.I.P... and your doings will be forever forgotten..."); + + game::DrawEverything(); + return true; } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 8c9ba308c..770ca620a 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1536,7 +1536,7 @@ void game::DrawMapNotesOverlay(bitmap* buffer) //TODO draw to a bitmap in the 1st call and just fast blit it later (with mask), unless it becomes animated in some way. int iLineHeightPixels=15; //line height in pixels - int iFontWidth=8; //font width + int iFontWidth=FONT->GetFontSize().X; int iM=3; //margin const static int iTotCol=5; From 9056dbe64a87499bb233ebe80ec61cd74178fd29 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 22 Apr 2020 01:31:49 -0300 Subject: [PATCH 153/235] fixing wizard and curseddeveloper code availability based on related defines pointed by lgtm and travisci. --- Main/Include/curseddeveloper.h | 10 ++++++++-- Main/Include/wizautoplay.h | 26 +++++++++++++------------- Main/Source/char.cpp | 7 +++++++ Main/Source/curseddeveloper.cpp | 8 ++------ Main/Source/wizautoplay.cpp | 21 ++++++++++++--------- 5 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h index f23b2d19e..12dba8f90 100644 --- a/Main/Include/curseddeveloper.h +++ b/Main/Include/curseddeveloper.h @@ -14,12 +14,18 @@ #define __CURSEDDEVELOPER_H__ class cursedDeveloper { +#ifdef CURSEDDEVELOPER public: - static bool BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev); - static bool LifeSaveJustABit(character* Killer); static bool IsCursedDeveloper(){return bCursedDeveloper;}; + static bool LifeSaveJustABit(character* Killer); + static bool BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev); private: static bool bCursedDeveloper; +#else + public: + static bool IsCursedDeveloper(){return false;} + static bool LifeSaveJustABit(character* Killer){return false;} +#endif }; #endif //__CURSEDDEVELOPER_H__ diff --git a/Main/Include/wizautoplay.h b/Main/Include/wizautoplay.h index bb95b23cf..e2ef17441 100644 --- a/Main/Include/wizautoplay.h +++ b/Main/Include/wizautoplay.h @@ -21,6 +21,12 @@ class wizautoplay { + public: + static int GetMaxValueless(){return iMaxValueless;} + private: + static int iMaxValueless; + +#ifdef WIZARD public: static void AutoPlayCommandKey(character* C,int& Key,truth& HasActed,truth& ValidKeyPressed); static truth AutoPlayAICommand(int&); @@ -39,28 +45,22 @@ class wizautoplay static truth AutoPlayAISetAndValidateKeepGoingTo(v2 v2KGTo); static void AutoPlayAITeleport(bool bDeathCountBased); static void AutoPlayAIReset(bool bFailedToo); - static int GetMaxValueless(){return iMaxValueless;} static truth AutoPlayAIequipConsumeZapReadApply(); - static truth IsPlayerAutoPlay(character* C); - -#ifdef WIZARD static void IncAutoPlayMode(); - static int GetAutoPlayMode() { return AutoPlayMode; } static void AutoPlayModeApply(); static void DisableAutoPlayMode() {AutoPlayMode=AUTOPLAYMODE_DISABLED;AutoPlayModeApply();} -#else - static int GetAutoPlayMode() { return AUTOPLAYMODE_DISABLED; } -#endif + static truth IsPlayerAutoPlay(character* C); + static int GetAutoPlayMode() { return AutoPlayMode; } private: static truth IsPlayerAutoPlay(){return IsPlayerAutoPlay(P);}; - /** - * 5 seems good, broken cheap weapons, stones, very cheap weapons non broken etc - * btw, lantern price is currently 10. - */ - static int iMaxValueless; static character* P; static int AutoPlayMode; +#else + public: + static truth IsPlayerAutoPlay(character* C){return false;} + static int GetAutoPlayMode() { return AUTOPLAYMODE_DISABLED; } +#endif }; #endif //__WIZAUTOPLAY_H__ diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 6f72193e7..a56f37f92 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1693,9 +1693,12 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) { ADD_MESSAGE("You die."); +#ifdef CURSEDDEVELOPER if(cursedDeveloper::LifeSaveJustABit((character*)Killer)) return; +#endif +#ifdef WIZARD if(game::WizardModeIsActive()) { game::DrawEverything(); @@ -1713,6 +1716,8 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) return; } } +#endif + } else if(CanBeSeenByPlayer() && !(DeathFlags & DISALLOW_MSG)) ProcessAndAddMessage(GetDeathMessage()); @@ -4008,8 +4013,10 @@ void character::TeleportRandomly(truth Intentional) if(GetAction() && GetAction()->IsVoluntary()) GetAction()->Terminate(false); +#ifdef WIZARD if(wizautoplay::IsPlayerAutoPlay(this)) wizautoplay::AutoPlayAIReset(true); +#endif // There's a small chance that some warp gas/fluid is left behind. if(FromSquare->IsFlyable() && !RAND_N(1000)) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 22019f89b..213292d2c 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -18,15 +18,10 @@ /** * This is a developer environment variable to test the game without wizard mode. */ + #ifdef CURSEDDEVELOPER bool cursedDeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); -#else -bool cursedDeveloper::bCursedDeveloper = false; -#endif -#ifndef CURSEDDEVELOPER -bool cursedDeveloper::LifeSaveJustABit(character* Killer){return false;} -#else bool cursedDeveloper::LifeSaveJustABit(character* Killer) { if(!bCursedDeveloper) @@ -200,4 +195,5 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in return true; } + #endif //CURSEDDEVELOPER diff --git a/Main/Source/wizautoplay.cpp b/Main/Source/wizautoplay.cpp index 3d11d4043..4f6fd4205 100644 --- a/Main/Source/wizautoplay.cpp +++ b/Main/Source/wizautoplay.cpp @@ -14,7 +14,16 @@ #include "dbgmsgproj.h" +/** + * 5 seems good, broken cheap weapons, stones, very cheap weapons non broken etc + * btw, lantern price is currently 10. + */ +int wizautoplay::iMaxValueless = 5; + +#ifdef WIZARD + int wizautoplay::AutoPlayMode=AUTOPLAYMODE_DISABLED; + character* wizautoplay::P=NULL; bool bSafePrayOnce=false; @@ -49,12 +58,6 @@ character* AutoPlayLastChar=NULL; const int iMaxWanderTurns=20; const int iMinWanderTurns=3; -/** - * 5 seems good, broken cheap weapons, stones, very cheap weapons non broken etc - * btw, lantern price is currently 10. - */ -int wizautoplay::iMaxValueless = 5; - v2 v2KeepGoingTo=v2(0,0); v2 v2TravelingToAnotherDungeon=v2(0,0); int iWanderTurns=iMinWanderTurns; @@ -1285,7 +1288,6 @@ void wizautoplay::IncAutoPlayMode() { AutoPlayModeApply(); } -#ifdef WIZARD void wizautoplay::AutoPlayModeApply(){ int iTimeout=0; bool bPlayInBackground=false; @@ -1333,7 +1335,6 @@ void wizautoplay::AutoPlayModeApply(){ globalwindowhandler::SetKeyTimeout(iTimeout,'.');//,'~'); } -#endif void wizautoplay::AutoPlayCommandKey(character* C,int& Key,truth& HasActed,truth& ValidKeyPressed) { @@ -1362,4 +1363,6 @@ void wizautoplay::AutoPlayCommandKey(character* C,int& Key,truth& HasActed,truth } } } -} \ No newline at end of file +} + +#endif From 9283cd6ed1d543ba20e6f4e0ccad569073f70ddc Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 22 Apr 2020 02:14:53 -0300 Subject: [PATCH 154/235] WIP-cursedDeveloper: improving; --- Main/Include/curseddeveloper.h | 3 +++ Main/Source/char.cpp | 5 +++-- Main/Source/curseddeveloper.cpp | 21 +++++++++++++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h index 12dba8f90..e3855e16c 100644 --- a/Main/Include/curseddeveloper.h +++ b/Main/Include/curseddeveloper.h @@ -17,13 +17,16 @@ class cursedDeveloper { #ifdef CURSEDDEVELOPER public: static bool IsCursedDeveloper(){return bCursedDeveloper;}; + static bool IsCursedDeveloperTeleport(){return bCursedDeveloperTeleport;} static bool LifeSaveJustABit(character* Killer); static bool BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev); private: static bool bCursedDeveloper; + static bool bCursedDeveloperTeleport; #else public: static bool IsCursedDeveloper(){return false;} + static bool IsCursedDeveloperTeleport(){return false;} static bool LifeSaveJustABit(character* Killer){return false;} #endif }; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index a56f37f92..f2ae47937 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -3994,10 +3994,11 @@ void character::TeleportRandomly(truth Intentional) else if(IsPlayer()) { // This is to prevent uncontrolled teleportation from going unnoticed by players. - game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); + if(!cursedDeveloper::IsCursedDeveloperTeleport()) + game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); } - if(IsPlayer()) + if(IsPlayer() && !cursedDeveloper::IsCursedDeveloperTeleport()) ADD_MESSAGE("A rainbow-colored whirlpool twists the existence around you. " "You are sucked through a tunnel piercing a myriad of surreal " "universes. Luckily you return to this dimension in one piece."); diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 213292d2c..9108ac84f 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -21,6 +21,7 @@ #ifdef CURSEDDEVELOPER bool cursedDeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); +bool cursedDeveloper::bCursedDeveloperTeleport = false; bool cursedDeveloper::LifeSaveJustABit(character* Killer) { @@ -33,8 +34,15 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) int iKillerBuff=0,iKillerDebuff=0; bool bRev; bool bStay = BuffAndDebuffPlayerKiller(Killer,iKillerBuff,iKillerDebuff,bRev); //to spice it up - if(!bStay) - Killer->SetAssignedName(festring()+"[B"+iKillerBuff+"D"+iKillerDebuff+(bRev?"R":"")+"]"); //player killed count + if(!bStay){ + festring fsAN = Killer->GetAssignedName(); + festring fsToken=" <[B"; + ulong pos = fsAN.Find(fsToken); + if(pos!=festring::NPos) + fsAN.Erase(pos,fsAN.GetSize()-pos); + fsAN<"; + Killer->SetAssignedName(fsAN); + } // save life but just a little bit for(int c = 0; c < P->BodyParts; ++c){ //only enough to continue testing normal gameplay @@ -90,16 +98,17 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here."); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn + bCursedDeveloperTeleport=true; P->TeleportRandomly(true); + ADD_MESSAGE("You see a flash of dark light and teleport away from the killing blow!"); + bCursedDeveloperTeleport=false; } // at resurrect spot - if(iKillerDebuff>0){ // if enemy got over powerful, buff the player randomly + if(iKillerDebuff>0) // if enemy got too powerful, buff the player randomly P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); - P->GetLSquareUnder()->AddSmoke(gas::Spawn(GOOD_WONDER_STAFF_VAPOUR, 100)); - } - ADD_MESSAGE("But wait... you are cursed, forbidden to R.I.P... and your doings will be forever forgotten..."); + ADD_MESSAGE("Your curse forbids you to rest and be remembered..."); game::DrawEverything(); From fcdf5140c46fac9bd2e1b65ecdc2d27b77cfa5d4 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 22 Apr 2020 03:37:34 -0300 Subject: [PATCH 155/235] Added more automatic colors to map notes; fixed(restored) SetMapNote() behavior; WIP-cursedDeveloper: improving; --- Main/Source/command.cpp | 4 +--- Main/Source/curseddeveloper.cpp | 11 ++++++----- Main/Source/game.cpp | 26 ++++++++++++++++++++++---- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index e29ce39b8..1a1970526 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1085,9 +1085,7 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng What=c; if(What.GetSize()>0){ if(What[0]==game::MapNoteToken()){ //having map note token means it is already a map note, so let it be read/write at will - std::string str=What.CStr(); - What.Empty(); - What<HasBeenSeen()){ /***** diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 9108ac84f..0e11f0a24 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -95,7 +95,8 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(BLOOD, 30 * P->GetAttribute(ENDURANCE))); if(!bStay && Killer && !game::IsInWilderness()){ - game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here."); + if(!P->GetLSquareUnder()->GetEngraved()) + game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here@"); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn bCursedDeveloperTeleport=true; @@ -141,9 +142,9 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in riBuff++; if(!Killer->HasStateFlag(SLOW)) if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return false;} - riBuff++; - if(!Killer->HasStateFlag(HICCUPS)) - if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return false;} +// riBuff++; +// if(!Killer->HasStateFlag(HICCUPS)) +// if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return false;} riBuff++; if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return false;} riBuff++; @@ -162,7 +163,7 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in // DEBUFFs, after player has taken too much it is time to make it stop, but slowly: riDebuff=1; if(!Killer->HasStateFlag(HICCUPS)){ - Killer->DeActivateTemporaryState(INVISIBLE); +// Killer->DeActivateTemporaryState(INVISIBLE); Killer->GainIntrinsic(HICCUPS); return false; } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 770ca620a..1bea74847 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1267,15 +1267,24 @@ truth game::OnScreen(v2 Pos) && Pos.X < GetCamera().X + GetScreenXSize() && Pos.Y < GetCamera().Y + GetScreenYSize(); } -/** - * prepend - */ +//static int iMaxNoteLength=100; +//void game::AppendMapNote(lsquare* lsqrN,festring What) +//{ +// festring finalWhat; +// finalWhat << game::MapNoteToken(); +// finalWhat << What; +// if(lsqrN->GetEngraved()) +// finalWhat << " " << lsqrN->GetEngraved(); +// static int iMaxLength=100; +// if(finalWhat.GetSize()>iMaxLength) +// finalWhat.Resize(iMaxLength); +// lsqrN->Engrave(finalWhat); +//} void game::SetMapNote(lsquare* lsqrN,festring What) { festring finalWhat; finalWhat << game::MapNoteToken(); finalWhat << What; - if(lsqrN->GetEngraved())finalWhat << " " << lsqrN->GetEngraved(); lsqrN->Engrave(finalWhat); } @@ -1597,6 +1606,15 @@ void game::DrawMapNotesOverlay(bitmap* buffer) // col16 colBkg = iNoteHighlight==i ? colBkg=YELLOW : colMapNoteBkg; if(validateV2(bkgTL,buffer,bkgB)){ col16 colMapNoteBkg2=colMapNoteBkg; + if(festring(vMapNotes[i].note).Find("@")!=festring::NPos) + colMapNoteBkg2=BLACK; + else + if(festring(vMapNotes[i].note).Find("?")!=festring::NPos) + colMapNoteBkg2=GREEN; + else + if(festring(vMapNotes[i].note).Find("!!!")!=festring::NPos) + colMapNoteBkg2=YELLOW; + else if(festring(vMapNotes[i].note).Find("!!")!=festring::NPos) colMapNoteBkg2=RED; else From df673d774ec89f28336809ca530500233389d0c0 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 22 Apr 2020 04:05:11 -0300 Subject: [PATCH 156/235] WIP-cursedDeveloper: improving; --- Main/Source/curseddeveloper.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 0e11f0a24..81fdebd85 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -126,19 +126,19 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in if(!bCursedDeveloper)return true; if(!Killer)return true; - riBuff=1; riDebuff=0; rbRev=false; // BUFFs, every death makes it harder to player: + riBuff=1; if(!Killer->HasStateFlag(ESP)){Killer->GainIntrinsic(ESP);return false;} riBuff++; if(!Killer->HasStateFlag(INFRA_VISION)){Killer->GainIntrinsic(INFRA_VISION);return false;} - riBuff++; - if(!Killer->HasStateFlag(VAMPIRISM)){Killer->GainIntrinsic(VAMPIRISM);return false;} - riBuff++; - if(!Killer->HasStateFlag(PANIC)) - if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return false;} +// riBuff++; +// if(!Killer->HasStateFlag(VAMPIRISM)){Killer->GainIntrinsic(VAMPIRISM);return false;} +// riBuff++; +// if(!Killer->HasStateFlag(PANIC)) +// if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return false;} riBuff++; if(!Killer->HasStateFlag(SLOW)) if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return false;} @@ -160,7 +160,9 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in riBuff++; if(!Killer->HasStateFlag(POLYMORPH_LOCK)){Killer->GainIntrinsic(POLYMORPH_LOCK);return false;} - // DEBUFFs, after player has taken too much it is time to make it stop, but slowly: + /************************* + * DEBUFFs, after player has taken too much it is time to make it stop, but slowly: + */ riDebuff=1; if(!Killer->HasStateFlag(HICCUPS)){ // Killer->DeActivateTemporaryState(INVISIBLE); @@ -181,17 +183,17 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in if(!Killer->HasStateFlag(CONFUSED)){Killer->GainIntrinsic(CONFUSED);return false;} riDebuff++; if(!Killer->HasStateFlag(LEPROSY)){Killer->GainIntrinsic(LEPROSY);return false;} - riDebuff++; +// riDebuff++; // this is too much as adds worm mobs on the dungeon... // if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} -// riDebuff++; - if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} riDebuff++; - if(!Killer->HasStateFlag(PANIC)){ - Killer->DeActivateTemporaryState(FEARLESS); - Killer->GainIntrinsic(PANIC); - return true; - } + if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} +// riDebuff++; +// if(!Killer->HasStateFlag(PANIC)){ +// Killer->DeActivateTemporaryState(FEARLESS); +// Killer->GainIntrinsic(PANIC); +// return true; +// } // Revenge, grant it will stop: game::GetCurrentLevel()->Explosion( From b72ae7f896d78cd9f7b3ddfa9c4d7677b5c5f667 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 23 Apr 2020 21:22:59 -0300 Subject: [PATCH 157/235] WIP-cursedDeveloper: improving; --- .../nbproject/configurations.xml | 842 +++++++++++++----- .../AquariusPower/nbproject/project.xml | 4 +- Main/Source/curseddeveloper.cpp | 124 ++- 3 files changed, 691 insertions(+), 279 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index e2e35875a..98008b016 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -2,11 +2,8 @@ - MIDICodes.h MIDIDebug.cpp - MIDIDebug.h MIDIUtils.cpp - MIDIUtils.h RtMidi.cpp audio.cpp audio_stack.cpp @@ -14,27 +11,41 @@ midiparser.cpp midiplayback.cpp + + + + CMakeFiles/3.10.2/CompilerIdC/CMakeCCompilerId.c + + + CMakeFiles/3.10.2/CompilerIdCXX/CMakeCXXCompilerId.cpp + + + CMakeFiles/feature_tests.c + CMakeFiles/feature_tests.cxx + example.cc namegen.cc - curseddeveloper.h - dungeon.h - wizautoplay.h - - - bitmap.h - felist.h - feloops.h - femath.h - festring.h - fetime.h - graphics.h - hscore.h + + + + /usr/share/cmake-3.10/Modules/CMakeCCompilerABI.c + /usr/share/cmake-3.10/Modules/CMakeCXXCompilerABI.cpp + /usr/share/cmake-3.10/Modules/CMakeCompilerABI.h + bitmap.cpp config.cpp @@ -116,12 +127,9 @@ - config.h xbrz.cpp - xbrz.h libxbrzscale.cpp - libxbrzscale.h false false + + + + + + @@ -181,17 +195,9 @@ ${MAKE} -f Makefile clean - - SDL2-2.0.4/include - /usr/include - - CURSEDDEVELOPER DBGMSG FELIST_WAITKEYUP - FIX_LARGECREATURE_TELEPORT_GLITCH - UNIX - USE_SDL WIZARD @@ -201,213 +207,621 @@ ${CMAKE} -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${IDE_CC} -DCMAKE_CXX_COMPILER=${IDE_CXX} -DCMAKE_C_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_CXX_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + CMakeFiles/CMakeTmp + + + + + + + CMakeFiles/CMakeTmp + + + + + + + FeLib/Include + /usr/include/SDL2 + /usr/include/libpng16 + xbrzscale + xbrzscale/xbrz + FeLib + + + BACKTRACE + DATADIR="/home/teique/Projects/ivan/ivan.instDev.THE_MERGED_BRANCHES_I_AM_PLAYING/share" + GCC + IVAN_VERSION="0.58" + + + + + + + FeLib/Include + /usr/include/SDL2 + /usr/include/libpng16 + xbrzscale + xbrzscale/xbrz + FeLib + + + BACKTRACE + DATADIR="/home/teique/Projects/ivan/ivan.instDev.THE_MERGED_BRANCHES_I_AM_PLAYING/share" + GCC + IVAN_VERSION="0.58" + + + + + + + FeLib/Include + /usr/include/SDL2 + /usr/include/libpng16 + xbrzscale + xbrzscale/xbrz + FeLib + + + BACKTRACE + DATADIR="/home/teique/Projects/ivan/ivan.instDev.THE_MERGED_BRANCHES_I_AM_PLAYING/share" + GCC + IVAN_VERSION="0.58" + + + + + + + FeLib/Include + /usr/include/SDL2 + /usr/include/libpng16 + xbrzscale + xbrzscale/xbrz + FeLib + + + BACKTRACE + DATADIR="/home/teique/Projects/ivan/ivan.instDev.THE_MERGED_BRANCHES_I_AM_PLAYING/share" + GCC + IVAN_VERSION="0.58" + + + + + + + FeLib/Include + /usr/include/SDL2 + /usr/include/libpng16 + xbrzscale + xbrzscale/xbrz + FeLib + + + BACKTRACE + DATADIR="/home/teique/Projects/ivan/ivan.instDev.THE_MERGED_BRANCHES_I_AM_PLAYING/share" + GCC + IVAN_VERSION="0.58" + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + UNIX + USE_SDL + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + UNIX + USE_SDL + + + + + + + UNIX + USE_SDL + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + UNIX + USE_SDL + + + + + + + SDL2-2.0.4/include + /usr/include + + + CURSEDDEVELOPER + FIX_LARGECREATURE_TELEPORT_GLITCH + UNIX + USE_SDL + + + + + + + CMakeFiles/CMakeTmp + + + + + CMakeFiles/CMakeTmp + + + + + + + + + + + + + + + + + + diff --git a/.devsPrefs/AquariusPower/nbproject/project.xml b/.devsPrefs/AquariusPower/nbproject/project.xml index 04a97821a..3ae0b57ca 100644 --- a/.devsPrefs/AquariusPower/nbproject/project.xml +++ b/.devsPrefs/AquariusPower/nbproject/project.xml @@ -4,8 +4,8 @@ Ivan.github - - cc,cpp + c + cc,cpp,cxx h UTF-8 diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 81fdebd85..a095ec85a 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -99,10 +99,21 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here@"); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn - bCursedDeveloperTeleport=true; - P->TeleportRandomly(true); - ADD_MESSAGE("You see a flash of dark light and teleport away from the killing blow!"); - bCursedDeveloperTeleport=false; + if(Killer->GetSquaresUnder()>1){ + bCursedDeveloperTeleport=true; + P->TeleportRandomly(true); + ADD_MESSAGE("You see a flash of dark light and teleport away from the killing blow!"); + bCursedDeveloperTeleport=false; + }else{ + bool bRestoreTL=false; + if(Killer->HasStateFlag(TELEPORT_LOCK)){ + Killer->DeActivateTemporaryState(TELEPORT_LOCK); + bRestoreTL=true; + } + Killer->TeleportRandomly(true); + if(bRestoreTL) + Killer->GainIntrinsic(TELEPORT_LOCK); + } } // at resurrect spot @@ -116,6 +127,31 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) return true; } +bool AddState(character* Killer,long Flag,cchar* FlagName,long FlagD,cchar* FlagNameD,int& iB) +{ + if(FlagD && Killer->HasStateFlag(FlagD)){ + DBG5("DEACTIVATING",Killer->GetName(DEFINITE).CStr(),FlagD,FlagNameD,iB); + Killer->DeActivateTemporaryState(FlagD); + } + + DBG5("TRYADD",Killer->GetName(DEFINITE).CStr(),Flag,FlagName,iB); + if(!Killer->HasStateFlag(Flag)){ + Killer->GainIntrinsic(Flag); + if(Killer->HasStateFlag(Flag)){ + iB++; + DBG2("SUCCEED TO ADD!!!",iB); + return true; + }else{ + DBG1("FAILED TO ADD"); + } + }else{ + iB++; + DBG1("HAS ALREADY"); + } + + return false; +} + /** * this will make the NPC that kills the player more challenging for every kill * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? @@ -128,72 +164,34 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in riDebuff=0; rbRev=false; - + // BUFFs, every death makes it harder to player: riBuff=1; - if(!Killer->HasStateFlag(ESP)){Killer->GainIntrinsic(ESP);return false;} - riBuff++; - if(!Killer->HasStateFlag(INFRA_VISION)){Killer->GainIntrinsic(INFRA_VISION);return false;} -// riBuff++; -// if(!Killer->HasStateFlag(VAMPIRISM)){Killer->GainIntrinsic(VAMPIRISM);return false;} -// riBuff++; -// if(!Killer->HasStateFlag(PANIC)) -// if(!Killer->HasStateFlag(FEARLESS)){Killer->GainIntrinsic(FEARLESS);return false;} - riBuff++; - if(!Killer->HasStateFlag(SLOW)) - if(!Killer->HasStateFlag(HASTE)){Killer->GainIntrinsic(HASTE);return false;} -// riBuff++; -// if(!Killer->HasStateFlag(HICCUPS)) -// if(!Killer->HasStateFlag(INVISIBLE)){Killer->GainIntrinsic(INVISIBLE);return false;} - riBuff++; - if(!Killer->HasStateFlag(SWIMMING)){Killer->GainIntrinsic(SWIMMING);return false;} - riBuff++; - if(!Killer->HasStateFlag(ETHEREAL_MOVING)){Killer->GainIntrinsic(ETHEREAL_MOVING);return false;} - riBuff++; - if(!Killer->HasStateFlag(REGENERATION)){Killer->GainIntrinsic(REGENERATION);return false;} - riBuff++; - if(!Killer->HasStateFlag(LEVITATION)){Killer->GainIntrinsic(LEVITATION);return false;} - riBuff++; - if(!Killer->HasStateFlag(GAS_IMMUNITY)){Killer->GainIntrinsic(GAS_IMMUNITY);return false;} - riBuff++; - if(!Killer->HasStateFlag(TELEPORT_LOCK)){Killer->GainIntrinsic(TELEPORT_LOCK);return false;} - riBuff++; - if(!Killer->HasStateFlag(POLYMORPH_LOCK)){Killer->GainIntrinsic(POLYMORPH_LOCK);return false;} +#define ASRET(e,b) if(AddState(Killer,e,#e,0,NULL,b))return false; +#define ASRETD(e,d,b) if(AddState(Killer,e,#e,d,#d,b))return false; + ASRET(ESP,riBuff); + ASRET(INFRA_VISION,riBuff); +// ASRETD(HASTE,SLOW,riBuff); + ASRET(HASTE,riBuff); + ASRET(SWIMMING,riBuff); + ASRET(ETHEREAL_MOVING,riBuff); + ASRET(REGENERATION,riBuff); + ASRET(LEVITATION,riBuff); + ASRET(GAS_IMMUNITY,riBuff); + ASRET(TELEPORT_LOCK,riBuff); + ASRET(POLYMORPH_LOCK,riBuff); /************************* * DEBUFFs, after player has taken too much it is time to make it stop, but slowly: */ riDebuff=1; - if(!Killer->HasStateFlag(HICCUPS)){ -// Killer->DeActivateTemporaryState(INVISIBLE); - Killer->GainIntrinsic(HICCUPS); - return false; - } - riDebuff++; - - if(!Killer->HasStateFlag(SLOW)){ - Killer->DeActivateTemporaryState(HASTE); - Killer->GainIntrinsic(SLOW); - return false; - } - riDebuff++; - - if(!Killer->HasStateFlag(PARASITE_TAPE_WORM)){Killer->GainIntrinsic(PARASITE_TAPE_WORM);return false;} - riDebuff++; - if(!Killer->HasStateFlag(CONFUSED)){Killer->GainIntrinsic(CONFUSED);return false;} - riDebuff++; - if(!Killer->HasStateFlag(LEPROSY)){Killer->GainIntrinsic(LEPROSY);return false;} -// riDebuff++; -// this is too much as adds worm mobs on the dungeon... -// if(!Killer->HasStateFlag(PARASITE_MIND_WORM)){Killer->GainIntrinsic(PARASITE_MIND_WORM);return false;} - riDebuff++; - if(!Killer->HasStateFlag(POISONED)){Killer->GainIntrinsic(POISONED);return false;} -// riDebuff++; -// if(!Killer->HasStateFlag(PANIC)){ -// Killer->DeActivateTemporaryState(FEARLESS); -// Killer->GainIntrinsic(PANIC); -// return true; -// } + ASRET(HICCUPS,riDebuff); +// ASRETD(SLOW,HASTE,riDebuff); + ASRET(SLOW,riDebuff); + ASRET(PARASITE_TAPE_WORM,riDebuff); + ASRET(CONFUSED,riDebuff); + ASRET(LEPROSY,riDebuff); + ASRET(POISONED,riDebuff); // Revenge, grant it will stop: game::GetCurrentLevel()->Explosion( From a35a43049beccada6d3782917af1565ab2bf6c74 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 23 Apr 2020 23:18:34 -0300 Subject: [PATCH 158/235] fixed a bug when digging and arm goes missing; WIP-cursedDeveloper: fixed a bug when resurrecting in wilderness; --- Main/Source/actions.cpp | 33 ++++++++++++++++++--------------- Main/Source/curseddeveloper.cpp | 22 ++++++++++++++-------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp index 5e968ad1b..235839a22 100644 --- a/Main/Source/actions.cpp +++ b/Main/Source/actions.cpp @@ -471,25 +471,28 @@ void dig::Handle() Terminate(true); } - if(StoppedDigging) + humanoid* h = dynamic_cast(Actor); + if(StoppedDigging && h) { - if(MoveDigger && Actor->GetMainWielded()) - Actor->GetMainWielded()->MoveTo(Actor->GetStack()); + if(MoveDigger && h->GetMainWielded()) + h->GetMainWielded()->MoveTo(h->GetStack()); - item* RightBackup = game::SearchItem(RightBackupID); - - if(RightBackup && RightBackup->Exists() && Actor->IsOver(RightBackup)) - { - RightBackup->RemoveFromSlot(); - Actor->SetRightWielded(RightBackup); + if(h->GetRightArm()){ + item* RightBackup = game::SearchItem(RightBackupID); + if(RightBackup && RightBackup->Exists() && h->IsOver(RightBackup)) + { + RightBackup->RemoveFromSlot(); + h->SetRightWielded(RightBackup); + } } - item* LeftBackup = game::SearchItem(LeftBackupID); - - if(LeftBackup && LeftBackup->Exists() && Actor->IsOver(LeftBackup)) - { - LeftBackup->RemoveFromSlot(); - Actor->SetLeftWielded(LeftBackup); + if(h->GetLeftArm()){ + item* LeftBackup = game::SearchItem(LeftBackupID); + if(LeftBackup && LeftBackup->Exists() && h->IsOver(LeftBackup)) + { + LeftBackup->RemoveFromSlot(); + h->SetLeftWielded(LeftBackup); + } } } diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index a095ec85a..6afd6c652 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -92,11 +92,13 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) P->GetAction()->Terminate(false); //just to avoid messing any action // at death spot - P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(BLOOD, 30 * P->GetAttribute(ENDURANCE))); + if(!game::IsInWilderness()) + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(BLOOD, 30 * P->GetAttribute(ENDURANCE))); if(!bStay && Killer && !game::IsInWilderness()){ - if(!P->GetLSquareUnder()->GetEngraved()) - game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here@"); + if(!game::IsInWilderness()) + if(!P->GetLSquareUnder()->GetEngraved()) + game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here@"); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn if(Killer->GetSquaresUnder()>1){ @@ -118,7 +120,8 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) // at resurrect spot if(iKillerDebuff>0) // if enemy got too powerful, buff the player randomly - P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); + if(!game::IsInWilderness()) + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); ADD_MESSAGE("Your curse forbids you to rest and be remembered..."); @@ -194,12 +197,15 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in ASRET(POISONED,riDebuff); // Revenge, grant it will stop: - game::GetCurrentLevel()->Explosion( - game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); + if(!game::IsInWilderness() && game::GetCurrentLevel()) + game::GetCurrentLevel()->Explosion( + game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); - Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); - Killer->GetLSquareUnder()->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, 100)); + if(!game::IsInWilderness()){ + Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); + Killer->GetLSquareUnder()->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, 100)); + } rbRev=true; From 3de8c61f6a4bca4c92460d308b807866e95deed4 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 23 Apr 2020 23:45:47 -0300 Subject: [PATCH 159/235] added option to allow fight (not move) while overloaded. --- Main/Include/iconf.h | 2 ++ Main/Source/human.cpp | 12 +++++++----- Main/Source/iconf.cpp | 5 +++++ Main/Source/nonhuman.cpp | 24 ++++++++++++++---------- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index f2ef7d9e3..0f87c0a4d 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -43,6 +43,7 @@ class ivanconfig static int GetAltSilhouettePreventColorGlitch(){return AltSilhouettePreventColorGlitch.Value;} static int GetShowMap(){return ShowMap.Value;} static truth IsShowMapAtDetectMaterial() { return ShowMapAtDetectMaterial.Value; } + static truth IsOverloadedFight() { return OverloadedFight.Value; } static truth IsTransparentMapLM() { return TransparentMapLM.Value; } static truth IsWaitNeutralsMoveAway() { return WaitNeutralsMoveAway.Value; } static truth IsEnhancedLights() { return EnhancedLights.Value; } @@ -205,6 +206,7 @@ class ivanconfig static cycleoption AltSilhouettePreventColorGlitch; static cycleoption ShowMap; static truthoption ShowMapAtDetectMaterial; + static truthoption OverloadedFight; static truthoption TransparentMapLM; static truthoption WaitNeutralsMoveAway; static truthoption AllWeightIsRelevant; diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index b60a5d6f6..2970882e8 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -463,12 +463,14 @@ truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) return false; - if(GetBurdenState() == OVER_LOADED) - { - if(IsPlayer()) - ADD_MESSAGE("You cannot fight while carrying so much."); + if(!ivanconfig::IsOverloadedFight()){ + if(GetBurdenState() == OVER_LOADED) + { + if(IsPlayer()) + ADD_MESSAGE("You cannot fight while carrying so much."); - return false; + return false; + } } int c, AttackStyles; diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index d4af591cc..c29944f4b 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -144,6 +144,10 @@ truthoption ivanconfig::ShowMapAtDetectMaterial("ShowMapAtDetectMaterial", "Show map while detecting material", "", false); +truthoption ivanconfig::OverloadedFight( "OverloadedFight", + "Allow fighting while overloaded", + "Moving is, of course, still denied.", + false); truthoption ivanconfig::AutoPickupThrownItems("AutoPickupThrownItems", "Auto pick up thrown weapons", "Automatically annotate any thrown weapon and pick it up without loosing a turn when you step on its square.", @@ -1125,6 +1129,7 @@ void ivanconfig::Initialize() configsystem::AddOption(fsCategory,&AutoPickupThrownItems); configsystem::AddOption(fsCategory,&AutoPickUpMatching); configsystem::AddOption(fsCategory,&DropBeforeOffering); + configsystem::AddOption(fsCategory,&OverloadedFight); fsCategory="Game Window"; configsystem::AddOption(fsCategory,&Contrast); diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp index 135246336..594948fa8 100644 --- a/Main/Source/nonhuman.cpp +++ b/Main/Source/nonhuman.cpp @@ -313,12 +313,14 @@ truth nonhumanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) return false; - if(GetBurdenState() == OVER_LOADED) - { - if(IsPlayer()) - ADD_MESSAGE("You cannot fight while carrying so much."); + if(!ivanconfig::IsOverloadedFight()){ + if(GetBurdenState() == OVER_LOADED) + { + if(IsPlayer()) + ADD_MESSAGE("You cannot fight while carrying so much."); - return false; + return false; + } } /* Behold this Terrible Father of Gum Solutions! */ @@ -1313,12 +1315,14 @@ truth twoheadedmoose::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) return false; - if(GetBurdenState() == OVER_LOADED) - { - if(IsPlayer()) - ADD_MESSAGE("You cannot fight while carrying so much."); + if(!ivanconfig::IsOverloadedFight()){ + if(GetBurdenState() == OVER_LOADED) + { + if(IsPlayer()) + ADD_MESSAGE("You cannot fight while carrying so much."); - return false; + return false; + } } Hostility(Enemy); From 70da2819bb9cce4f3e3cbbb823ec239b7a32eb8f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 24 Apr 2020 00:03:32 -0300 Subject: [PATCH 160/235] WIP-cursedDeveloper: improving; --- Main/Source/curseddeveloper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 6afd6c652..c972f9141 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -191,9 +191,9 @@ bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in ASRET(HICCUPS,riDebuff); // ASRETD(SLOW,HASTE,riDebuff); ASRET(SLOW,riDebuff); - ASRET(PARASITE_TAPE_WORM,riDebuff); +// ASRET(PARASITE_TAPE_WORM,riDebuff); ASRET(CONFUSED,riDebuff); - ASRET(LEPROSY,riDebuff); +// ASRET(LEPROSY,riDebuff); ASRET(POISONED,riDebuff); // Revenge, grant it will stop: From 0f1832282867fe751003add19ca1038909ade596 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 24 Apr 2020 01:02:31 -0300 Subject: [PATCH 161/235] WIP-cursedDeveloper: improving; --- Main/Source/curseddeveloper.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index c972f9141..d3d127cfb 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -95,10 +95,9 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) if(!game::IsInWilderness()) P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(BLOOD, 30 * P->GetAttribute(ENDURANCE))); - if(!bStay && Killer && !game::IsInWilderness()){ - if(!game::IsInWilderness()) - if(!P->GetLSquareUnder()->GetEngraved()) - game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here@"); + if(!bStay && Killer && !game::IsInWilderness() && iKillerDebuff==0){ + if(!P->GetLSquareUnder()->GetEngraved()) + game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here@"); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn if(Killer->GetSquaresUnder()>1){ From bd072c4e5eb6dfdb7b8256f9b4e5a795bdef7cf1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 24 Apr 2020 01:22:01 -0300 Subject: [PATCH 162/235] devcons: fixed wizard commands help and granted (denied) access; --- Main/Source/devcons.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp index 0ce13f34d..7c7ba3088 100644 --- a/Main/Source/devcons.cpp +++ b/Main/Source/devcons.cpp @@ -58,6 +58,8 @@ truth IsValidChar(character* C){ return true; } void TeleToChar(festring fsFilter){ + if(!game::WizardModeIsReallyActive())return; + characteridmap map = game::GetCharacterIDMapCopy(); for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ character* C = itr->second; @@ -71,6 +73,8 @@ void TeleToChar(festring fsFilter){ } } void TeleToMe(festring fsFilter){ + if(!game::WizardModeIsReallyActive())return; + characteridmap map = game::GetCharacterIDMapCopy(); for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ character* C = itr->second; @@ -84,6 +88,8 @@ void TeleToMe(festring fsFilter){ } } void FillWithWalls(festring fsFilter){ + if(!game::WizardModeIsReallyActive())return; + int iAround=0; if(!fsFilter.IsEmpty()) iAround=atoi(fsFilter.CStr()); @@ -110,6 +116,8 @@ void FillWithWalls(festring fsFilter){ DEVCMDMSG1P("new walls: %d",iCount); } void ListChars(festring fsFilter){ + if(!game::WizardModeIsReallyActive())return; + ulong idFilter=0; if(!fsFilter.IsEmpty()) idFilter=atoi(fsFilter.CStr()); @@ -154,6 +162,8 @@ void ListChars(festring fsFilter){ DEVCMDMSG1P("total:%d",vCharLastSearch.size()); } void DelChars(festring fsParams){ + if(!game::WizardModeIsReallyActive())return; + ulong count=0; if(!fsParams.IsEmpty()) count=atoi(fsParams.CStr()); @@ -211,6 +221,8 @@ character* GetOwnerChar(item* it,festring& rfsType = fsDummy){ return NULL; } void DelItems(festring fsParams){ + if(!game::WizardModeIsReallyActive())return; + ulong count=0; if(!fsParams.IsEmpty()) count=atoi(fsParams.CStr()); @@ -238,6 +250,8 @@ void DelItems(festring fsParams){ DEVCMDMSG2P("total=%d, remaining=%d",iRm,vItemLastSearch.size()); } void ListItems(festring fsParams){ + if(!game::WizardModeIsReallyActive())return; + ulong idCharFilter=0; ulong idItemFilter=0; festring fsFilter; @@ -389,6 +403,8 @@ const int iVarTot=10; float afVars[iVarTot]; void devcons::SetVar(festring fsParams) { + if(!game::WizardModeIsReallyActive())return; + if(!fsParams.IsEmpty()){ std::string part; std::stringstream iss(fsParams.CStr()); @@ -454,7 +470,7 @@ void devcons::OpenCommandsConsole() for(;;){ static festring fsFullCmd; festring fsQ; - if(game::WizardModeIsActive()) + if(game::WizardModeIsReallyActive()) fsQ="Developer(WIZ) "; fsQ<<"Console Command (try 'help' or '?'):"; //TODO key up/down commands history and save/load to a txt file @@ -507,14 +523,12 @@ void devcons::Help(festring fsFilter) { festring fsWM; for(int j=0;j Date: Fri, 24 Apr 2020 01:49:13 -0300 Subject: [PATCH 163/235] if sumo wrestler house is crowded messing the game, there is a new console command to fix it: FixSumoWrestlerHouse. It will randomly teleport all nearby banana growers. --- Main/Source/human.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 2970882e8..2d5444790 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -14,6 +14,7 @@ #include "dbgmsgproj.h" #include "whandler.h" +#include "devcons.h" cint humanoid::DrawOrder[] = { TORSO_INDEX, GROIN_INDEX, RIGHT_LEG_INDEX, LEFT_LEG_INDEX, RIGHT_ARM_INDEX, LEFT_ARM_INDEX, HEAD_INDEX }; @@ -5221,8 +5222,41 @@ truth humanoid::SpecialBiteEffect(character* Victim, v2 HitPos, int BodyPartInde return false; } +void FixSumoWrestlerHouse(festring fsCmdParams) +{ + sumowrestler* SM = NULL; + characteridmap map = game::GetCharacterIDMapCopy(); + for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){ + character* C = itr->second; + if(dynamic_cast(C)){ + SM=(sumowrestler*)C; + break; + } + } + + if(SM){ + for(int d = 0; d < SM->GetNeighbourSquares(); ++d) + { + lsquare* Square = SM->GetNeighbourLSquare(d); + + if(Square){ + character* C2 = Square->GetCharacter(); + if(C2 && dynamic_cast(C2)){ + C2->TeleportRandomly(true); + } + } + } + } +} + void sumowrestler::GetAICommand() { + static bool bInitDummy = [](){ + devcons::AddDevCmd("FixSumoWrestlerHouse",FixSumoWrestlerHouse, + "BugFix sumo wrestler house in case banana growers over crowd it."); + return true;}(); + + EditNP(-25); SeekLeader(GetLeader()); From 0e08393f86e863aa2575e34765eeff540eb4cf5d Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 24 Apr 2020 22:25:58 -0300 Subject: [PATCH 164/235] new message about material info read from manual --- Main/Source/cmdcraft.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index cfbcbf478..3bfe4274d 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2005,6 +2005,8 @@ void addMaterialInfo(character* C,item* it){ if(dynamic_cast(v[i])){ it->SetTag('m'); it->SetLabel(it->GetLabel()+"s"+matM->GetStrengthValue()+"f"+matM->GetFlexibility()); + ADD_MESSAGE("You consult %s about %s. It has a strength of %d and a flexibility of %i.", + v[i]->GetName(DEFINITE).CStr(),matM->GetNameStem().CStr(),matM->GetStrengthValue(),matM->GetFlexibility()); break; } } From cfaf6b4ac990d9587bca3093c8d510e524d57dfc Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 00:23:21 -0300 Subject: [PATCH 165/235] new option: allow contrast to make readable too dark text or items or materials; WIP-ShowEquippedOnList: on craft ingredients list, not working yet...; --- Main/Include/iconf.h | 3 +++ Main/Include/ivandef.h | 1 + Main/Source/cmdcraft.cpp | 27 ++++++++++++++++++++++----- Main/Source/game.cpp | 6 ++++++ Main/Source/human.cpp | 5 ++++- Main/Source/iconf.cpp | 25 +++++++++++++++++++++++++ Main/Source/item.cpp | 6 ++++++ Main/Source/miscitem.cpp | 2 +- 8 files changed, 68 insertions(+), 7 deletions(-) diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index 0f87c0a4d..eec667444 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -26,6 +26,7 @@ class ivanconfig static cfestring& GetAutoPickUpMatching() { return AutoPickUpMatching.Value; } static truth IsAllWeightIsRelevant() { return AllWeightIsRelevant.Value; } static truth IsDropBeforeOffering() { return DropBeforeOffering.Value; } + static truth IsAllowContrastBackground() { return AllowContrastBackground.Value; }; static long GetAutoSaveInterval() { return AutoSaveInterval.Value; } static long GetContrast() { return Contrast.Value; } static long GetHitIndicator() { return HitIndicator.Value; } @@ -79,6 +80,7 @@ class ivanconfig static long GetVolume() { return Volume.Value; } static long GetSfxVolume() { return SfxVolume.Value; } static long GetMIDIOutputDevice() { return MIDIOutputDevice.Value; } + static col16 CheckChangeColor(col16 col); #ifndef __DJGPP__ static int GetGraphicsScale() { return GraphicsScale.Value; } @@ -211,6 +213,7 @@ class ivanconfig static truthoption WaitNeutralsMoveAway; static truthoption AllWeightIsRelevant; static truthoption DropBeforeOffering; + static truthoption AllowContrastBackground; static truthoption ShowVolume; static truthoption EnhancedLights; diff --git a/Main/Include/ivandef.h b/Main/Include/ivandef.h index c62780d9f..c88153334 100644 --- a/Main/Include/ivandef.h +++ b/Main/Include/ivandef.h @@ -1176,6 +1176,7 @@ cv2 TILE_V2(TILE_SIZE, TILE_SIZE); #define SQUARE_INDEX_MASK 0xFFFF #define ALLOW_ANIMATE 0x10000 #define ALLOW_ALPHA 0x20000 +#define ALLOW_CONTRAST 0x40000 #define TALENTS 5 diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 3bfe4274d..5959c1673 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -651,6 +651,7 @@ struct ci{ //create item info/helper/data/config/param float fUsablePercVol=1.0; bool bMustBeTailorable=false; bool bMixRemainingLump=true; + bool bAddEquippedItemsToChoiceList=false; }; struct recipe{ festring action; @@ -1074,7 +1075,7 @@ struct recipe{ if(reqVol==0) ABORT("ingredient required 0 volume?"); - const itemvector vi = vitInv(rpd); + const itemvector vi = vitInv(rpd,true,CI.bAddEquippedItemsToChoiceList); prepareFilter(rpd,vi,reqVol,CI); int iWeakest=-1; @@ -1219,13 +1220,27 @@ struct recipe{ CIok); } - static itemvector vitInv(recipedata& rpd){ + static itemvector vitInv(recipedata& rpd,bool bAllowWielded=true,bool bAllowAllEquipped=false){ itemvector vi; //prefer already equipped - if(rpd.rc.H()->GetLeftWielded ())vi.push_back(rpd.rc.H()->GetLeftWielded ()); - if(rpd.rc.H()->GetRightWielded())vi.push_back(rpd.rc.H()->GetRightWielded()); - + if(bAllowWielded){ //TODO not showing on list... + if(rpd.rc.H()->GetRightWielded())vi.push_back(rpd.rc.H()->GetRightWielded()); + if(rpd.rc.H()->GetLeftWielded ())vi.push_back(rpd.rc.H()->GetLeftWielded ()); + } + + if(bAllowAllEquipped){ //TODO not showing on list... + for(int c = 0; c < rpd.rc.H()->GetEquipments(); ++c){ + if( + c!=RIGHT_WIELDED_INDEX && + c!=LEFT_WIELDED_INDEX && + rpd.rc.H()->GetEquipment(c) + ){ + vi.push_back(rpd.rc.H()->GetEquipment(c)); + } + } + } + rpd.rc.H()->GetStack()->FillItemVector(vi); //TODO once, the last item from here had an invalid pointer, HOW? return vi; @@ -2021,6 +2036,7 @@ struct srpInspect : public recipe{ //TODO this is instantaneous, should take tim virtual bool work(recipedata& rpd){ ci CI; + CI.bAddEquippedItemsToChoiceList=true; if(!choseOneIngredient(rpd,&CI)){ rpd.SetAlreadyExplained(); return false; @@ -2059,6 +2075,7 @@ struct srpResistanceVS : public recipe{ //TODO this is instantaneous, should tak virtual bool work(recipedata& rpd){ ci CI; + CI.bAddEquippedItemsToChoiceList=true; CI.iMinMainMaterStr=1; if(!choseOneIngredient(rpd,&CI)){ rpd.SetAlreadyExplained(); diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 1bea74847..18a46c641 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -3129,6 +3129,8 @@ bitmap* PrepareItemsUnder(bool bUseDB, stack* su, int iMax, v2 v2PosIni, int iDi blitdata B = DEFAULT_BLITDATA; B.CustomData = ALLOW_ANIMATE; + if(ivanconfig::IsAllowContrastBackground()) + B.CustomData |= ALLOW_CONTRAST; B.Stretch = 1; //ignored? anyway this will work only from/to 16x16... B.Border = { TILE_SIZE, TILE_SIZE }; B.Luminance = ivanconfig::GetContrastLuminance(); @@ -6132,8 +6134,12 @@ void game::ItemEntryDrawer(bitmap* Bitmap, v2 Pos, uint I) if(ItemVector[c]->AllowAlphaEverywhere()) B.CustomData |= ALLOW_ALPHA; + if(ivanconfig::IsAllowContrastBackground()) + B.CustomData |= ALLOW_CONTRAST; + ItemVector[c]->Draw(B); B.CustomData &= ~ALLOW_ALPHA; + B.CustomData &= ~ALLOW_CONTRAST; } if(ItemVector.size() > 3) diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 2d5444790..0498259f3 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -2138,9 +2138,12 @@ void humanoid::DrawSilhouette(truth AnimationDraw) const if(Equipment->AllowAlphaEverywhere()) B1.CustomData |= ALLOW_ALPHA; - + + if(ivanconfig::IsAllowContrastBackground()) + B1.CustomData |= ALLOW_CONTRAST; Equipment->Draw(B1); B1.CustomData &= ~ALLOW_ALPHA; + B1.CustomData &= ~ALLOW_CONTRAST; // if(eqMHover==Equipment){ // v2 v2M = globalwindowhandler::GetMouseLocation(); diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index c29944f4b..634d01cea 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -172,6 +172,10 @@ truthoption ivanconfig::DropBeforeOffering("DropBeforeOffering", "Drop the item on altar in case it is not accepted", "Automatically drop offered items on an altar to prevent them from cluttering your inventory should the god not accept your gift. Beware it may be owned floor!", false); +truthoption ivanconfig::AllowContrastBackground("AllowContrastBackground", + "Better contrast background to dark items and materials", + "", + false); truthoption ivanconfig::ShowVolume( "ShowVolume", "Show item volume in cm3", "", @@ -880,6 +884,26 @@ void ivanconfig::SelectedBkgColorChanger(stringoption* O, cfestring& What) } } +col16 ivanconfig::CheckChangeColor(col16 col) +{ + if(IsAllowContrastBackground()){ + static col16 colRef = DARK_GRAY; + static col16 iR = GetRed16(colRef); + static col16 iG = GetGreen16(colRef); + static col16 iB = GetBlue16(colRef); + if( + GetRed16(col) GetColor()); + if(col!=GetMainMaterial()->GetColor()) + BlitData.Bitmap->Fill(BlitData.Dest,TILE_V2,col); + } + if(BlitData.CustomData & ALLOW_ALPHA) P->AlphaLuminanceBlit(BlitData); else diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index 31f878075..55d8b139d 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -3776,7 +3776,7 @@ void materialmanual::FinishReading(character* Reader) Entry << Material[c]->GetFlexibility(); Entry.Resize(70); Entry << Material[c]->GetDensity(); - List.AddEntry(Entry, Material[c]->GetColor()); + List.AddEntry(Entry, ivanconfig::CheckChangeColor(Material[c]->GetColor())); } List.Draw(); From 1046fac34c3cd3cbd091dfc10293768e42ad7a7d Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 00:27:55 -0300 Subject: [PATCH 166/235] fix allow contrast to dark text, items or materials; --- Main/Source/iconf.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 634d01cea..9be7db7af 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -896,8 +896,7 @@ col16 ivanconfig::CheckChangeColor(col16 col) GetGreen16(col) Date: Sat, 25 Apr 2020 00:34:51 -0300 Subject: [PATCH 167/235] another fix to allow contrast to dark text, items or materials; --- Main/Source/item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp index 3f19a74b4..9163c99c6 100644 --- a/Main/Source/item.cpp +++ b/Main/Source/item.cpp @@ -1513,7 +1513,7 @@ void item::Draw(blitdata& BlitData) const } cbitmap* P = bmp; - if(BlitData.CustomData & ALLOW_CONTRAST){ + if(GetMainMaterial() && (BlitData.CustomData & ALLOW_CONTRAST)){ col16 col = ivanconfig::CheckChangeColor(GetMainMaterial()->GetColor()); if(col!=GetMainMaterial()->GetColor()) BlitData.Bitmap->Fill(BlitData.Dest,TILE_V2,col); From 9edd2d6314f3c68ced8234a2ee5bb15f784f8cb6 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 01:31:39 -0300 Subject: [PATCH 168/235] Will fall down if overloaded and try to kick (if OverloadedFight is enabled only). --- Main/Include/char.h | 1 + Main/Source/char.cpp | 17 +++++++++++++++++ Main/Source/human.cpp | 14 +++++++------- Main/Source/nonhuman.cpp | 2 ++ 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Main/Include/char.h b/Main/Include/char.h index 2ea26bbda..f81268db4 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -1195,6 +1195,7 @@ class character : public entity, public id void SetNewVomitMaterial(int What) { MyVomitMaterial = What; } festring GetHitPointDescription() const; truth WillGetTurnSoon() const; + truth OverloadedKickFailCheck(); protected: static truth DamageTypeDestroysBodyPart(int); virtual void LoadSquaresUnder(); diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index f2ae47937..16c2750bf 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -12372,3 +12372,20 @@ truth character::WillGetTurnSoon() const { return GetAP() >= 900; } + +truth character::OverloadedKickFailCheck() +{ + if(ivanconfig::IsOverloadedFight() && GetBurdenState() == OVER_LOADED){ + if(IsPlayer()) + ADD_MESSAGE("You try to kick, lose balance and fall down."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s tries to kick, loses balance and falls down.", CHAR_NAME(DEFINITE)); + + ReceiveDamage(0, 1 + (RAND() & 1), PHYSICAL_DAMAGE, ALL); + CheckDeath(CONST_S("was overloaded, tried to kick and fell down"), 0); + + EditAP(-500); + return true; + } + return false; +} diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 0498259f3..6021460c8 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -464,14 +464,11 @@ truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) return false; - if(!ivanconfig::IsOverloadedFight()){ - if(GetBurdenState() == OVER_LOADED) - { - if(IsPlayer()) - ADD_MESSAGE("You cannot fight while carrying so much."); + if(!ivanconfig::IsOverloadedFight() && GetBurdenState() == OVER_LOADED){ + if(IsPlayer()) + ADD_MESSAGE("You cannot fight while carrying so much."); - return false; - } + return false; } int c, AttackStyles; @@ -550,6 +547,9 @@ truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) case USE_LEGS: if(HasTwoUsableLegs()) { + if(OverloadedKickFailCheck()) + return false; + msgsystem::EnterBigMessageMode(); Hostility(Enemy); Kick(GetNearLSquare(HitPos), Direction, Flags & SADIST_HIT); diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp index 594948fa8..a67dea335 100644 --- a/Main/Source/nonhuman.cpp +++ b/Main/Source/nonhuman.cpp @@ -359,6 +359,8 @@ truth nonhumanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) msgsystem::LeaveBigMessageMode(); return true; case USE_LEGS: + if(OverloadedKickFailCheck()) + return false; msgsystem::EnterBigMessageMode(); Hostility(Enemy); Kick(GetNearLSquare(HitPos), Direction, Flags & SADIST_HIT); From b5da5b0a6ba06b3f17390ca8d47cf3a7093939c3 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 01:59:50 -0300 Subject: [PATCH 169/235] WIP-OverloadedFight: AI to auto use head instead of letting use legs if overloaded; --- Main/Source/char.cpp | 4 ++-- Main/Source/human.cpp | 5 +++++ Main/Source/nonhuman.cpp | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 16c2750bf..312efbb96 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -12381,10 +12381,10 @@ truth character::OverloadedKickFailCheck() else if(CanBeSeenByPlayer()) ADD_MESSAGE("%s tries to kick, loses balance and falls down.", CHAR_NAME(DEFINITE)); - ReceiveDamage(0, 1 + (RAND() & 1), PHYSICAL_DAMAGE, ALL); + ReceiveDamage(0, 1 + (RAND() & 1), PHYSICAL_DAMAGE, ALL); //based on banana peel slip CheckDeath(CONST_S("was overloaded, tried to kick and fell down"), 0); - EditAP(-500); + EditAP(-100000 / APBonus(GetAttribute(AGILITY))); //based on kick command return true; } return false; diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 6021460c8..404f88bfd 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -495,6 +495,11 @@ truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) if(Chosen == USE_LEGS && HasTwoUsableLegs()) Chosen = USE_HEAD; } + + if(ivanconfig::IsOverloadedFight() && GetBurdenState() == OVER_LOADED && GetHead()){ + if(Chosen == USE_LEGS) + Chosen = USE_HEAD; + } switch(Chosen) { diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp index a67dea335..c108964f6 100644 --- a/Main/Source/nonhuman.cpp +++ b/Main/Source/nonhuman.cpp @@ -330,9 +330,11 @@ truth nonhumanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) if(AttackStyle & USE_LEGS) { room* Room = GetNearLSquare(HitPos)->GetRoom(); - if(Room && !Room->AllowKick(this, GetNearLSquare(HitPos))) AttackStyle &= ~USE_LEGS; + + if(ivanconfig::IsOverloadedFight() && GetBurdenState() == OVER_LOADED && RAND()%3!=0) + AttackStyle &= ~USE_LEGS; } int c, AttackStyles; From eb2af2dcd43d2fd96cdcedb02348a260a5e37817 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 02:18:00 -0300 Subject: [PATCH 170/235] WIP-OverloadedFight: fix to not let player -ActionPoints mess the gameplay; --- Main/Source/char.cpp | 4 +++- Main/Source/human.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 312efbb96..18ea3a066 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -12384,7 +12384,9 @@ truth character::OverloadedKickFailCheck() ReceiveDamage(0, 1 + (RAND() & 1), PHYSICAL_DAMAGE, ALL); //based on banana peel slip CheckDeath(CONST_S("was overloaded, tried to kick and fell down"), 0); - EditAP(-100000 / APBonus(GetAttribute(AGILITY))); //based on kick command + if(!IsPlayer()) // if player, the -AP would accumulate while the game turn would NOT increase messing the gameplay + EditAP(-100000 / APBonus(GetAttribute(AGILITY))); //based on kick command anyway + return true; } return false; diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 404f88bfd..63a3f9097 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -498,7 +498,7 @@ truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) if(ivanconfig::IsOverloadedFight() && GetBurdenState() == OVER_LOADED && GetHead()){ if(Chosen == USE_LEGS) - Chosen = USE_HEAD; + Chosen = USE_HEAD; //TODO why this is not working for player!?!?!?!? } switch(Chosen) From c85ff4a6b04b3b7f8c520e0db972f6227534024c Mon Sep 17 00:00:00 2001 From: gussak thor Date: Sat, 25 Apr 2020 18:28:04 -0300 Subject: [PATCH 171/235] WIP-curseddev: improving; --- .../nbproject/configurations.xml | 34 +++-- Main/Include/curseddeveloper.h | 4 + Main/Source/curseddeveloper.cpp | 144 +++++++++++++----- 3 files changed, 130 insertions(+), 52 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 98008b016..08b244db9 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -34,9 +34,9 @@ - + - + false + true @@ -167,6 +168,15 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean + + + /usr/include + + + CURSEDDEVELOPER + WIZARD + + . @@ -194,7 +204,15 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean + + + CMakeFiles/CMakeTmp + + + + CMakeFiles/CMakeTmp + DBGMSG FELIST_WAITKEYUP @@ -795,18 +813,6 @@ - - - - CMakeFiles/CMakeTmp - - - - - CMakeFiles/CMakeTmp - - - diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h index e3855e16c..5cd272ec4 100644 --- a/Main/Include/curseddeveloper.h +++ b/Main/Include/curseddeveloper.h @@ -20,6 +20,10 @@ class cursedDeveloper { static bool IsCursedDeveloperTeleport(){return bCursedDeveloperTeleport;} static bool LifeSaveJustABit(character* Killer); static bool BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev); + static void RestoreLimbs(festring fsCmdParams); + static bool HealTorso(bodypart* bp); + static bool HealBP(int iIndex,int iMode,int iMinHpOK=0); + static bool CreateBP(int iIndex); private: static bool bCursedDeveloper; static bool bCursedDeveloperTeleport; diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index d3d127cfb..39d6ec30f 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -11,6 +11,7 @@ */ #include "dbgmsgproj.h" +#include "devcons.h" #include #include @@ -23,11 +24,110 @@ bool cursedDeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); bool cursedDeveloper::bCursedDeveloperTeleport = false; +#define HEAL_1 1 +#define HEAL_MINOK 2 +#define HEAL_MAX 3 + +void cursedDeveloper::RestoreLimbs(festring fsCmdParams) +{ + int iMode = fsCmdParams.IsEmpty() ? 0 : atoi(fsCmdParams.CStr()); + + character* P = game::GetPlayer(); + + // save life but just a little bit + for(int c = 0; c < P->BodyParts; ++c){ //only enough to continue testing normal gameplay + if(!CreateBP(c))continue; + + HealBP(c,iMode); + } + P->CalculateBodyPartMaxHPs(0); //this also calculates the overall current HP + DBG2(P->HP,P->MaxHP); + if(P->HP>P->MaxHP) // it MUST be ok here!!! + ABORT("HP>MaxHP %d>%d",P->HP,P->MaxHP); +} + +bool cursedDeveloper::CreateBP(int iIndex) +{ + character* P = game::GetPlayer(); + + bodypart* bp = P->GetBodyPart(iIndex); + if(!bp && P->CanCreateBodyPart(iIndex)) + bp=P->CreateBodyPart(iIndex); + + if(bp && bp->GetHP() > bp->GetMaxHP()){ //TODO how does this happens??? + DBG4(iIndex,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + bp->SetHP(-1); //to allow properly fixing + } + + return bp!=NULL; +} + +bool cursedDeveloper::HealBP(int iIndex,int iMode,int iResHPoverride) +{ + if(!CreateBP(iIndex))return false; + + character* P = game::GetPlayer(); + + /** + * How to prevent endless die loop? + * Clear the bad effects? better not, let them continue working. + * A bit more of HP to the core body parts may suffice (funny head is not one lol). + */ + bodypart* bp = P->GetBodyPart(iIndex); + if(bp && bp->GetHP() < bp->GetMaxHP()){ + int iHpMinUsable = bp->GetMaxHP()/3 + (bp->GetMaxHP()%3>0 ? 1 : 0); //ceil + + int iHpRestore = 0; + switch(iMode){ + case HEAL_1: iHpRestore = 1; break; + case HEAL_MINOK: iHpRestore = iHpMinUsable; break; + case HEAL_MAX: iHpRestore = bp->GetMaxHP(); break; + } + + if(iResHPoverride>0)iHpRestore=iResHPoverride; + + if(bp->GetHP() < iHpRestore){ + DBG4(iIndex,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + bp->SetHP(iHpRestore); + bp->SignalPossibleUsabilityChange(); + } + + DBG5(iIndex,iHpMinUsable,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + +// bp->SetHP(P->GetMaxHP()>iMinHpOK ? iMinHpOK : P->GetMaxHP()); +// DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); + return true; + } + return false; +} +//bool cursedDeveloper::HealTorso(bodypart* bp) +//{ +// character* P = game::GetPlayer(); +// +// /** +// * How to prevent endless die loop? +// * Clear the bad effects? better not, let them continue working. +// * A bit more of HP to the core body parts may suffice (funny head is not one lol). +// */ +// static int iTorsoHpMinOk=10; //this is to fight mustard gas +// if(P->GetBodyPart(TORSO_INDEX)==bp && bp->GetHP() < iTorsoHpMinOk){ +// bp->SetHP(P->GetMaxHP()>iTorsoHpMinOk ? iTorsoHpMinOk : P->GetMaxHP()); +// DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); +// return true; +// } +// return false; +//} + bool cursedDeveloper::LifeSaveJustABit(character* Killer) { if(!bCursedDeveloper) return false; + static bool bInitDevCmdDummy = [](){ + devcons::AddDevCmd("RestoreLimbs",cursedDeveloper::RestoreLimbs, + "[1|2|3] 1=1HP 2=minUsableHP 3=maxHP. Restore missing limbs to the cursed developer, better use only if the game becomes unplayable."); + return true;}(); + character* P = game::GetPlayer(); game::DrawEverything(); @@ -44,44 +144,12 @@ bool cursedDeveloper::LifeSaveJustABit(character* Killer) Killer->SetAssignedName(fsAN); } - // save life but just a little bit - for(int c = 0; c < P->BodyParts; ++c){ //only enough to continue testing normal gameplay - bodypart* bp = P->GetBodyPart(c); - if(!bp && P->CanCreateBodyPart(c)) - bp=P->CreateBodyPart(c); - if(!bp)continue; - - if(bp->GetHP() > bp->GetMaxHP()){ //TODO how does this happens??? - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - bp->SetHP(-1); - } - - /** - * How to prevent endless die loop? - * Clear the bad effects? better not, let them continue working. - * A bit more of HP to the core body parts may suffice (funny head is not one lol). - */ - static int iTorsoHpMinOk=10; //this is to fight mustard gas - if(P->GetBodyPart(TORSO_INDEX)==bp && bp->GetHP() < iTorsoHpMinOk){ - bp->SetHP(P->GetMaxHP()>iTorsoHpMinOk ? iTorsoHpMinOk : P->GetMaxHP()); - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - continue; - } - - int iHpMinUsable = bp->GetMaxHP()/3 + (bp->GetMaxHP()%3>0 ? 1 : 0); //ceil - if(bp->GetHP() < iHpMinUsable){ - DBG4(c,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - bp->SetHP(iHpMinUsable); - bp->SignalPossibleUsabilityChange(); - } - - DBG5(c,iHpMinUsable,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); - } - P->CalculateBodyPartMaxHPs(0); //this also calculates the overall current HP - DBG2(P->HP,P->MaxHP); - if(P->HP>P->MaxHP) // it MUST be ok here!!! - ABORT("HP>MaxHP %d>%d",P->HP,P->MaxHP); - + // automatic minimal to save life +// HealTorso(P->GetTorso()); + HealBP(HEAD_INDEX,HEAL_1); + HealBP(TORSO_INDEX,0,10);//10hp is min to fight mustard gas + HealBP(GROIN_INDEX,HEAL_1); + if(P->GetNP() < HUNGER_LEVEL) P->SetNP(HUNGER_LEVEL); //to avoid endless sleeping From dd1930b1c18346d0f5d3809852857174d5c4a2d0 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 19:04:22 -0300 Subject: [PATCH 172/235] cursedDev: fix classnaming; --- Main/Include/char.h | 2 +- Main/Include/curseddeveloper.h | 2 +- Main/Source/char.cpp | 14 +++++++------- Main/Source/curseddeveloper.cpp | 16 ++++++++-------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Main/Include/char.h b/Main/Include/char.h index f81268db4..4fed4a218 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -287,7 +287,7 @@ class character : public entity, public id public: friend class databasecreator; friend class corpse; - friend class cursedDeveloper; + friend class curseddeveloper; friend class wizautoplay; typedef characterprototype prototype; typedef characterdatabase database; diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h index 5cd272ec4..626560199 100644 --- a/Main/Include/curseddeveloper.h +++ b/Main/Include/curseddeveloper.h @@ -13,7 +13,7 @@ #ifndef __CURSEDDEVELOPER_H__ #define __CURSEDDEVELOPER_H__ -class cursedDeveloper { +class curseddeveloper { #ifdef CURSEDDEVELOPER public: static bool IsCursedDeveloper(){return bCursedDeveloper;}; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 18ea3a066..28a0288eb 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1694,7 +1694,7 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) ADD_MESSAGE("You die."); #ifdef CURSEDDEVELOPER - if(cursedDeveloper::LifeSaveJustABit((character*)Killer)) + if(curseddeveloper::LifeSaveJustABit((character*)Killer)) return; #endif @@ -2514,7 +2514,7 @@ void character::AddScoreEntry(cfestring& Description, double Multiplier, truth A { if(game::WizardModeIsReallyActive()) return; - if(cursedDeveloper::IsCursedDeveloper()) + if(curseddeveloper::IsCursedDeveloper()) return; highscore HScore(GetUserDataDir() + HIGH_SCORE_FILENAME); @@ -2705,7 +2705,7 @@ void character::GetPlayerCommand() BeginTemporaryState(PANIC, 500 + RAND_N(500)); } - if(!cursedDeveloper::IsCursedDeveloper()) + if(!curseddeveloper::IsCursedDeveloper()) game::AskForKeyPress(CONST_S("You are horrified by your situation! [press any key to continue]")); } else if(ivanconfig::GetWarnAboutDanger()) @@ -3994,11 +3994,11 @@ void character::TeleportRandomly(truth Intentional) else if(IsPlayer()) { // This is to prevent uncontrolled teleportation from going unnoticed by players. - if(!cursedDeveloper::IsCursedDeveloperTeleport()) + if(!curseddeveloper::IsCursedDeveloperTeleport()) game::AskForKeyPress(CONST_S("You teleport! [press any key to continue]")); } - if(IsPlayer() && !cursedDeveloper::IsCursedDeveloperTeleport()) + if(IsPlayer() && !curseddeveloper::IsCursedDeveloperTeleport()) ADD_MESSAGE("A rainbow-colored whirlpool twists the existence around you. " "You are sucked through a tunnel piercing a myriad of surreal " "universes. Luckily you return to this dimension in one piece."); @@ -4297,7 +4297,7 @@ int character::ReceiveBodyPartDamage(character* Damager, int Damage, int Type, i else if(IsPlayer() || CanBeSeenByPlayer()) ADD_MESSAGE("It vanishes."); - if(IsPlayer() && !cursedDeveloper::IsCursedDeveloper()) + if(IsPlayer() && !curseddeveloper::IsCursedDeveloper()) game::AskForKeyPress(CONST_S("Bodypart severed! [press any key to continue]")); } @@ -5211,7 +5211,7 @@ void character::DrawPanel(truth AnimationDraw) const v2(game::GetMaxScreenXSize() << 4, 9)); int iLeftPos=0; #ifdef CURSEDDEVELOPER - if(cursedDeveloper::IsCursedDeveloper()){ + if(curseddeveloper::IsCursedDeveloper()){ festring fsCD="(Cursed Developer) "; iLeftPos+=fsCD.GetSize()*FONT->GetFontSize().X; FONT->Printf(DOUBLE_BUFFER, v2(16, 45 + (game::GetMaxScreenYSize() << 4)), YELLOW, fsCD.CStr(), GetPanelName().CStr()); diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 39d6ec30f..4e6552633 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -21,14 +21,14 @@ */ #ifdef CURSEDDEVELOPER -bool cursedDeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); -bool cursedDeveloper::bCursedDeveloperTeleport = false; +bool curseddeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); +bool curseddeveloper::bCursedDeveloperTeleport = false; #define HEAL_1 1 #define HEAL_MINOK 2 #define HEAL_MAX 3 -void cursedDeveloper::RestoreLimbs(festring fsCmdParams) +void curseddeveloper::RestoreLimbs(festring fsCmdParams) { int iMode = fsCmdParams.IsEmpty() ? 0 : atoi(fsCmdParams.CStr()); @@ -46,7 +46,7 @@ void cursedDeveloper::RestoreLimbs(festring fsCmdParams) ABORT("HP>MaxHP %d>%d",P->HP,P->MaxHP); } -bool cursedDeveloper::CreateBP(int iIndex) +bool curseddeveloper::CreateBP(int iIndex) { character* P = game::GetPlayer(); @@ -62,7 +62,7 @@ bool cursedDeveloper::CreateBP(int iIndex) return bp!=NULL; } -bool cursedDeveloper::HealBP(int iIndex,int iMode,int iResHPoverride) +bool curseddeveloper::HealBP(int iIndex,int iMode,int iResHPoverride) { if(!CreateBP(iIndex))return false; @@ -118,13 +118,13 @@ bool cursedDeveloper::HealBP(int iIndex,int iMode,int iResHPoverride) // return false; //} -bool cursedDeveloper::LifeSaveJustABit(character* Killer) +bool curseddeveloper::LifeSaveJustABit(character* Killer) { if(!bCursedDeveloper) return false; static bool bInitDevCmdDummy = [](){ - devcons::AddDevCmd("RestoreLimbs",cursedDeveloper::RestoreLimbs, + devcons::AddDevCmd("RestoreLimbs",curseddeveloper::RestoreLimbs, "[1|2|3] 1=1HP 2=minUsableHP 3=maxHP. Restore missing limbs to the cursed developer, better use only if the game becomes unplayable."); return true;}(); @@ -227,7 +227,7 @@ bool AddState(character* Killer,long Flag,cchar* FlagName,long FlagD,cchar* Flag * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? * @return if player should stay (true) or teleport (false) */ -bool cursedDeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev) +bool curseddeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev) { if(!bCursedDeveloper)return true; if(!Killer)return true; From 9618d5aeaf26ab9b8e322300b91bf7fa2a1716ba Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 19:24:50 -0300 Subject: [PATCH 173/235] cursedDev:fix life save when polymorphed; --- Main/Source/curseddeveloper.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 4e6552633..f1e65e475 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -50,6 +50,13 @@ bool curseddeveloper::CreateBP(int iIndex) { character* P = game::GetPlayer(); + if(dynamic_cast(P)) + { + //ok + } + else if(iIndex!=TORSO_INDEX) //can be polymorphed into non humanoid + return false; + bodypart* bp = P->GetBodyPart(iIndex); if(!bp && P->CanCreateBodyPart(iIndex)) bp=P->CreateBodyPart(iIndex); From 5b15ddbc9ff4d0d2478b17902258d412857198e1 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 21:47:35 -0300 Subject: [PATCH 174/235] cursedDev:improving; --- Main/Source/char.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 28a0288eb..ad74551bf 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -8194,8 +8194,7 @@ truth character::ConsumeItem(item* Item, cfestring& ConsumeVerb, truth nibbling) return false; } - if(IsPlayer() - && HasHadBodyPart(Item) + if(IsPlayer() && !curseddeveloper::IsCursedDeveloper() && HasHadBodyPart(Item) && !game::TruthQuestion(CONST_S("Are you sure? You may be able to put it back... [y/N]"))) return false; From 91599cc630f13c52da65fc0f4ccf83fb990b324b Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 25 Apr 2020 22:55:00 -0300 Subject: [PATCH 175/235] cursedDev:wip; --- Main/Source/curseddeveloper.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index f1e65e475..6aeb1e108 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -58,8 +58,13 @@ bool curseddeveloper::CreateBP(int iIndex) return false; bodypart* bp = P->GetBodyPart(iIndex); - if(!bp && P->CanCreateBodyPart(iIndex)) + if(!bp && P->CanCreateBodyPart(iIndex)){ bp=P->CreateBodyPart(iIndex); + if(bp){ + ADD_MESSAGE("A new cursed %s grows on you.",bp->GetName(INDEFINITE).CStr()); + bp->SpillFluid(P,liquid::Spawn(LIQUID_DARKNESS, 5)); + } + } if(bp && bp->GetHP() > bp->GetMaxHP()){ //TODO how does this happens??? DBG4(iIndex,bp->GetHP(),bp->GetMaxHP(),bp->GetBodyPartName().CStr()); From 509e32d80f884ef6319213a037d963a1322b32be Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 26 Apr 2020 18:00:19 -0300 Subject: [PATCH 176/235] cursedDev: finally looks good, long lasting non wizard tests can be performed, this is probably ready; --- Main/Source/curseddeveloper.cpp | 100 +++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 6aeb1e108..dc8e3eec4 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -28,6 +28,11 @@ bool curseddeveloper::bCursedDeveloperTeleport = false; #define HEAL_MINOK 2 #define HEAL_MAX 3 +/** + * is some special/named/important character + */ +bool IsSpecialCharacter(character* C){return !C->CanBeCloned();} + void curseddeveloper::RestoreLimbs(festring fsCmdParams) { int iMode = fsCmdParams.IsEmpty() ? 0 : atoi(fsCmdParams.CStr()); @@ -58,11 +63,34 @@ bool curseddeveloper::CreateBP(int iIndex) return false; bodypart* bp = P->GetBodyPart(iIndex); - if(!bp && P->CanCreateBodyPart(iIndex)){ - bp=P->CreateBodyPart(iIndex); + + if(!bp){ + int iMod=1; + for(ulong iOBpID : P->GetOriginalBodyPartID(iIndex)){ + bp = dynamic_cast(game::SearchItem(iOBpID)); + if(bp)break; + } + if(bp){ - ADD_MESSAGE("A new cursed %s grows on you.",bp->GetName(INDEFINITE).CStr()); - bp->SpillFluid(P,liquid::Spawn(LIQUID_DARKNESS, 5)); + bp->RemoveFromSlot(); + P->AttachBodyPart(bp); + ADD_MESSAGE("Your creepy %s comes back to you.",bp->GetName(INDEFINITE).CStr()); + iMod=5; + }else + if(P->CanCreateBodyPart(iIndex)){ + bp=P->CreateBodyPart(iIndex); + if(bp){ + ADD_MESSAGE("A new cursed %s vibrates and grows on you.",bp->GetName(INDEFINITE).CStr()); + iMod=10; + } + } + + if(bp){ + bp->SpillFluid(P,liquid::Spawn(LIQUID_DARKNESS,iMod*2)); + if(iIndex == HEAD_INDEX){ + int iTm=iMod*10; + P->BeginTemporaryState(CONFUSED, iTm + RAND()%iTm); + } } } @@ -180,20 +208,22 @@ bool curseddeveloper::LifeSaveJustABit(character* Killer) game::SetMapNote(P->GetLSquareUnder(),"Your cursed life was saved here@"); //teleport is required to prevent death loop: killer keeps killing the player forever on every turn - if(Killer->GetSquaresUnder()>1){ + if(Killer->GetSquaresUnder()>1){ //huge foes bCursedDeveloperTeleport=true; P->TeleportRandomly(true); ADD_MESSAGE("You see a flash of dark light and teleport away from the killing blow!"); bCursedDeveloperTeleport=false; }else{ - bool bRestoreTL=false; - if(Killer->HasStateFlag(TELEPORT_LOCK)){ - Killer->DeActivateTemporaryState(TELEPORT_LOCK); - bRestoreTL=true; + if(IsSpecialCharacter(Killer)){ + bool bRestoreTL=false; + if(Killer->HasStateFlag(TELEPORT_LOCK)){ + Killer->DeActivateTemporaryState(TELEPORT_LOCK); + bRestoreTL=true; + } + Killer->TeleportRandomly(true); + if(bRestoreTL) + Killer->GainIntrinsic(TELEPORT_LOCK); } - Killer->TeleportRandomly(true); - if(bRestoreTL) - Killer->GainIntrinsic(TELEPORT_LOCK); } } @@ -235,7 +265,8 @@ bool AddState(character* Killer,long Flag,cchar* FlagName,long FlagD,cchar* Flag } /** - * this will make the NPC that kills the player more challenging for every kill + * This will make the Special NPC that kills the player more challenging for every kill. + * Non special NPCs will be promptly harmed and fully debuffed. * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? * @return if player should stay (true) or teleport (false) */ @@ -243,25 +274,28 @@ bool curseddeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in { if(!bCursedDeveloper)return true; if(!Killer)return true; + if(game::IsInWilderness())return true; riDebuff=0; rbRev=false; // BUFFs, every death makes it harder to player: riBuff=1; -#define ASRET(e,b) if(AddState(Killer,e,#e,0,NULL,b))return false; -#define ASRETD(e,d,b) if(AddState(Killer,e,#e,d,#d,b))return false; - ASRET(ESP,riBuff); - ASRET(INFRA_VISION,riBuff); -// ASRETD(HASTE,SLOW,riBuff); - ASRET(HASTE,riBuff); - ASRET(SWIMMING,riBuff); - ASRET(ETHEREAL_MOVING,riBuff); - ASRET(REGENERATION,riBuff); - ASRET(LEVITATION,riBuff); - ASRET(GAS_IMMUNITY,riBuff); - ASRET(TELEPORT_LOCK,riBuff); - ASRET(POLYMORPH_LOCK,riBuff); +#define ASRET(e,b) if(AddState(Killer,e,#e,0,NULL,b) && IsSpecialCharacter(Killer))return false; +//#define ASRETD(e,d,b) if(AddState(Killer,e,#e,d,#d,b) && IsSpecialCharacter(Killer))return false; + if(IsSpecialCharacter(Killer)){ + ASRET(ESP,riBuff); + ASRET(INFRA_VISION,riBuff); + // ASRETD(HASTE,SLOW,riBuff); + ASRET(HASTE,riBuff); + ASRET(SWIMMING,riBuff); + ASRET(ETHEREAL_MOVING,riBuff); + ASRET(REGENERATION,riBuff); + ASRET(LEVITATION,riBuff); + ASRET(GAS_IMMUNITY,riBuff); + ASRET(TELEPORT_LOCK,riBuff); + ASRET(POLYMORPH_LOCK,riBuff); + } /************************* * DEBUFFs, after player has taken too much it is time to make it stop, but slowly: @@ -276,15 +310,13 @@ bool curseddeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in ASRET(POISONED,riDebuff); // Revenge, grant it will stop: - if(!game::IsInWilderness() && game::GetCurrentLevel()) - game::GetCurrentLevel()->Explosion( - game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); - + game::GetCurrentLevel()->Explosion( + game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); + +// Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); + Killer->GetTorso()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 5 * PLAYER->GetAttribute(WISDOM))); +// Killer->GetLSquareUnder()->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, 100)); ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); - if(!game::IsInWilderness()){ - Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); - Killer->GetLSquareUnder()->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, 100)); - } rbRev=true; From a57c6b238511fc1cb656890dca8c05e5cdfd3743 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 27 Apr 2020 00:47:53 -0300 Subject: [PATCH 177/235] cursedDev: trying to fix SEGFAULT when dwarf explodes and player's head flies away; --- .../nbproject/configurations.xml | 12 ++----- Main/Source/curseddeveloper.cpp | 31 ++++++++++++++----- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 08b244db9..1842d2851 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -160,7 +160,6 @@ false - true @@ -168,15 +167,6 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean - - - /usr/include - - - CURSEDDEVELOPER - WIZARD - - . @@ -197,6 +187,7 @@ + true @@ -214,6 +205,7 @@ CMakeFiles/CMakeTmp + CURSEDDEVELOPER DBGMSG FELIST_WAITKEYUP WIZARD diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index dc8e3eec4..d4a31471c 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -68,20 +68,35 @@ bool curseddeveloper::CreateBP(int iIndex) int iMod=1; for(ulong iOBpID : P->GetOriginalBodyPartID(iIndex)){ bp = dynamic_cast(game::SearchItem(iOBpID)); - if(bp)break; + if(bp){ + if(iIndex == HEAD_INDEX){ + /** + * when a kamikaze dwarf explodes and player's head flies away, + * bringing it back apparently causes SEGFAULT at: + * game::run() > pool::be() > character::be() at `BodyPart->Be();` + * there is no null pointer, the `BodyPart` is valid and can be debugged, + * but when BodyPart->Be() is called it SEGFAULTs... + */ + bp->RemoveFromSlot(); + bp->SendToHell(); //so lets just destroy it to let a new one be created + bp=NULL; + } + break; + } } if(bp){ bp->RemoveFromSlot(); P->AttachBodyPart(bp); - ADD_MESSAGE("Your creepy %s comes back to you.",bp->GetName(INDEFINITE).CStr()); + ADD_MESSAGE("Your creepy %s comes back to you.",bp->GetName(UNARTICLED).CStr()); iMod=5; - }else - if(P->CanCreateBodyPart(iIndex)){ - bp=P->CreateBodyPart(iIndex); - if(bp){ - ADD_MESSAGE("A new cursed %s vibrates and grows on you.",bp->GetName(INDEFINITE).CStr()); - iMod=10; + }else{ + if(P->CanCreateBodyPart(iIndex)){ + bp=P->CreateBodyPart(iIndex); + if(bp){ + ADD_MESSAGE("A new cursed %s vibrates and grows on you.",bp->GetName(INDEFINITE).CStr()); + iMod=10; + } } } From 2c508157d7a081c8c3aa51b2297a95ba5ab03711 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 27 Apr 2020 01:33:18 -0300 Subject: [PATCH 178/235] fixed wizard mode resurrect to take precedence over cursedDev one. --- FeLib/Source/whandler.cpp | 2 +- Main/Source/char.cpp | 5 +++-- Main/Source/curseddeveloper.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/FeLib/Source/whandler.cpp b/FeLib/Source/whandler.cpp index 54fb614ec..f5e886285 100644 --- a/FeLib/Source/whandler.cpp +++ b/FeLib/Source/whandler.cpp @@ -614,7 +614,7 @@ void globalwindowhandler::ProcessKeyDownMessage(SDL_Event* Event) return; } - if(Event->key.keysym.mod & KMOD_CTRL){ + if(Event->key.keysym.mod & KMOD_CTRL){ //TODO right control key is being ignored on lists for ctrl+f filter on the first try if(ControlKeyHandler!=NULL) //this one was completely externalized ControlKeyHandler(Event->key.keysym.sym); return; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index ad74551bf..aa65c6cb3 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1694,8 +1694,9 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) ADD_MESSAGE("You die."); #ifdef CURSEDDEVELOPER - if(curseddeveloper::LifeSaveJustABit((character*)Killer)) - return; + if(!game::WizardModeIsReallyActive()) + if(curseddeveloper::LifeSaveJustABit((character*)Killer)) + return; #endif #ifdef WIZARD diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index d4a31471c..dd0163232 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -94,7 +94,7 @@ bool curseddeveloper::CreateBP(int iIndex) if(P->CanCreateBodyPart(iIndex)){ bp=P->CreateBodyPart(iIndex); if(bp){ - ADD_MESSAGE("A new cursed %s vibrates and grows on you.",bp->GetName(INDEFINITE).CStr()); + ADD_MESSAGE("A new cursed %s vibrates and grows on you.",bp->GetName(UNARTICLED).CStr()); iMod=10; } } From 4c054f4394717437b0f4f6d95a01214895b60075 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 27 Apr 2020 02:26:04 -0300 Subject: [PATCH 179/235] revised/improved character::ValidateTrapData() usage; --- Main/Include/char.h | 2 +- Main/Source/bodypart.cpp | 3 +- Main/Source/char.cpp | 60 +++++++++++++++++++++---------------- Main/Source/miscitem.cpp | 3 +- Main/Source/traps.cpp | 3 +- Main/Source/wizautoplay.cpp | 2 ++ 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/Main/Include/char.h b/Main/Include/char.h index 4fed4a218..2243d81f4 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -1129,7 +1129,7 @@ class character : public entity, public id festring GetTrapDescription() const; truth TryToUnStickTraps(v2); void RemoveTrap(ulong); - void ValidateTrapData(); + truth ValidateTrapData(); void AddTrap(ulong, ulong); truth IsStuckToTrap(ulong) const; void RemoveTraps(); diff --git a/Main/Source/bodypart.cpp b/Main/Source/bodypart.cpp index 4f2a56833..25a6bf9c3 100644 --- a/Main/Source/bodypart.cpp +++ b/Main/Source/bodypart.cpp @@ -3537,8 +3537,7 @@ void bodypart::UpdateFlags() else Flags &= ~BADLY_HURT; - Master->ValidateTrapData(); - if(Master->BodyPartIsStuck(GetBodyPartIndex())) + if(Master->ValidateTrapData() && Master->BodyPartIsStuck(GetBodyPartIndex())) Flags |= STUCK; else Flags &= ~STUCK; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index aa65c6cb3..6eee1e277 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -11097,16 +11097,17 @@ truth character::IsUsingWeaponOfCategory(int Category) const truth character::TryToUnStickTraps(v2 Dir) { - ValidateTrapData(); - for(trapdata* T = TrapData; T; T = T->Next) - if(IsEnabled()) - { - entity* Trap = game::SearchTrap(T->TrapID); - if(Trap && Trap->GetVictimID() == GetID() && Trap->TryToUnStick(this, Dir)) - break; + if(ValidateTrapData()){ + for(trapdata* T = TrapData; T; T = T->Next){ + if(IsEnabled()) + { + entity* Trap = game::SearchTrap(T->TrapID); + if(Trap && Trap->GetVictimID() == GetID() && Trap->TryToUnStick(this, Dir)) + break; + } } - - ValidateTrapData(); + ValidateTrapData(); //if something changed this fixes it + } return !TrapData && IsEnabled(); } @@ -11117,30 +11118,39 @@ struct trapidcomparer ulong ID; }; -void character::ValidateTrapData() +truth character::ValidateTrapData() { - for(trapdata* T = TrapData; T;) - { - if(!game::SearchTrap(T->TrapID)){ - trapdata* ToDel = T; - if(TrapData==ToDel) - TrapData=T->Next; - T = T->Next; - delete ToDel; - }else{ + if(!TrapData)return false; + + static trapdata* ToDel=NULL; + trapdata* T = TrapData; + for(;T;){ + if(game::SearchTrap(T->TrapID)){ T = T->Next; + continue; + } + + ToDel = T; + T = T->Next; + if(TrapData==ToDel){ + TrapData=T; //update 1st on the LL } + delete ToDel; + ToDel=NULL; } + + return TrapData!=NULL; } void character::RemoveTrap(ulong ID) { - ValidateTrapData(); - trapdata*& T = ListFind(TrapData, trapidcomparer(ID)); - if(T){ - trapdata* ToDel = T; - T = T->Next; - delete ToDel; + if(ValidateTrapData()){ + trapdata*& T = ListFind(TrapData, trapidcomparer(ID)); + if(T){ + trapdata* ToDel = T; + T = T->Next; + delete ToDel; + } } doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange); } diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp index 55d8b139d..cf3e5eb0e 100644 --- a/Main/Source/miscitem.cpp +++ b/Main/Source/miscitem.cpp @@ -1602,8 +1602,7 @@ void beartrap::StepOnEffect(character* Stepper) truth beartrap::CheckPickUpEffect(character* Picker) { - Picker->ValidateTrapData(); - if(Picker->IsStuckToTrap(GetTrapID())) + if(Picker->ValidateTrapData() && Picker->IsStuckToTrap(GetTrapID())) { if(Picker->IsPlayer()) ADD_MESSAGE("You are tightly stuck in %s.", CHAR_NAME(DEFINITE)); diff --git a/Main/Source/traps.cpp b/Main/Source/traps.cpp index d870f3c91..95a27e4d3 100644 --- a/Main/Source/traps.cpp +++ b/Main/Source/traps.cpp @@ -57,8 +57,7 @@ truth web::TryToTearDown(character* Actor,int Modifier) { //if(GetLSquareUnder()->GetPos()==Actor->GetPos())C->RemoveTrap(GetTrapID());else character* C = GetLSquareUnder()->GetCharacter(); - if(C)C->ValidateTrapData(); - if(C && C->IsStuckToTrap(GetTrapID())) + if(C && C->ValidateTrapData() && C->IsStuckToTrap(GetTrapID())) C->RemoveTrap(GetTrapID()); TrapData.VictimID = 0; GetLSquareUnder()->RemoveTrap(this); diff --git a/Main/Source/wizautoplay.cpp b/Main/Source/wizautoplay.cpp index 4f6fd4205..55ac34a32 100644 --- a/Main/Source/wizautoplay.cpp +++ b/Main/Source/wizautoplay.cpp @@ -401,8 +401,10 @@ truth wizautoplay::AutoPlayAIDropThings() bool wizautoplay::IsAutoplayAICanPickup(item* it,bool bPlayerHasLantern) { if(!it->CanBeSeenBy(P))return false; + P->ValidateTrapData(); if(!it->IsPickable(P))return false; + if(it->GetSquaresUnder()!=1)return false; //avoid big corpses 2x2 if(!bPlayerHasLantern && (it->IsOnFire(P) || it->GetEmitation()>0)){ From 91f88626c6dc6506341d5a30c4556e423a200929 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 27 Apr 2020 03:41:43 -0300 Subject: [PATCH 180/235] cursedDev: fix init of devCons related cmd; --- Main/Include/curseddeveloper.h | 6 ++++++ Main/Source/curseddeveloper.cpp | 18 ++++++++++++++---- Main/Source/main.cpp | 4 ++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h index 626560199..794c38b63 100644 --- a/Main/Include/curseddeveloper.h +++ b/Main/Include/curseddeveloper.h @@ -13,9 +13,14 @@ #ifndef __CURSEDDEVELOPER_H__ #define __CURSEDDEVELOPER_H__ +class bodypart; +class character; +class festring; + class curseddeveloper { #ifdef CURSEDDEVELOPER public: + static void Init(); static bool IsCursedDeveloper(){return bCursedDeveloper;}; static bool IsCursedDeveloperTeleport(){return bCursedDeveloperTeleport;} static bool LifeSaveJustABit(character* Killer); @@ -27,6 +32,7 @@ class curseddeveloper { private: static bool bCursedDeveloper; static bool bCursedDeveloperTeleport; + static bool bInit; #else public: static bool IsCursedDeveloper(){return false;} diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index dd0163232..edb1ce512 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -173,15 +173,25 @@ bool curseddeveloper::HealBP(int iIndex,int iMode,int iResHPoverride) // return false; //} +//curseddeveloper::curseddeveloper() +//{ +// devcons::AddDevCmd("RestoreLimbs",curseddeveloper::RestoreLimbs, +// "[1|2|3] 1=1HP 2=minUsableHP 3=maxHP. Restore missing limbs to the cursed developer, better use only if the game becomes unplayable."); +//} +void curseddeveloper::Init(){ + devcons::AddDevCmd("RestoreLimbs",curseddeveloper::RestoreLimbs, + "[1|2|3] 1=1HP 2=minUsableHP 3=maxHP. Restore missing limbs to the cursed developer, better use only if the game becomes unplayable."); +} + bool curseddeveloper::LifeSaveJustABit(character* Killer) { if(!bCursedDeveloper) return false; - static bool bInitDevCmdDummy = [](){ - devcons::AddDevCmd("RestoreLimbs",curseddeveloper::RestoreLimbs, - "[1|2|3] 1=1HP 2=minUsableHP 3=maxHP. Restore missing limbs to the cursed developer, better use only if the game becomes unplayable."); - return true;}(); +// static bool bInitDevCmdDummy = [](){ +// devcons::AddDevCmd("RestoreLimbs",curseddeveloper::RestoreLimbs, +// "[1|2|3] 1=1HP 2=minUsableHP 3=maxHP. Restore missing limbs to the cursed developer, better use only if the game becomes unplayable."); +// return true;}(); character* P = game::GetPlayer(); game::DrawEverything(); diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index 79652e0d8..028b032a7 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -30,6 +30,7 @@ #endif #include "game.h" +#include "curseddeveloper.h" #include "database.h" #include "definesvalidator.h" #include "devcons.h" @@ -174,6 +175,9 @@ int main(int argc, char** argv) specialkeys::init(); bugfixdp::init(); devcons::Init(); +#ifdef CURSEDDEVELOPER + curseddeveloper::Init(); +#endif definesvalidator::init(); msgsystem::Init(); protosystem::Initialize(); From a5d69953e1999e376796431fab100df462faf41a Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Mon, 27 Apr 2020 19:26:56 -0300 Subject: [PATCH 181/235] Join and split lump now grants minimum craft skill possible. cursedDev: improving; --- Main/Source/cmdcraft.cpp | 3 +++ Main/Source/curseddeveloper.cpp | 27 +++++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 5959c1673..a47ccf6cc 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -1786,6 +1786,8 @@ struct srpJoinLumps : public recipe{ } virtual bool work(recipedata& rpd){ // it is just like to put them all together, no effort, instant. + rpd.fDifficulty=0.1; + askForEqualLumps(rpd); if(rpd.ingredientsIDs.empty()){ @@ -2157,6 +2159,7 @@ struct srpSplitLump : public recipe{ item* ToSplit = NULL; rpd.bGradativeCraftOverride=true; //may be disabled below + rpd.fDifficulty=0.1; if(ToSplit==NULL && choseOneIngredient(rpd)){ //can be split with hands only ToSplit = game::SearchItem(rpd.ingredientsIDs[0]); diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index edb1ce512..50911f436 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -257,6 +257,13 @@ bool curseddeveloper::LifeSaveJustABit(character* Killer) if(!game::IsInWilderness()) P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); + if(bRev || IsSpecialCharacter(Killer)){ + character* M = P->DuplicateToNearestSquare(P, MIRROR_IMAGE|IGNORE_PROHIBITIONS|CHANGE_TEAM); + static int i1Min=33; + if(M) + M->SetLifeExpectancy(i1Min*5,i1Min*10);//33 is 1 min or 1 turn right? see: game::GetTime() + } + ADD_MESSAGE("Your curse forbids you to rest and be remembered..."); game::DrawEverything(); @@ -291,7 +298,7 @@ bool AddState(character* Killer,long Flag,cchar* FlagName,long FlagD,cchar* Flag /** * This will make the Special NPC that kills the player more challenging for every kill. - * Non special NPCs will be promptly harmed and fully debuffed. + * Non special NPCs will fall faster. * TODO could these NPC permanent upgrades be part of the normal gameplay in some way? May be, the life saving ammulet could let these buffs also be applied? * @return if player should stay (true) or teleport (false) */ @@ -329,19 +336,23 @@ bool curseddeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in ASRET(HICCUPS,riDebuff); // ASRETD(SLOW,HASTE,riDebuff); ASRET(SLOW,riDebuff); -// ASRET(PARASITE_TAPE_WORM,riDebuff); +//no, adds more mobs... ASRET(PARASITE_TAPE_WORM,riDebuff); ASRET(CONFUSED,riDebuff); -// ASRET(LEPROSY,riDebuff); - ASRET(POISONED,riDebuff); +//no, may mess the player... ASRET(LEPROSY,riDebuff); + if(RAND()%10==0) + ASRET(POISONED,riDebuff); // Revenge, grant it will stop: - game::GetCurrentLevel()->Explosion( - game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); + if(RAND()%5==0) + game::GetCurrentLevel()->Explosion( + game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); // Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); - Killer->GetTorso()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 5 * PLAYER->GetAttribute(WISDOM))); + if(RAND()%10==0){ + Killer->GetTorso()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 5 * PLAYER->GetAttribute(WISDOM))); + ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); + } // Killer->GetLSquareUnder()->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, 100)); - ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); rbRev=true; From d70f9907233e1440d7c5936535266b693a714c31 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 28 Apr 2020 12:36:47 -0300 Subject: [PATCH 182/235] cursedDev: improving; --- .../nbproject/configurations.xml | 9 ++ Main/Include/curseddeveloper.h | 3 + Main/Source/char.cpp | 14 ++- Main/Source/curseddeveloper.cpp | 85 ++++++++++++++++--- 4 files changed, 97 insertions(+), 14 deletions(-) diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 1842d2851..9039e529d 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -160,6 +160,7 @@ false + true @@ -167,6 +168,14 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean + + + CURSEDDEVELOPER + DBGMSG + FELIST_WAITKEYUP + WIZARD + + . diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h index 794c38b63..c8a7998a5 100644 --- a/Main/Include/curseddeveloper.h +++ b/Main/Include/curseddeveloper.h @@ -29,10 +29,13 @@ class curseddeveloper { static bool HealTorso(bodypart* bp); static bool HealBP(int iIndex,int iMode,int iMinHpOK=0); static bool CreateBP(int iIndex); + static long UpdateKillCredit(character* Victim); + static long GetKillCredit(){return lKillCredit;} private: static bool bCursedDeveloper; static bool bCursedDeveloperTeleport; static bool bInit; + static long lKillCredit; #else public: static bool IsCursedDeveloper(){return false;} diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 6eee1e277..e8f966fcf 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -1687,8 +1687,15 @@ void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) if(!IsEnabled()) return; +#ifdef CURSEDDEVELOPER + if(Killer && Killer->IsPlayer()){ + if(!game::WizardModeIsReallyActive()) + curseddeveloper::UpdateKillCredit(this); + } +#endif + RemoveTraps(); - + if(IsPlayer()) { ADD_MESSAGE("You die."); @@ -5213,9 +5220,10 @@ void character::DrawPanel(truth AnimationDraw) const int iLeftPos=0; #ifdef CURSEDDEVELOPER if(curseddeveloper::IsCursedDeveloper()){ - festring fsCD="(Cursed Developer) "; + festring fsCD;fsCD<<"(Cursed Developer, KC="<GetFontSize().X; - FONT->Printf(DOUBLE_BUFFER, v2(16, 45 + (game::GetMaxScreenYSize() << 4)), YELLOW, fsCD.CStr(), GetPanelName().CStr()); + FONT->Printf(DOUBLE_BUFFER, v2(16, 45 + (game::GetMaxScreenYSize() << 4)), + curseddeveloper::GetKillCredit()<0?RED:YELLOW, fsCD.CStr(), GetPanelName().CStr()); } #endif FONT->Printf(DOUBLE_BUFFER, v2(16+iLeftPos, 45 + (game::GetMaxScreenYSize() << 4)), WHITE, "%s", GetPanelName().CStr()); diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 50911f436..4368df917 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -23,16 +23,52 @@ #ifdef CURSEDDEVELOPER bool curseddeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); bool curseddeveloper::bCursedDeveloperTeleport = false; +long curseddeveloper::lKillCredit=0; -#define HEAL_1 1 +#define HEAL_1 1 #define HEAL_MINOK 2 -#define HEAL_MAX 3 +#define HEAL_MAX 3 /** * is some special/named/important character */ bool IsSpecialCharacter(character* C){return !C->CanBeCloned();} +long curseddeveloper::UpdateKillCredit(character* Victim){ + character* P=PLAYER; + while(P->GetID()!=1){ + if(PLAYER->GetPolymorphBackup()){ + DBG2(P->GetID(),P->GetNameSingular().CStr()); + P=PLAYER->GetPolymorphBackup(); + DBGEXEC(if(P){DBG2(P->GetID(),P->GetNameSingular().CStr());}); + }else{ + break; + } + } + if(P->GetID()!=1) + ABORT("Player ID 1 can't be found!!!"); + + static bool bInitKC=true; + if(bInitKC){ + festring fs=P->GetTorso()->GetLabel(); + if(!fs.IsEmpty()) + lKillCredit = atol(fs.CStr()); + bInitKC=false; + } + + if(Victim){ +// lKillCredit += IsSpecialCharacter(Victim) ? 100 : 1; //TODO consider danger ? + int i = Victim->GetRelativeDanger(P)*10; + if(i<1)i=1; + lKillCredit+=i; + } + + P->GetTorso()->SetLabel(festring()<RemoveFromSlot(); P->AttachBodyPart(bp); ADD_MESSAGE("Your creepy %s comes back to you.",bp->GetName(UNARTICLED).CStr()); + lKillCredit--; iMod=5; }else{ if(P->CanCreateBodyPart(iIndex)){ bp=P->CreateBodyPart(iIndex); if(bp){ ADD_MESSAGE("A new cursed %s vibrates and grows on you.",bp->GetName(UNARTICLED).CStr()); + lKillCredit-=2; iMod=10; } } @@ -255,17 +293,34 @@ bool curseddeveloper::LifeSaveJustABit(character* Killer) // at resurrect spot if(iKillerDebuff>0) // if enemy got too powerful, buff the player randomly if(!game::IsInWilderness()) - P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); + P->GetLSquareUnder()->SpillFluid(NULL, liquid::Spawn(MAGIC_LIQUID, 30 * P->GetAttribute(WISDOM))); if(bRev || IsSpecialCharacter(Killer)){ character* M = P->DuplicateToNearestSquare(P, MIRROR_IMAGE|IGNORE_PROHIBITIONS|CHANGE_TEAM); - static int i1Min=33; - if(M) - M->SetLifeExpectancy(i1Min*5,i1Min*10);//33 is 1 min or 1 turn right? see: game::GetTime() + static int i1Min=33; //33 is 1 min or 1 turn right? see: game::GetTime() + if(M){ + M->SetLifeExpectancy(i1Min*5,i1Min*10); + lKillCredit--; + } } ADD_MESSAGE("Your curse forbids you to rest and be remembered..."); + if(lKillCredit<0 && RAND()%10==0){ + game::TryTravel(3, 0, DOUBLE_BED, false, true); // teleport to a bed in tweiraith island TODO should be the small bed at the small house + + ADD_MESSAGE("You wakeup from a nightmare!"); + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(SWEAT, 5 * P->GetAttribute(ENDURANCE))); + + if(RAND()%3){ + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(OMMEL_URINE, 5 * P->GetAttribute(ENDURANCE))); //ugh.. not ommel's tho.. TODO will this buff the player? + ADD_MESSAGE("You need a bath now..."); + } + + lKillCredit=0; // to reset it + UpdateKillCredit(NULL); + } + game::DrawEverything(); return true; @@ -339,18 +394,26 @@ bool curseddeveloper::BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,in //no, adds more mobs... ASRET(PARASITE_TAPE_WORM,riDebuff); ASRET(CONFUSED,riDebuff); //no, may mess the player... ASRET(LEPROSY,riDebuff); - if(RAND()%10==0) + if(RAND()%10==0){ ASRET(POISONED,riDebuff); + lKillCredit-=2; + } // Revenge, grant it will stop: - if(RAND()%5==0) - game::GetCurrentLevel()->Explosion( - game::GetPlayer(), CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); + if(RAND()%5==0){ + for(int i=0; i < ( RAND() % (IsSpecialCharacter(Killer)?5:2) + 1 ) ;i++){ + if(Killer->IsDead())break; + game::GetCurrentLevel()->Explosion( + NULL, CONST_S("Killed by cursed fire!"), Killer->GetPos(), 9/*1 square size*/, false, true); + lKillCredit--; + } + } // Killer->GetLSquareUnder()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 30 * PLAYER->GetAttribute(WISDOM))); if(RAND()%10==0){ - Killer->GetTorso()->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 5 * PLAYER->GetAttribute(WISDOM))); + Killer->GetTorso()->SpillFluid(NULL, liquid::Spawn(SULPHURIC_ACID, 5 * PLAYER->GetAttribute(WISDOM))); ADD_MESSAGE("Cursed acid hits %s!", Killer->GetName(DEFINITE).CStr()); + lKillCredit-=3; } // Killer->GetLSquareUnder()->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, 100)); From a7d801997f7e870880af0b388db5f8710707097f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 28 Apr 2020 16:09:24 -0300 Subject: [PATCH 183/235] cursedDev: wip; --- Main/Include/curseddeveloper.h | 8 +++-- Main/Source/curseddeveloper.cpp | 53 ++++++++++++++++++++++++--------- Main/Source/game.cpp | 5 ++++ 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/Main/Include/curseddeveloper.h b/Main/Include/curseddeveloper.h index c8a7998a5..ba6a84df0 100644 --- a/Main/Include/curseddeveloper.h +++ b/Main/Include/curseddeveloper.h @@ -24,23 +24,25 @@ class curseddeveloper { static bool IsCursedDeveloper(){return bCursedDeveloper;}; static bool IsCursedDeveloperTeleport(){return bCursedDeveloperTeleport;} static bool LifeSaveJustABit(character* Killer); + static long UpdateKillCredit(character* Victim); + static long GetKillCredit(){return lKillCredit;} + protected: static bool BuffAndDebuffPlayerKiller(character* Killer,int& riBuff,int& riDebuff,bool& rbRev); static void RestoreLimbs(festring fsCmdParams); static bool HealTorso(bodypart* bp); static bool HealBP(int iIndex,int iMode,int iMinHpOK=0); static bool CreateBP(int iIndex); - static long UpdateKillCredit(character* Victim); - static long GetKillCredit(){return lKillCredit;} + static void NightmareWakeUp(character* P); private: static bool bCursedDeveloper; static bool bCursedDeveloperTeleport; static bool bInit; static long lKillCredit; + static bool bNightmareWakeUp; #else public: static bool IsCursedDeveloper(){return false;} static bool IsCursedDeveloperTeleport(){return false;} - static bool LifeSaveJustABit(character* Killer){return false;} #endif }; diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 4368df917..2ebad913a 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -24,6 +24,7 @@ bool curseddeveloper::bCursedDeveloper = [](){char* pc=getenv("IVAN_CURSEDDEVELOPER");return strcmp(pc?pc:"","true")==0;}(); bool curseddeveloper::bCursedDeveloperTeleport = false; long curseddeveloper::lKillCredit=0; +bool curseddeveloper::bNightmareWakeUp=false; #define HEAL_1 1 #define HEAL_MINOK 2 @@ -34,8 +35,23 @@ long curseddeveloper::lKillCredit=0; */ bool IsSpecialCharacter(character* C){return !C->CanBeCloned();} +void curseddeveloper::NightmareWakeUp(character* P) +{ + ADD_MESSAGE("You wakeup from a nightmare! But... for some reason, you feel stronger..."); + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(SWEAT, 5 * P->GetAttribute(ENDURANCE))); + + if(RAND()%3){ + P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(OMMEL_URINE, 5 * P->GetAttribute(ENDURANCE))); //ugh.. not ommel's tho.. TODO will this buff the player? + ADD_MESSAGE("You need a bath now..."); + } + + lKillCredit=0; // to reset it +} + long curseddeveloper::UpdateKillCredit(character* Victim){ character* P=PLAYER; + if(!P)return lKillCredit; + while(P->GetID()!=1){ if(PLAYER->GetPolymorphBackup()){ DBG2(P->GetID(),P->GetNameSingular().CStr()); @@ -51,6 +67,7 @@ long curseddeveloper::UpdateKillCredit(character* Victim){ static bool bInitKC=true; if(bInitKC){ festring fs=P->GetTorso()->GetLabel(); + DBG2("StoredKillCredit",fs.CStr()); if(!fs.IsEmpty()) lKillCredit = atol(fs.CStr()); bInitKC=false; @@ -63,6 +80,11 @@ long curseddeveloper::UpdateKillCredit(character* Victim){ lKillCredit+=i; } + if(bNightmareWakeUp){ + NightmareWakeUp(P); + bNightmareWakeUp=false; + } + P->GetTorso()->SetLabel(festring()<GetLSquareUnder()->SpillFluid(P, liquid::Spawn(SWEAT, 5 * P->GetAttribute(ENDURANCE))); - - if(RAND()%3){ - P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(OMMEL_URINE, 5 * P->GetAttribute(ENDURANCE))); //ugh.. not ommel's tho.. TODO will this buff the player? - ADD_MESSAGE("You need a bath now..."); + UpdateKillCredit(NULL); + + game::DrawEverything(); + + if(lKillCredit<0){ + if(RAND()%10==0){ + for(int i=0;i<10;i++){ + if(game::TryTravel(3, 0, DOUBLE_BED, false, true)){ // teleport to a bed in tweiraith island TODO should be the small bed at the small house + bNightmareWakeUp=true; + UpdateKillCredit(NULL); + return true; // after TryTravel() avoid most code... + } + P->TeleportRandomly(true); //try to move away from foes to be able to travel + ADD_MESSAGE("You feel haunted!"); + } + }else{ + ADD_MESSAGE("You feel unconfortable..."); } - - lKillCredit=0; // to reset it - UpdateKillCredit(NULL); } - game::DrawEverything(); - return true; } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 18a46c641..b3bb21558 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -41,6 +41,7 @@ #include "bugworkaround.h" #include "confdef.h" #include "command.h" +#include "curseddeveloper.h" #include "definesvalidator.h" #include "feio.h" #include "felist.h" @@ -3823,6 +3824,10 @@ void game::SetPlayer(character* NP) if(Player) Player->AddFlags(C_PLAYER); + +#ifdef CURSEDDEVELOPER + curseddeveloper::UpdateKillCredit(NULL); +#endif } void game::InitDungeons() From b502b64debfcfd43e4d132459a8700b5946f0174 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 28 Apr 2020 19:46:02 -0300 Subject: [PATCH 184/235] cursedDev: wip; --- Main/Source/curseddeveloper.cpp | 35 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index 2ebad913a..b9c7c3aa3 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -37,7 +37,7 @@ bool IsSpecialCharacter(character* C){return !C->CanBeCloned();} void curseddeveloper::NightmareWakeUp(character* P) { - ADD_MESSAGE("You wakeup from a nightmare! But... for some reason, you feel stronger..."); + ADD_MESSAGE("You had a nightmare! And... for some reason, you feel stronger..."); P->GetLSquareUnder()->SpillFluid(P, liquid::Spawn(SWEAT, 5 * P->GetAttribute(ENDURANCE))); if(RAND()%3){ @@ -64,14 +64,19 @@ long curseddeveloper::UpdateKillCredit(character* Victim){ if(P->GetID()!=1) ABORT("Player ID 1 can't be found!!!"); - static bool bInitKC=true; - if(bInitKC){ - festring fs=P->GetTorso()->GetLabel(); - DBG2("StoredKillCredit",fs.CStr()); - if(!fs.IsEmpty()) - lKillCredit = atol(fs.CStr()); - bInitKC=false; - } +// static bool bInitKC=true; +// static character* CharPrevious=NULL; +// if(CharPrevious!=P) +// if(bInitKC){ +// festring fs=P->GetTorso()->GetLabel(); +// DBG2("StoredKillCredit",fs.CStr()); +// if(!fs.IsEmpty()) +// lKillCredit = atol(fs.CStr()); +// bInitKC=false; +// } + festring fs=P->GetTorso()->GetLabel(); + if(!fs.IsEmpty()) + lKillCredit = atol(fs.CStr()); if(Victim){ // lKillCredit += IsSpecialCharacter(Victim) ? 100 : 1; //TODO consider danger ? @@ -332,12 +337,18 @@ bool curseddeveloper::LifeSaveJustABit(character* Killer) game::DrawEverything(); - if(lKillCredit<0){ + int iDung=3,iLvl=0; //tweiraith island + bool bIsAtHomeIsland = P->GetDungeon()->GetIndex()==iDung && P->GetLevel()->GetIndex()==iLvl; + if(lKillCredit<0 && bIsAtHomeIsland) + lKillCredit=0; //to prevent pointless too negative value + if(lKillCredit<0 && !game::IsInWilderness() && !bIsAtHomeIsland){ if(RAND()%10==0){ for(int i=0;i<10;i++){ - if(game::TryTravel(3, 0, DOUBLE_BED, false, true)){ // teleport to a bed in tweiraith island TODO should be the small bed at the small house + ADD_MESSAGE("You try to wakeup..."); + if(game::TryTravel(iDung, iLvl, DOUBLE_BED, false, true)){ //TODO should be the small bed at the small house bNightmareWakeUp=true; - UpdateKillCredit(NULL); + ADD_MESSAGE("You finally wakeup."); + UpdateKillCredit(NULL); //to call nightmare wakeup return true; // after TryTravel() avoid most code... } P->TeleportRandomly(true); //try to move away from foes to be able to travel From c0943baa01d437e1248d79b924ab384a10cc9779 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 28 Apr 2020 19:59:02 -0300 Subject: [PATCH 185/235] cursedDev: looks ok (again); --- Main/Source/curseddeveloper.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Main/Source/curseddeveloper.cpp b/Main/Source/curseddeveloper.cpp index b9c7c3aa3..5366f0fce 100644 --- a/Main/Source/curseddeveloper.cpp +++ b/Main/Source/curseddeveloper.cpp @@ -74,12 +74,15 @@ long curseddeveloper::UpdateKillCredit(character* Victim){ // lKillCredit = atol(fs.CStr()); // bInitKC=false; // } - festring fs=P->GetTorso()->GetLabel(); - if(!fs.IsEmpty()) - lKillCredit = atol(fs.CStr()); + festring fsKillCredit=P->GetTorso()->GetLabel(); + DBG1(fsKillCredit.CStr()); + if(!fsKillCredit.IsEmpty()){ + lKillCredit = atol(fsKillCredit.CStr()); + }else{ + lKillCredit=0; + } if(Victim){ -// lKillCredit += IsSpecialCharacter(Victim) ? 100 : 1; //TODO consider danger ? int i = Victim->GetRelativeDanger(P)*10; if(i<1)i=1; lKillCredit+=i; From 8eb4c63a2691efd3cbb0fda834567d99198f71d4 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Tue, 28 Apr 2020 21:22:07 -0300 Subject: [PATCH 186/235] character::ValidateTrapData() workaround to be more performance wise --- Main/Include/char.h | 3 ++- Main/Source/char.cpp | 40 +++++++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Main/Include/char.h b/Main/Include/char.h index 2243d81f4..a1a2f0832 100644 --- a/Main/Include/char.h +++ b/Main/Include/char.h @@ -1129,7 +1129,7 @@ class character : public entity, public id festring GetTrapDescription() const; truth TryToUnStickTraps(v2); void RemoveTrap(ulong); - truth ValidateTrapData(); + truth ValidateTrapData(bool bForceNow = false); void AddTrap(ulong, ulong); truth IsStuckToTrap(ulong) const; void RemoveTraps(); @@ -1304,6 +1304,7 @@ class character : public entity, public id ulong WarnFlags; int ScienceTalks; trapdata* TrapData; + long lTmpLastValidateTrapDataTurn; //performace wise, no need to store expmodifiermap ExpModifierMap; int CounterToMindWormHatch; ulong MemorizedEquippedItemIDs[MAX_EQUIPMENT_SLOTS]; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index e8f966fcf..eaf4366ff 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -537,7 +537,8 @@ character::character(ccharacter& Char) Stamina(Char.Stamina), MaxStamina(Char.MaxStamina), BlocksSinceLastTurn(0), GenerationDanger(Char.GenerationDanger), CommandFlags(Char.CommandFlags), WarnFlags(0), - ScienceTalks(Char.ScienceTalks), TrapData(0), CounterToMindWormHatch(0) + ScienceTalks(Char.ScienceTalks), TrapData(0), CounterToMindWormHatch(0), + lTmpLastValidateTrapDataTurn(-1) { Flags &= ~C_PLAYER; Flags |= C_INITIALIZING|C_IN_NO_MSG_MODE; @@ -596,7 +597,8 @@ character::character() Money(0), Action(0), MotherEntity(0), PolymorphBackup(0), EquipmentState(0), SquareUnder(0), RegenerationCounter(0), HomeData(0), LastAcidMsgMin(0), BlocksSinceLastTurn(0), GenerationDanger(DEFAULT_GENERATION_DANGER), - WarnFlags(0), ScienceTalks(0), TrapData(0), CounterToMindWormHatch(0) + WarnFlags(0), ScienceTalks(0), TrapData(0), CounterToMindWormHatch(0), + lTmpLastValidateTrapDataTurn(-1) { Stack = new stack(0, this, HIDDEN); @@ -11105,7 +11107,7 @@ truth character::IsUsingWeaponOfCategory(int Category) const truth character::TryToUnStickTraps(v2 Dir) { - if(ValidateTrapData()){ + if(ValidateTrapData(true)){ for(trapdata* T = TrapData; T; T = T->Next){ if(IsEnabled()) { @@ -11126,25 +11128,29 @@ struct trapidcomparer ulong ID; }; -truth character::ValidateTrapData() +truth character::ValidateTrapData(bool bForceNow) { if(!TrapData)return false; - static trapdata* ToDel=NULL; - trapdata* T = TrapData; - for(;T;){ - if(game::SearchTrap(T->TrapID)){ + if(bForceNow || game::GetTurn()!=lTmpLastValidateTrapDataTurn){ + static trapdata* ToDel=NULL; + trapdata* T = TrapData; + for(;T;){ + if(game::SearchTrap(T->TrapID)){ //may ne slow, performance bottle neck... + T = T->Next; + continue; + } + + ToDel = T; T = T->Next; - continue; - } - - ToDel = T; - T = T->Next; - if(TrapData==ToDel){ - TrapData=T; //update 1st on the LL + if(TrapData==ToDel){ + TrapData=T; //update 1st on the LL + } + delete ToDel; + ToDel=NULL; } - delete ToDel; - ToDel=NULL; + + lTmpLastValidateTrapDataTurn = game::GetTurn(); } return TrapData!=NULL; From 83e76c2e4f4e6a7fc32dcfe9e43a482c810c354c Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Wed, 29 Apr 2020 02:07:09 -0300 Subject: [PATCH 187/235] dynamic main menu lowered size of menu image(s) --- .../nbproject/configurations.xml | 1 + FeLib/Include/feio.h | 2 +- FeLib/Source/feio.cpp | 57 +++++++++--------- Graphics/Menu.png | Bin 496669 -> 0 bytes Graphics/Menu1.png | Bin 0 -> 73857 bytes Graphics/Menu2.png | Bin 0 -> 60809 bytes Graphics/Menu3.png | Bin 0 -> 61659 bytes Graphics/Menu4.png | Bin 0 -> 47581 bytes Graphics/Menu5.png | Bin 0 -> 6491 bytes Main/Include/game.h | 2 +- Main/Include/igraph.h | 5 +- Main/Source/char.cpp | 2 +- Main/Source/game.cpp | 12 ++-- Main/Source/igraph.cpp | 15 ++++- 14 files changed, 54 insertions(+), 42 deletions(-) delete mode 100644 Graphics/Menu.png create mode 100644 Graphics/Menu1.png create mode 100644 Graphics/Menu2.png create mode 100644 Graphics/Menu3.png create mode 100644 Graphics/Menu4.png create mode 100644 Graphics/Menu5.png diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml index 9039e529d..7eef5c2c9 100644 --- a/.devsPrefs/AquariusPower/nbproject/configurations.xml +++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml @@ -173,6 +173,7 @@ CURSEDDEVELOPER DBGMSG FELIST_WAITKEYUP + USE_SDL WIZARD diff --git a/FeLib/Include/feio.h b/FeLib/Include/feio.h index 76f6b63dd..119f63290 100644 --- a/FeLib/Include/feio.h +++ b/FeLib/Include/feio.h @@ -38,7 +38,7 @@ class iosystem static long ScrollBarQuestion(cfestring&, v2, long, long, long, long, long, col16, col16, col16, int, int, truth, void (*)(long) = 0); - static int Menu(cbitmap*, v2, cfestring&, + static int Menu(std::vector vBackGround, v2, cfestring&, cfestring&, col16, cfestring& = CONST_S(""), cfestring& = CONST_S("")); diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp index 0172df9d9..9bbe84798 100644 --- a/FeLib/Source/feio.cpp +++ b/FeLib/Source/feio.cpp @@ -156,7 +156,7 @@ truth iosystem::IsOnMenu(){ If you need to use this function use the comments. Don't try to understand it. It is impossible. */ -int iosystem::Menu(cbitmap* BackGround, v2 Pos, +int iosystem::Menu(std::vector vBackGround, v2 Pos, cfestring& Topic, cfestring& sMS, col16 Color, cfestring& SmallText1, cfestring& SmallText2) @@ -173,38 +173,39 @@ int iosystem::Menu(cbitmap* BackGround, v2 Pos, Buffer.ActivateFastFlag(); int c = 0; - if(BackGround){ - if( (RES.X!=BackGround->GetSize().X) || (RES.Y!=BackGround->GetSize().Y) ){ - blitdata B = DEFAULT_BLITDATA; - B.Bitmap = &Buffer; - - B.Src.X = (RES.X - BackGround->GetSize().X)/2; - if(B.Src.X>0)B.Src.X=0; - if(B.Src.X<0)B.Src.X*=-1; - B.Src.Y = (RES.Y - BackGround->GetSize().Y)/2; - if(B.Src.Y>0)B.Src.Y=0; - if(B.Src.Y<0)B.Src.Y*=-1; - - B.Dest.X = (RES.X - BackGround->GetSize().X)/2; - if(B.Dest.X<0)B.Dest.X=0; - B.Dest.Y = (RES.Y - BackGround->GetSize().Y)/2; - if(B.Dest.Y<0)B.Dest.Y=0; - - B.Border = BackGround->GetSize() - v2(); - - Buffer.ClearToColor(0); - BackGround->NormalBlit(B); - }else{ - BackGround->FastBlit(&Buffer); //vanilla was 800x600 as the background menu image - } - }else - Buffer.ClearToColor(0); - festring sCopyOfMS; festring VeryUnGuruPrintf; while(!bReady) { + cbitmap* BackGround = vBackGround.size()>iSelected?vBackGround[iSelected]:NULL; + if(BackGround){ + if( (RES.X!=BackGround->GetSize().X) || (RES.Y!=BackGround->GetSize().Y) ){ + blitdata B = DEFAULT_BLITDATA; + B.Bitmap = &Buffer; + + B.Src.X = (RES.X - BackGround->GetSize().X)/2; + if(B.Src.X>0)B.Src.X=0; + if(B.Src.X<0)B.Src.X*=-1; + B.Src.Y = (RES.Y - BackGround->GetSize().Y)/2; + if(B.Src.Y>0)B.Src.Y=0; + if(B.Src.Y<0)B.Src.Y*=-1; + + B.Dest.X = (RES.X - BackGround->GetSize().X)/2; + if(B.Dest.X<0)B.Dest.X=0; + B.Dest.Y = (RES.Y - BackGround->GetSize().Y)/2; + if(B.Dest.Y<0)B.Dest.Y=0; + + B.Border = BackGround->GetSize() - v2(); + + Buffer.ClearToColor(0); + BackGround->NormalBlit(B); + }else{ + BackGround->FastBlit(&Buffer); //vanilla was 800x600 as the background menu image + } + }else + Buffer.ClearToColor(0); + clock_t StartTime = clock(); sCopyOfMS = Topic; int i; diff --git a/Graphics/Menu.png b/Graphics/Menu.png deleted file mode 100644 index 264e9ea363b1e156f9412b916118350ca1257844..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 496669 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�L$A89B5&+PGcS*#y-!e(R9djRE{7@@ z-KpXA^hR=QgmdOj7>A+Z{vuFdE_`5_H{oF*LC2hu5K@MMp+-F_A>TOwIl!;nXtHnTgC<-{VLegJ+S2yCL{x2Iv)I)F>Fz=u57i_K^4GK)4z=aA(GN3W2 z?L8;FZAuL#M1@7I>YKfxm#IX#%};5+9X-9O$BT01?DjWq!O*G8VpA>f=ifBk4m6ya zfS2*ZCVbVqk)b?mQmXA&kd@8BZAE%g#H;dGECL;K? z4Jia58&6R&=wJ=5;(*g1Ey8_7nDg9$ELth4L;=DrBAR^viHRQYLe$=naFmj-bHKYJ^-)y?cb z%9c!8kCtc)u3fR#l!i55DR7}fEK4`uWes*rZG{v@$lA)@YQslvFXzC-m?&Jo+l_#} zee?PPwzeUuS{H~BMnC%h7r&2}b&&^ux)V-e!8sCsU<90x(G`Qx6M#0;6;9z%==hsl z9=qi+BGL+|^l(9$wana#>m89&$slb6^ORaKvNB~<>wEfmpHO=m>m5z|ByD=tqfr`u zq5DP~_;{RmMt}NiNolV157>*OYx40F{tJ)wA_+)Br;xJ{WlPv}drGkBVK6$A4+DH6 zq+GyULPt$r^mOrti2L z9$G>yG%$d_qUf=q$3kc~-KMOS$3lH0jNtLjC-Y$)u%yz(hzg(P#XI%d_e{7+ zb{p+RdJMr>~@yT;toY>~*%=2{~ zy{`{(pdx9j^o>C#M-|69IBkJA_UGrBn}E!yz8qvxK6A z0;ma!&??0!%!P+uM?`QNE|}P7KoN8W)6*7@6!RYrOEr(=zkBb%Gy;2ZO2(4piEe`#e z(zT{vn_)$4t0fIH!+aMT?BWWDNjPDMeXW{l zH&Yph04d>9Pcfv~9~Vf724ZfeN-+VxMuH0(UT0eBMC}J33Yq zkxslA{-qxr4LRLVv+T_2_M_M48TRT9S^JPrj$Av)yW?F1tb{}Nt$ey zRd5()neC#1Ut9rUwe({KhNaWBq%?rfz2n6y_A)BjDwdfnZ+g#NJ}~a#1*&Lp5Bb{? zQ%R%5w?g*#wbIO&=rp_6;#D#@8R;LSd`NI9ruwd= zl$02R4y}xTWJ-JgtQC->?~7ci&-KRGmGWwr;lbv@Soz0qtVH}!Q&&!t-^NZ50lkl4 zAy?rNjh0s-cHQcIwYAs8C#i3YNuF4Qn1R7S5|u5~q(PD`HhJ|_l?6Cc9WQ?P;gA0I z^9+AvpCTzyV*Ig3-X6hYD0*W%Ofz-0fdFh=OLMEx6^9{mLh6|!PT&j=B;o&}z6%o& z!+MTJhL2JGc%H5@Aw8uy1(#Y6ATr>6JhxY#vt;x3b}hsQLcm@Gblj<`z+lk)k^Ir4 zg_zJ5iebHUD-6b;9%rP3ASTw#0D3F4SoOkJsK;I-eZ5<7yB1EFs9$=lt5fvU2uq{c z5x#KrT=_qWeDUak!yn1}Kl#+!*UlX}Ja*-OdgIfdQ<%1aAD#K|$6)PuU+yd}LDai@ zkBpa(@4NOgt`$_@ee45i|LkRm|Iz-_a=du?p!~4>_P&nKnfSl6sqemUrh0blCts8w zo;`fHXYErbW$`xTd1^0i>^l3wZCaAZ;kWmdkGHS7g~7uwpE~QdWHlLI=NdV6^t<5~ z7}B9#Tp}u^_?s@Z9u)NUhi|`gGCaFm+Q>E+LqfA5XpCQjsDU1BV615e0?f$( z>jh$%Q?X`gb#7T}bD7amN)ph`Y1^47n}E|Om4G`TVdZq;^5@(mKNoE*o@&YZ8{?Q5 zxdCBz9EKeFWtYRzn_*q(J@pc=0ftPt4BTdqv54Q#FB=Bo$O{S+WWzAj8Hj?LJTWwP z?2SPJy=PBHyg~BFe?Ic*PI+-(X+f+1-Oo zeX8xFp@Z-K`ch;cKTz3wI-lPz@>lOH?UMI)zZGt5KK9*_rcX0Iy1}OI`N8Q=1^$b# zo&N3SeNUV!eIk5yyrp-u{Pn9j%)0#_I{|s6yYr)=@pl(NJw5Zv(RIz{PD@u`zq89b z{PsVLe+dz+c~YW0^P_4{;Q#Ook`$C{e)YAn!obm2@W$RTyQYAk7^uF7={peg?;(~5pHvaktWxuWCJ;NEAUJ~m+1|Ga*&4S6+}TD^Ob z#iI8H3;e{}unw=V(Rd3!K?0@1V)Ib48t8;ldH5BjPoFs2@(wN) z`_bs~mR!wD-1C-=c&APskDgr8!=h6qKKaUMvBmGcESnTGF8Pt8<)CHJ=t!licD!Kh zN}H*hh=*zU0C(5$$^m035nbOk5oMZJm>NIpB%srzO@zJ(F&9HXEEguV!MGa_$;Y0M zL?FJc#J<2=zUf(&5cKtxGOocO76_y#m252qz9O1`#y~*P#&o?$I#} z=kg2Niy2&d9knirPi?gh>^pnf zX*7O3RJ%qU8I04~q%MDy52Z9L?@Zfhn;Awhq)HuQEdZG168O$zkWHn@G(asdOPMUb z7V6@KE{1@x&Qlu0p>Z-6dU50sQ;-ZOm(ztTW%uX2JMr9J$2P1n@xYWC&Fc^-Wuia& zJ>2XD0!3irgOHCHvB?12M)BKf2p9?Ji%yJ4w3lK0uJg*cTnmkgkb%<7R3NFK##t*5 z7g}WaY$#n)@QU5MtzMc!N{yPPuHuL1j=ucriq6)o$k?&7nJk8oV||+L<(gWAmwcZ* z9ag)f|8!d?WDv!h_`FFyIr34ZZNXSq*q_6F7EHv^7-aIhGhujk%zxVm$lt$o=C+PZ zR2_qEIUf2gRUI_iYTyBymw0_|M{j@leSMTvi60J^k6r3zyMm-3C?BlO=JI$ytn8^> zjS|&H@zEHkm#B(_3KOH7oHaNqdbIGEJn~6gz=(cpSrO8&E6RA zP(j@QIM@;SepG&X|AB*=jZhYY@lIhsv*%=+FGu#C$S*s60#hbPM{A2+#gbh2K$m~+ z!~V`TibP9`l;7SvmS1`d10mqfVv-8cZo!$LiT_0h?rl!QM zKz{K2N24DdO$x#c2PPxXAmZm}&)hf`fa~-JcnM+w!N?TYA#m)cgK4-%qT!if+_mEN z?xqRph)|IF1Xq52{KXE>NrOyPId=5ZrD^580~m(~coQS4WNO57&z2k}cHWlnjVBLD zj8=WH`LNE#0a?Tm5<+|@*YdWLCL=41<`+rmU4M69vZD4 zmbS%XQQvr0*Vt1fb`6gnf8|7FY&0oo@%zW|fFB%r3-+7si-~Bx;yJ&iq*Y5{;q#4j zAc#ONFgk?8ERfv|va|(KEHXiEur@zM*(Pf$(GHJOF&~T9weZJ+ydEhy7R1BtKj7_b z#m@w85lDq#$iGuJAny1FGprt}&kjBf`t&VAsB9coA0pYmp$ow8)h-n9kxEtPw!2s#544ZfeFl08rcdpQ%bU*Lp7R zovs}AA$;i)`%gq|3o>3yTM!o{)iT<{e6_SvSq3Dv<29Byxj{Be(oUgMRALk+)gnX#I@!iGA&r>;lcZfV-$f7*63dvxXWzVa2}wFmyjS1s z!9<<&wyN6&rdIF<_pItKXR-df!~zG}D0| zyt&BaOz-$%43PhKLY>X$1$1+)IV%*{nDqTyKbS=sh z`#Tm^7cFR!0%qs|<`UMlEPk&u+rOmAOiKDcXzT1R|9h84naWs)9nJL* zFYNEeCm$CsmBn4Trv4?(5HCAjftzW~aCToD%Ssl{kt(qh%Ug?z)wWzxkYtQ^42Sj`tal> zTv+S{2}tsAlUKs=KT|C%o(WspUd5~83%E()9R3W+8<*sxO17N2^LyHp)k_Gvqb(s` zDpY3AR>t%FyCyGJW22~F=lj8}!NGkR19GpziA9`i96mtO^ns)lM*Y-3CHZm-F&AL@HI{qF%4RxG2ml zJCYRCJ&s$F7E4oS5OlZVA%od&q@yj+*KkQO9$+Z}aJQ4Xv_{+9KnrnZHA@vm%o7$; z+g1HcRyBgCS|OE zKHbNc^q=X{83@1&{bP$dyGL5vW$C+%qW0WkjQ9GNN^4iRU{PyZw!2MotzFWCeTYTU zm+LmEiv}v)?TdW~Vy?4G7UiY52#dIBnzdr|FG8zUFCba>ShX> z#RU%P!%Gs9YeDSn98vr0(U5m%2%(OfpGgEcPrM|ITiO;aSkh{H;O-AU>RQ~A3y`(FvRL?^%pct_hNuAM)!|SU6!8e;1Ema&?s>6@JCDXUHyWR zS{7wm*zvoQ=1lF+b>mRXyG_-s$3riv65G1G&eo;~`Yl|-6bNDPsuZXF ziRtpWK`7vzusJWy9sbhR1z|`w0>9UoTyr7cYI}JMewr4yF31GvOk4UQboTi!cKI!f z%Pp<>z;DAq3>u^Z42*q8m)csWb~iP7c@&65=&cs$5%?G6(J6G;B?oCOe$?`b!dc2~ zwh%Ny*7__JS)JA{712+WalLmRCMT&9o0ml0%`FjFH&u{yWw4#IWKoWrI-T}mYo2Rj zz!m43h2eNkPRdWn0tta3(G52n41AGP&Kh9OS^1utVSZyHH92rvBNf16I7u@boCeRu zw`VONNfqp4CK0g@Ykbxi7!5eD_o?M*B_4RksdtlNNs8l!LxaO$7)%qq0sm(RYbIDq zkDKs?bipg_s(o~Gc09LYx1z9?rIit=@C1%U_Ek^5XxqiVp6^uQ!|A-J!4|2G^`{ZR zudxN+>;)Er5JRoD?v}RZ=4hNU;~3WET5@jjSPNH5X%;Rnw>7t%%b~l@bxAuq?M2Lx zZ(FvYC7TZrEjb-OwQ9UcZCMgxvf{Ns%9<8o;3EqcaWA2@f`j_iC924fuL;G-;&FWF zM;3RtEMUja60K>57}Dfh&w0>6=@Q#Ml2%ACHBVBIEY0!(m*xbeF3ZrKopF7f$;IZv z1sbjyQiSEMI-^~K>a5;M2Qrai_)|#&C>tEmDkBKdC~o65SghZ+ZXCMEo(m9=8P}ww zkkp}g$Y6atP6}2tT#fSjb~DM+k!oO2URc>rI9$G4^l{2c391B%yj46H;YX#{vbY$r zie+A07X*e#H3P93gN(Ssx7U|9U)ScsJ1Ez8cw<=EfqXdA<2-OvSj^-^(-!+eruVv= z&w_VyEq*ij7aQf^PEAi)xwd>fg3q^>0T_7D6iW-r!I*htzW-sEYsm&4$7V|wQwe-R z_X3Si zl?ct$oY&-3L4u>uC~skg&x4&po86(C%rz~76!n-kO~Iw;=mf>g(+NY?S5rT!ck!cG z8l>!0(I=QTiRUZ|67Q2(88rE7)QdxPk=$7dNK73|pSTu$4kju`#|p$HlZ=L*{uW+6 zN>~U?08%hZnyA&+JKMRl5tTbOc+Vl5O^m!v{Wx(LdgI^5>g^p8aFiB#_{d<|#H3xSXFYkvoxJ!dOwb3sA<88Trl-nuMG=EACQe=VQ>hRoybr#sQJXH zY)gL0QDwJooyD}W%a_(Lo6mIX9ybtR5*^z}%{PZF*~K4F@*o4izw}+oO+JHj$RW;a z8Ix!JWha zWjzNVOy!`bv*5s658(A&Yb(4PM?l2JEv*@6)26PZnYptQkcskPV`4c+ z*bK1X!TCT`!a~r4hZ?{G`r|o1Vkp(rRqVQGMI{n_Sl|vh&Lb`S<(04RQHfIvH){Df zip5iYPh5vXy_n2Pb{{g-5z#()ur9)S7d{Z86~O#_JcqsY>`0{+Ttl`Ko-hTm{KL*w zu`KYopsH-+QLt|Pl< z+RIeVLO1y794L1 z6HVNlpeq#u!kRe^8RJ1>?AM71PiN_Q#uz-tmjEISE5oX9Fl311O%~2S`D3@2N}7TR z;$r6PnVo=~x0v{G>mXfx245}k(b#^7_m?tZVdHZEjGvS8fv??^w&=5af%JEJ0N~sn ze|_A+@TyNie7vFC^W|(L+2P%(?3&w<4I`?}dA#lUxK--G@(?E!d5-uP1&aHU3SDp2 z_gGGY;D|jzm%Ru6HYUxMCMK6+lzg&>*rXZ+OGSWMf{~9iCMIp?L({_pqD8oJ+oH0h z8Ul+`*07kYDLo4kq5F3!bdzc=ImvLU#HB|Z`cy$iX7Jf97$<}D&&W6;)@lRj`ALnL zs-I1#IksRN{7IV-V;k2i%{G6MAh;kMZxa!m&B%;uQeGEzaokx6Na}guMV)Mf>0VJ~ zj2ta>kfPg!aTcaZfSqtY6gnRtuk`ulm3tCi9UL+*1k-aG#M_385V1#sa!Hn5w?={P8EQdwmKR;# z`}Z;d;4A$~ZurG&rXMH&>R_i2c7JHoQ+#G(lHt_!7{IdRO%WYKq|v2h zqGs)&CO*zt?@5=G###?IP1ta9ny~zCmy?hwGCFW~+M+OmKRX^yu~urX0#Q+Yg4VC{ zu30igS}YQ2nWD%!MbAP&+#_k#OksBFp((T|9!f3*+>=Ymz`~9~Tx*D@$6q$mD|%bM z_OF$L6=W#h(0Bu4v>b=o%UP_+_HBcg$k@!LJ#?*)>-u0{g z>}P+;&wOOW!@D0Vz7|BXiLa61?YAmh21pJ$B`2p zzLJ5sVjWkn~LQSf3p;BSV~|9vg}y4RDGM6z15e6xv0r*Vgtb`zYY*OAh?gBkC z7LcTD)Xo+PA8V=A<;I579!w`~;L>Zr)!sX9_Fs8F7OpA4`P|NpfKbDU2w%5_kl-Jm zTl)*(;|t*86*l{Q6}$`{Mu(mzQtYFw^KcoK5kZ?D4#Hk$eJ=Dzo`=`ebA7h#9sdp! z7s)9-1y8ud)U*GLDUzU=m%cO&L2E+;+v=7e@m$0}e zJ&vu8s}_Q6>q#hY0UM5^0BB?ygAvB394*5gR$Q16D-i}_6N?98khKLXQ#gMb&*c@H zZ4!giiirUkb$3GGY`AGm=8lnHT+a*zBv~b5@$AK+NKmKLxXLh{){_tADqLa)81n== zz(P(RXA-$^k3A7ykwpNi5A27Si8?5u7{cdn-*Wl4QQkUsGCYPt&c3&Y)eiFqpEHGm z{f+X;_4nb0)hEV@4+e6#9?tVnQA4s361IM*PB0YAT6Wo;6|d=;Qo@r4pVxl0j`mf_ z+0+_x)Ni`hB%0y3VG!jKKMx0Q0OOBbV(FdukeeJuAyIgaSJ+r+OV^Xxc!jvQ?N!4H zz(2iVJ)%(M13sQDORZYr`I3>Msj;m$9HyY=D?tmKl)|flD46$TRs8YHtvB&HEKWX* z7D0*fz4-Xfw?+yq7o)-Y%M0`)$oN`Gt(fpOgeUIq$8`E{=>o; zf+!^GUP2BANHi=k_0x;tCdOBIZNc+`APmNGTMix^nWp=Qros-tLc9S(9uLGON*ZnFLq z>_596!xcYIT}Ldour(kn7{BGX3)c^_oOCD{Er}MQEk@XC1`QjAK5Za)$U3I6%#_9q z0-!|n#iU4mjM$xenf~Gwf$%!IdG?M``abPbUXKAz@ z5feg9N-R=dmpW&6Mgo#xPsxW957yn9)bk^j2_>dbmGbsoS=k4U#l|2$%=x+uj(>Lr zj+J2X7Ngm`zD8)o135OX>?LdS9Y6{~$iui8nMFUN7U`n`qHY+Bo)KIp*Lz6%rANOg z{rDKC`4BNG5Rw@8aH;Wn@CCTgf2nMVZeknuY$%XwXp=3DupZm|at2(ionnj~eC-ku zM{xHmNnerNp`ay*2!AHoId1N*^I#((lpLxMHnRB<*tWHzx@gu+1jG>nt`1N*`+LtF4YbaRMtz}pMtdUf@N>~$U3VZl{$5GN!< zheA@2!P`yoTF$FW5ef^R+3=#=;e{*k@&`89W#7IK`6(O5Ad2L>xxB~QhMTm221=Q- z6;qM{2Msq4YGgVk1=R4`#wC+GHULo!1F$pXu%L)BmZ87WE9MSO)+G8$BK?TM69V`#k1kw$ikgV(tpT2}AC7B!XpfpPa z0%H=gGVDgxHnHKv6aeiKgMiubO`;MXR$#(m6Rah!@(&UeRU{nZ_V;Q#n0dL=6|s<* zRx}VvA~BfObLi`W34p1^h1fF?kQ92eW>7;a*L4ueB-qC{PiZm_TxyPQ<3XSp#&=;q z!aU(#zfXM#MVSGA-)0aDss$5{u>906#rTBCKjW2Fh&-SQ3?Dy05q$3!(|lYk3#22% z)i)>}v7vZCK0d`EtIvZz5D17|OxYOFRD;3#F>MUe@YGTO#Ag%ZXBTN0iWLaG9ll>U zZduqiG(i;3xb&9r*WZVc09rjj3TT_iIj0U<-uz=1b%i&rMrY0m`4lA>fXA=d!jmBRDz$Gg>c=?4W(>TBV0eh2|$ zH;}0x-!s>HdjWaq+ZG|<{_q~q5z7K^uzVsy4leV7Zc(Xvz^gu|6(b%k1loq;k-x1@ zANVVEOC3-|Vt-l>1k6i447tbE6h&%$^V)W;L9!WxnC7$ax|G-p+DnI3A*QhiVyUXo zv`YA4+loU9gEPjAR9gsPq(^aJk_i`|WXz57;jCJphx!w~>`hZr-Bzh;tY zN0m_N5X0<*ueoB;|g^5g>9l{wAoQtqvCj%-f+_tP@3ypl)kil;6iO~ zjWgO2)MnH?#$Qr^Gx(l`h9i!JVJrml!Dj*hDD`5P0R}K)#2m2>30$w{%1Vk%e2RLG z*7*g**4foJUb$er8(wMQ3TU89rZy&kYoWA%09Rw=b7-H9#d9igAQXGEx9H2pNQZRb zJrSJSRSe4dkz_ilNW;@SN=5xm&B4(V#${puJ^P+<#6kR~>K-hD>D0r})6~S+)Ka+> zi-yfyMoUj8!*B4`MK*%96kum%`)!f-=#*_x>wvFEifn4llyGU1EK))m!^1^f#i8wh z?{Hau(3GSHLz_l@edrT0Y%r_A3Ch|MYYg`_4=}Uhrt&6(7gZi#wH&wib0CGU;Nk|83eFWGi)t+o$EXPI zqd1JvAVCZ~=G_|Ly~9j2;63BC0MM;JzG^vk616UL>I`stg+kxRvCa`)h0(gy!8C%C zP{6bl609coYH6^cF%za)CS+JzGbo(z9Kx&;wlMHi)Rik<-;H&o(l$+;~duCVsU zes^5Tm-pdP1g?Qt(s&zk>k2AM2B#^>R42BTh=mRhgSs#(X)UdKk3kxq2^27`>7vPy zU}fzp&-&T6QI51Nh;i@*#bZk8nv5n;LT>uB_v8A#w$pVluo8scugGkP9--&oECL>O_K*m|D zVb$;Z#!F8DU_<^E=pHQ`%K<0@TOe9M+Hxdgq1cPZdH_=GfR-DxYBbx#yf$^-J0zJ| z?^jZsUYK2YDsFQog2+BD5rCI1?F>_3O0*OTZc+O;Fht}R`tfN=Weoxl!eouORbu0Z zk0BB$iPfqQNNGIU(~>o_D>YTTVx5CEV*ycCLcPrhi%-aI14Bn_+7*s7?Bv7v8CKs4 z+ovO}@gcGZY3`@ZsT~ z#`kd%YTts-fvn&Wc&-AdzV&g#!vUORF+L+GGXs}Pw&(BrKu zD0U=y!=)aeGyh0a+`2k%u5hq1iDOGA=$y^80#d^}D1r}GJHo;)UFeS+`7lpU7-Q7u zuhRhx9^5N){ix31*%JwY7mtr(uu<|X0Ivs~ZZGe`cmMdL<9SLJDG0EVsoK+(Ex6as zmtM#X4epC04mo(B@@yo#e_yY0v}8&>OpR1{@liU|CA~e2nwYv7-1FIv8#VT%sWDP$ z#~_^>ao$zE`&3hE?(J7~36h01$w3A>Pe6n5teh zn$;EL5=}G?J~d-Pgij|he8FZ~0ZGcOEk~b^Bdp6%RloWc!zBgL6cRxB6z4~lqW}zi zfT{B4e%RWj)3T7rW&8;*;;IjR(P=nm40TW078>@BFo}<54=U^Q8 zj32J~GKb?tlST2-!q1D$X~Ru-WUyr@nPD8qGGf8h+@;`Pyf6sZkQzOo zMOiC9@eEV_P0v6G=p#-jAI|OJvUc9s8Qf})ulVTxJz;3v97HpjsD?cm1<>BMscq`X zLeR-YD>k88cpDmcY;Bj;kBPe9BwvBXKSV|h9Xy7Vx=18_>U&o8!X zo%yot0lu~54C!bMIT|Jv!}o3xvuR6)H_b&jUgF=Ri{Kn!W$r~Bh??CPJX2Ouc+p?% z=!1fW03e><5P9NR0HT?2zV~_nBgGI6m%ZYA@TuYOkUosZLtie9U^t6qXv;v_tKoTV z#qAY)lpOJBdqj!_T)~9aVrL{cgm^Xwc0UNHQi^bz+gWh&-B3R|!KIOXmte;b%@Y=i z{U@<$fa{?`49?LWY!XTm`Z$OnQL{DCyf~AE!MhU&rrVNY&lF)BJyYj=0x~5YUD(4p zbJUef-gpW%#zEvt9@C_%t>SxxhYpxj%d3X{3@Da0`4)Kb6@9C0{r)PirGa9pvCW`fH-5@mX3PCA#!bi zF~W;v^6;tKGk8^q!vMd74T7D)if*2>?X(fRu>*wUM22y+>>vv&B)N;rq{KEn7ij7j z;(XYYC<)MMt-X2CgRy3*3z6RSeLN(CC2BDFfhQ>+%9ZwE6$9_Zq9?H!!et~9ZQPjQ zh^YGAKg7HIn?X_i4`oG2`OuZL%nlM7lL_&j`sa@w96vc$E|Y5bh@_SUdqBy^5W6;i z`0+jC*otJKEYiwHvdNX6>uH?;)G(lcax^IsduqtS`{CXhiSU(P0^q+S!tr!RAG26v za-$s_i@@uZ!6OJGuKK-Uj?_5@R?=hEim?J)ie>6gXSxUgyNS__I3^iQX6{Nz5}BeV z_PB^yxwFt9$r31v7x{*d$1K^PWl1p9lW3U$0*tlKPteekO9jc4HzuDnh%V1b4AP%@#VjR*X67%2llTlk_=!=4KNp!t zklOQP9YQ=$F>=}a3;7~Z62k_SSb`aQVSd^TS(sApAMcfQu_Ra8RRcEQ@J3ug$@lmOhB@hTzo0vP8j;0#?HTm8mc%DeqIi5dh_JKHf=s4Or5Bop8IOA^i- zo&R!>RQ5ljlV>8H%b0OXp_LW3FJq*yPUU z=SZDJukpm@L0NFHya1P)Q&1!|7U%;wS86VUiVJ?WtO;I-mgo zc zvk(`@$KmHbyq$srtf?eFz6uvfxIbb9;~Eh5u!K|yC>=dHz#~un&N)%Wa2Ts~)U5ic49vl%{;g(TpWrX*gpYxJdoam47N4jv2Im4ab z2tlEPvS@->u9F4Qxy2BS)FD4y51|-|CKCo7w;$Acoh@X8S)29 zpU{s5b8%X8C@&wxF9repH%Ui{h9}5HPvDxus5<=vgu)BMdWL;VXd%9X7W(iBUyFpD zaxf0G5S|uplpP zj~cU_j*)`<5TA4Ip1~;@8V_O*$5g|_O>8Iv^V2axF0vFHhAR>DJKiZ}L6I&UzLSNDo9iKf$&#Ux%<{K!SKkWKorG2!fS%ojQxsS^OaA3a>rH+x zj8||BlDDE_cmrosLp=uHdCz!tu^Ki1@t6kTJ-HDYr)fcg_q;jqaF&joQsRm6DNRQP zc%KYHAi z3AFkykR6}OgJ!APz=l**Zv#8D3m`EmB`g|a9_5-=$0VmS#*+fSM`tk6;E+z1`htc~ zMY!J^eGOKoQiN(V-iAwbeF}p2V8@e2rm@(`OwVGyoCa?wT8+H4o3p58G>`Jo{KbM- zun@%=>Ib$Lc4~I!RRUc=`Y7HG3-Xm@;(v_gQxM@Ky1@7<$E5*>QxB3;l6wb8qh`)s z^%>ASFq_hZ`N(*}xLbODAZlBzMoe=#H7}}4h{8G#T8D!V}!{5Oetug3$uA8^Th)faXh`p(4s`%w z4Ff=@qWU7(z5x*rf@_md-j40O#rDG~1G5pFM3h`f47TbyU(V@*%ZGBn$r&F(5OhgQ zWVG2x2PS3a7Kh6|h!UH0O^C+pym{X>79a+$*P|S}S_R3)%9hKfLty%VG>!Rip1FpB07lj| zhf*brMCLQ96}6)f9*CSyKDPr`L%k`}ts%hdMe4oFGleZs66$U$QQU<4Aqgr8qu|^p9!YQo!}Ed}O>_puH6WUFQxcI@#ogE@aGv_^(&N&hAQgzM z*V82v%c5sO0v8VN3QNvzdK8ctkv^F3$kq~wqpET@p%`d*r{0E%RGyf^cAVCU4tAQr zD{o6%9JGi;X{Q5CE+%>(4-)Sh5Cp0j*omEQXdJ0aL=vM8zJiB@Av)?Ehoq>b?Kt2? zJ1Y+cuoOZAqL>cxLsW0$oNvUBUstDG(;^0JIuh)l2i0L=n>^5? z9XJ+3D`26k)G1BDo9!3EP`v_m@~QgNBm($WEm-`;3-W`Lma{fZ_exYtH^as zq9TZy*x_|VllW2v!%B*Um(%v+I;%KpTI)x@@5I}J>2K{N_DE2FbK0Rphh64jFE z8923SA%fBcy>!oRe7_4W`Q2j-!M{s5RtJ30aY??b0daWwL^r!TUydC9@@o%g{18OE z>C9}@sZUZuS&1*29W}a-^FAZ$!Am*bVg}xe3F2}zyJsiH4K3VloHH1JM8nlZ|1m14 zks!&NX45GQ{gMp{xne%A5QjI+5+K0bli2;6btnjka@^C&7M5yj8sA$0l=3r4|NwUG|f`=r;hEX2VA&uQUd=c$O z3v7i35DWkElNdRYk~LXiVgphz4z&Qg+dSV%zrEHgk)b(5FMHE!!c;H42ywX6_*8SW z%Cf*=q1d;I&M=G=ZJIuXPj`YzD^I$*R6&S`VEYMtxpYzKU3RB6f+WK1*C!Q0kYMSr zpe7-Wf%P|F2C%lcDv?DS0olG(?^S9_^Q4J@aBh;~9of!W~!-D7)a{)IjII!eY6sFLM zNGKEI|5#zmpCG|GWc7_L~*il}}dK0oE(o&R|jU9EI6eI9E<2xb()A}*Sx^JqLeQIevBe26;Wj|0f}vVz91 zlNThk*$j@ogs7s>eEZ=Bq`4wa<}pZ|oC0UsQqApw3&F%lu^1dUhO(IYpPmsoVJteV z7?=e)?JQt1X4Dzrw3wL51Hg+OBbXFTQ)ttx;qGz8`vvNN1(>3*ZTBq{$QOQMvX=yi za86B2V0v&C10?WeOfh{7`kD#~HvP=`#3b&$oqv~AL^|;DE8qC)Qvn6R_UW!1V%Nf= zS^V=kG!O&H;BN<9agb?9^CUKcV)`6kTrqRB`16_3JZBNzN+Htm7!p?SWZ0X?MfQoI zga2|WEP3n?F(;6FyZNM%=X>*5rJAH(v@yS#PCO~147EblmHVOqxM}^R=Ov!>z;G@+ zP;{%fK)r>}tLQ{v8leY|E=QdYO|B`LLSl;U9=rhnFl|CKg#O^_+tDR{tH;aB8QeOV zML~+ahM3bc{5;(5&?ZDPLJq#D^1cUf8 z!evo09cju~@k$VYk{PAI39THZjXQ%}tVANbKtRX_>p{kBj8MU}v-6xlsxOpG0{hcC&Jhss^tnuX~9NcIgL=^ z5>W@Y36AWfeyEB-tn#U=heJ@7DH=TlN;nAy2R(h6WrDG4Eabf_`vDTWp!9GhzX!Lk;qix)KML!6mH~QBKCiha{jBC0J2}FEi4CLkjsBUygT(bZy1DJ_W&o4kb+2 z)bT+50b74D9OC+*!|`?ncScG6(9(vgqY3>09@>&7!|Y?Y@uTyBSMu3Q++sy!JNexg z^z9$P@)D>8gSlSWj$jY-ps@jbT==9;^#ViFQ`9+@O)?@QM2*kPA$^tvjIa3wTQ-x4 ztF(G@04#%kutwt@W1=7Qh<-=}@rWhfcj<%%V8RAhvHljznh$tg9E=^a6x0}Wc=r_FPVZ`S)@@P!yAWO^#FpL>}fZv?vdRf?bLbzCTY|Qkp zAd)3b#LZ>{(xoBHze(S^gq3WZT<^O)DZiPFHENT}AuojC`3oZuckGUL?*NU*G%f`h z3wff6IA{uf7v>)lusN-qV2M*Sf-cpMz!X<4H3^^_UtXulG!N!O&xl2v>_QPR^tt_l zV$#=+uty~@lOA}IVJMETr??V_n^cM>?Q|ea_oK9G-@qYr+RGT(k6BDfSu7+T)d%Gx z2yH|_;tKPrX=Kj;D5kNBukxK$0(%7ERgp)VY+2O6p)dI)2n{D)%+klZu{dsr(s?JF z08LEzjg0Uh!?+sZ*Y86uKlh>A--vkW$K#Mz>4aj#?$E>wjmI9NAI!{*Z5ED)ItZxQp-ajro1Yo;0Jgh- z1q*1WxlGt)qCJqR+Vt`9S5V`MET9pojD7^PP_%Vy1552amq-{zOKF5cK~SKFQn~^l zFgWPx!>p|!d5XxMRt6X)gE)hRzNC$R91?x1vQn7#{2;-jBvYUW(~UO7^?K8}7XV=x~9 zIjxLvnQ$lcQZfgtF+ReD;4fLMPC?Lsa5yJM3FO@B-0ZgM2P$!?ecghDs7u!22agtf z<;*HQHUH(lEulkYcaGOz{O=#am+B}}yYS{=5%V)oqlbW!PU~Nuhk{fe1^?6ceSOFP z;XI__zODFB@bvu;p@k5zl=TpdvrBlY^oXyg8`Y{BdN z@;|weAIJzhj{gjD4eAhcXCuCQLCFXDweQbKHej7K_YGkzZtdC5z~mJzNX-**Tf1st zC^o^#h!Jq!%Z;g$OVk+c8WtK5QVp?vt_j-YMhXV{!K$Z{*AJAu6q_J4$tBedb$wKo zGn>FjaD8-q3`@FL&%kX7I3E^xwLJe)rmtI3~fGtyS@)IZqhY(-Of|{?}=t6%k8U>oa*u+ibC^d6- zCnk|BPM{3ANzLq>+=NaLZZ!C89$(jxxBedvOlBN6qqtOrtMPm{(8yE_xEP72!W2e>D8RWDsgn+^LQzHeXp6bH#V1fX$mnfGQ; zjbGZr0Tz#p5@pvz0HFe+o1xwor}zjxX6n!oG8M%fxo%D^H8Y2W(I}<2ai1i63v#e{ z1;bIE!3#O^1t|1L>hR+u)f7AF<%>N~BEr=}h)VK;o!V`1MOO+@(KW!R_F7FJEr`5O zJ{?pi>&hc#Cr5^PnGsiGtSZ&PO|8&K-XWlaiEw zQXoS?CXK%9m<)j34aLD|GyPdQ@j{h~!u4X0?I}SdUhYsN&{g0Votc^b;Iyowu=M0_ z;yxzvMWJxDf1u$c7Rod@vLse^^`RhLk;yNb#CNimX$bQ)IF@v5H31t7THX33%>4Yk zZYaodsw52*#GGqzHlh$q(t`!w*|)(Y>IdJ~n)7Y;n-W}(Ywe8e<|3Nl+w@~<2q^=B zElvCK#}cYXDiUiL+-7bC?5*IVW3wA_8Wj)wuc*7-%uH^0PX6K-hUlJOsO!>XQrdTb zNCm@lsKbxYGopyYpl7@XAGhJJ@-O~t_GIj|elUe13FQyxiM{{T5DQ8EKnY(X{`}Yj zq(MY2z+dNpr}MG+bA2p~LNuPFM~jA7eVcx|Sa>5b{)sLYQwitm@oWnXa~Kfd=I$!C z7)XX$jpXt}k7JRe>mG{p|fPimVOgX*~szT(PR7*13X`m#)qRXE=EW}z9w-C zfHl07+G98bhgO6Z#Ib+Jsrd7;l&Zo{jxc!tf*k}@#=bk%- zaoq|-+<$70y-PQ0xMbpCKIJcv#V5E*3ukF}fXKKwUDy$jF?zsVp5CT~G)jLj)HR zBv03f2%!lyZ$Q`vk7yqinAyTd>Y6>xBOvQH+54|EE+jbHwaB<+g8bk{KIax|m85nr zIpcUJ@0F$EGwq&uoG^QTe^P1~R5U&~ivUtMH|3?@djSh5!KCKMkDyW5RuV;%`Tg^GSjeCkJN_yWb!k>dcE3Fg7heIb>fzf#nui-EEu_i3;P@hC?v zD13tO&#whvhLZTQ5$H*7ozPNvo6#}?5-cEC7@N3alchf=QC_fcn;}B29T?Nn_p$i& z`RR7&aaMKrr6BT785#8=W~L#oPS%hZ0VxLD$lOlRXpLHsdCxWpa&NKMd=;O}aJ-F< zsVvB5X^Km@Gx73oY4gIw_<#5{;97j8vpDjUWC}1+#J}Bx>#BGgL(=*r7GPfUI&4!| zibBTQ*%JYq)41}$%d0EZuPdD2*U!RAmSa=6gpnQwJ2!kl*Ru#O`npiN zy!{N)vnVbDSf-%C;9Jil#UmU-=}>s32lwVWZ$<(xFbQu1!$6l=KJ4#fu@$p^cwaj+ zYbF|kRJ1q~a_{r+c%a}O={&NF9jU6RAGmUnCp!vnv95IhTN`+RXA4&;Qf{<{` zlcTHHoyCm(K9x=W!>y63f3v&kgL7>C{_ZV*&#HvV4ecsNVK-r{53kEL_Sx0rl&j7O( zknL&m8TQW!4y-15gr^0w+Z9f($c%{RoD`l(|6t!!b+Wos`8!r_Qf##BAIhernV%mQ z*j^uvg)S*TLDFwx$-Q7^en4z|1@sU^4BA0SfO=fQz9p71e6B~sGDi8bh=p9B6C4G) zy*%q{LAofQMTL;T=*#F^!H&r_9ZfN(-ZWxNP>G~h>-F8|a6rDWmh2@z1}EfIFqk;~ zf3seM4!F38|{zJu7bl3p_ z`DSZVn$dDIW?Hik$-WFfo4b##gj9r?Wm zb~04V{(~%QEeznaMiA|_gNoCB8^#LR@^0-p{xATVVELN2Usqze;77Cf(~ZXbkcBBsb5SPBLY zq5$|~QOZq`QrfBggFnHAQj}#g@IU;ylYK zCYai1pXaG5Vjzc$G6|zNfT0$on>VR*|CV?Zq%r!o`&5veN|3bkNnO+@q zOG>s;8QD(mP#XD~ywxD7)J?rHcUq1d#^Emep#!LMtTeamSb<^bk)x?OeAqlYIdk~Q zqsAV2c!Kc<(zM2NxPYMs$nx<4N@eM^Fe!Rsr)k%g&YcFnV#yQEd;{X+=Sby1j`*f~ zh`{IG+RtDC)H0XrsH38tydMgt>CjR$$El>&C#a-wMd8-ZhpVW6&Lp#o4^5#^lKRt! zPs4$xz&3vO-&1~+&lT(|TB^OWbc}^21$QE^A-H#P-xI9&P}NBc&_b7QF5OqzGrCh& zP#D4ujAvXZhB1c;Ilz%?w?NV#F*|%bGgEU z4AN?*(U?9(U*+o&Z{Lbq7ybh|K@@;>Jv}vt4%rm?k=m0JNd;<|*hS1h<&#b)5K1OC zIc;Li6F5w%I{!Y1z{?ScJm8{!csE13eF`gWazKXLbVwbj>~2|%8q#RIXQ@SzFxo)I?nyZ;wy(%`ZXb9GR_EGH=|O!jLaS`TBM z+$;CzXiFjL0U>!T9up|bF)~-}0KS`WE6YLxmY(~LaOozHk{Ai5D>xLsvfZzO1Zh-< z_u_(4;Tvq7$AZI$nHF||3_ivxMP*l5(7C+z^Lf-8WFlH&9FuQx@6f<=1N@t5a}WoMefDn&Vvz{G$=(<|3en#o9z^9!ry!{uy zH_7@cwG+=Dl+s7!q+MkL!7`@E@}HZpXrs4v!hE&C%@YbvUQ3?D0j1dcQm-RXqT>Gz ztS_Pv6{Q(VL}v*M7nC^($OrZB?pHy&DF!V-!16v;+!Ves5)T7&ImBDF&+-71bhlSRRtp7ga0Kz26{hdszy zKeFC6Ag=xiG4hT;Dme0?o6C&?>Sa6_Uk(W82O6_PBRI86M>G3}v zHsP}lj+LNV`{=%opLED?ty%IZE}gRYLXVkj%&qR9cYw;up$XXcH! zI9}TAj_B|1dGK$Kg?bl#>ONFj4DJk;*Hp?A1FkTY1^YMVH=AC z_l?xjNVX<|Z9aWh+hY@2DA8k+12}r* zA|g7b0z+IRwKxFTHOb?){X>?rSv2+EBWdZ3&033%5o`kF2a8eA&DslV-~hD-fz-L< zP@d%5GmA@TL|ABYKkJNa(gG4=Y1-m+gI@rz@EB5P2(68nzFQDiFKA{%N;owER|cv# ztQ8v5jGMscCuNzLoyjtZ7n+%|je9As zC{oO!RQ4T5liR?o7wR`q6{f}dfLnD|;;^j1d?u~IgbKO@Z(_?cfkKy+Szt(d5fd)t z9xecvp-APynpEFOm3MTifXYc4XhI;>M_%THN`y7SLyt^ASUwxDjThgPL{b~utAkvs zU^F7DgDPUoZ*!90nX7LQV$&a(XI{}#8-O3T?1|t=A+ji4A4~mOw%oA@GH63KWcpT+ zZt9r~cZc9LV!}|pNJg2zbm8K30S};bW^S?ppiVN7J?Z>>2^0kw8jvg{k6k`j;{Y`wJ%d$QmI9jRksUY< zQI29rn#}`?pMGc_{SZgK+Eg?|D5Yn4eg^iWKmX3p8>roQ8_X;1gPdwXXM3s&A;Cwa zK!qhwaXs^Oa}Rr0G@VN8`yvP;BtT-FjLk@Aa0{iAl~BBn0-F$D8)8}qh9lIR33xeJ zFK7P>vXVw9JZ~eNCi49NUjpw;dv+SFBAE)KuPSeND#2JpT3E)1rZAjHO3rx*L@ai@ zPVn1&EXW@e7_6tO_~64NFIqJ59PXud?35i#qeWcQ4nj_sBwbi!8QpFT1XYoQcw*(V z2M2s{xgd6o()ke-(Bh}$yv$@m_Lk0`j?6&)fH4QAPe~kKhV~aw5EMl6^09*t^BXGc zm$3i5`X;r;y!`~W7nxx?RYO=JseS4pR-OC`mnC1(%a54@5XA+Q&<`Zs{J97ipO-{X*uZUAls5B)9gNJGe@Ji!4VZ^& zruEC8;Jci;5gapvuYf8o)O6>C>nMfvY>eYEF>bJr9{KQ{gxUg619K=W7@Q1^(S zBg|(x*=JsSd<46g5SPMk?HhmXq=NeKV9W(zV7Gn>i+}uiCl+DE51WTKFl14Vb|M)6 zu9B7ru+}!~P!J|2#yRu>ekE??5EKL<^@UMX_GG3_7e2soKLsN%$n0CF1lE)w*TRkM z=g{oZhz-l>i37s7X9#O1X6Qcf|Cw)$>bU?2B_ELy&Kqx;HK?YJ@l!QPAl7sk4vq8QgM=B&O|#SyEHb2 z`s=*1j}rO!`^on-8k%@NIr}V{8}_Ql|C30^zhuuRz1oB=?6>BbfAQbQ$u?9D{5s8d z4}%n3A|v)}+CbL}-%uT2X0rqa-i!QGGPzl(*G`9?I(5&Rhp zAcZzLMAtV=mY;X>4 zYZ3^Dj>+y}tU6&^(3v(v@;rdsHy|?e2fuc3l{nMMqtjm+;XrrKL<;!xL4<+*a|V+k zK`u;s@>J#7!Or$Qr#J{9CjUFL4Pc{0#s{x1p2d10J5P<@{R`;c^DM%SIn0#O>Qm9= zNGlHY7TcPcnIjAfg_GJt2NLv_?AslI_p)5k`#rrt5iwz%nUyg>{h0MA$cprG+p#=O zjDuK^18`^Yj4BN!)*C|TLyl2Jox+8vKqh+i<+*9RxRilMR^AW;(A34e2-%g6w$sMdYNigcW%E>_ZdSc9PaXhNZ)}IHmMxSH`S) zDov_a2lYNfAO^9bss&`F!TbP*E7X}4Mb04w>{T2aQxM&9*o&-+HtdL13|V-liHtGl z{C7qm%(Fmo6I27)M-zgjy+&geDw4w`5{x3F!|UD?Ah=ZaNl9YRKHIK#DszN5BJyx< zAq8R>C<;ybi$M;fe5}X;DdLMeDwT<@iAfd|15fPPolc_K%Q$;*kA4+vCti5#YHLJy z5Siy1=sExiU-CR_qCFGn1@Ndh{5Z&9`L&K=(!SeFN_XJ{DRC|K97Z#yPC5Xa5bst@ z@5{wl!|A&jYoKXxj=VTwQO*hmF!^zYgJ1g1Gz-B+k5GZQg41xKX$5~fjmI@p8i)`Y zggb#J82{uTJIK%zmI}NIPiG@si{M%iM?xN<0g(?0lSsB?=dCLhJX(erZv zK}av9Y3xcQ!-tDSbLwnuabiHG9UW8kJO+3URLu!g=P0fy8JOVr5)>i(o8R~bdJ$6Y zkN_4n8kYIL{nH-$v3Cq#kJhHIG9hNbr2s-c=PC=%Ckv0d(g|@?iO~#uboH%duVawj zgM?-Ut%c5WxRb0UW?jE0Ae*K;zxGjR*_0Lf*ufBoj=38n2weFEGzqA;`ovrP9uE z0Of(%b3cofpN08+6an*jo;f!k7b_!X_xX;F(+M~;F6Dqjr>{28AJADSl%XyW5b5alOI@^82htx`A!o;_JZKv!LKfws(lx+T~h%$2;zVfx?~o*f5h;+RZr=qt)v&05k~_{Spg%-bo9MLc3~hJ>Pgs z8f2M7U>yE96d`tVRu0;?h6lqFr&Gl+MHsD7G?tD);Kse^te>>9fGQHXeXV@k1|UnV3sqkK!9CJI@jpQDC`wB%!6=E|Aa)O^n0q>wzq-$GGff zVpWXHZj2f(=$XZ48pkH~Fg$&OU;qyWz_}%%Y&$$01{dG30q}-+bc>BMSn0q}gxs5b z<5ZjNwKczZ4fF8ifP{)gXADa{B%&z-U$$MV*8zQ?qLlX99LRl;P$6x zX#lDM4-4oZB$|Iw{YCQwd~6ym$vHq+9=oYe!;KabE+}N55A`p)WmTX@L$ZD&Vk>SLZIxXZ(@rm%P)N; z`m%+B7-X698W*mO&;8-w;iZ5E7zAtnhd*Ex)_QotVELWDe*DetfIS`mgD zb3axA*$%^vvwFVxn2OGv3=pPw z27^5{1$DkCjJ^K+ZoPPBS^W zR5hcD;m|NM*|>LdTYY3~;#YR;s3Dt(roXU4j74?A2vwSbp(Izc*Q+3&cp%SCDE2?O zG<2+P@R0{*rGCi_xqLEQM4MR*Zlfv!7!(AWv3RysClf!;HpD9~BY~8)+c>00l2E3% zKOscv;i3JldE9SgFaLvpW4HMLk=;C&v?AJax@>@&81;Hz`3xLbNkQt>t*Kd<{L0A3 zis~8Y`D!0;+soCRk5^)k;=BAFC;dC{aD=$$|>f6tWQDK zH&SP&*<|ER1`t=hg~vTWTDp>9B3rC4vR7sh*a15>O%pc}2i>FgIb6Uwc8^`?#6%X; z)Q2ET0;q?e43U{_^JV1^C~SBng$`O^?1F?_X(+1&0~-}32hczc^h9l9Z+XKneKf*)#!3g8=on`?SHktaxv^gH#U%?Jx zgTq%4n*?CwaYc%M-37ni7DOaWQpJ0S;Y<3-UUp@0?bOIU5aLVNeFy`NiOM=`jpMXf zL+bTryfC=1GfK{Y)vbX`^X@Vu!IcSO?0;M!vNfaJtHXP?(+|j=j4HwK+ZHZ9u%oQj zWb6STer^~t$YBVpE|-25opDPe90;sX$BHe8#jPtB&1kYU`sIY2Ya_+1OiU|J+?P}r zj#P$|bZY!-zj5Dvl{3i%Rtdp;?8trQ?d0vpq|=LzxEcS(VeIJpDvwaaMwCAlB?B2I zuUT+8@_np}_ibHD>e7UuyF7K@e>>E%S20hMd@4=h;o|Em&-8ZKAcLG;yysgw92i-q0brk;UZLQoN?0J(Q36Y zF+#liqhKa4<-Q%#oS1fk2J z-qH>n@CrhCQOvxKkBw}VPO%HEJpRZ~XJGMY`@ht%2C{wo=U;AZ zO#bX=``HhM$fnhFCOMp7SsVinoDgLG>4`M``$sXlM9ZGeh)AH^Snmr#pXT}Vq9f`5 zJINb;J^{kO85tcNjVtw?3uXZgirG>bS)e)~E(%}ha1=+vv-O^fUP0qJQ?LAkNL^cXhQO$}y6u--Rug3-yq>C)oGI67H4YFABwqNkZ zWoIbk-9}+9K*gI#bP1}0G?wVS-eoj8#MTMmdF-PXoI(szKoerOKs`Z+C;<5egiFH& z%Rg*QDfr`E2+9qk9 zW+^?8oI87e?bF)Xf7pG`?*I7sAUat~72B>n^QK9gQ3EHIvMjh8{>8?{3vV{he9(FP zp{LXHXl7iYYZ>{H!gLmNWO%~1YiHWEJ->z#6x?_dDnPB5Zj;KW?c_6j3f1z;v}g;+XWdaYn7>XP|QgwSgeO z1uCH&IYsWyc!t)TXwbtCO#(27EzNSg3rA_pa__wU{1g^i5Ket?c&<^@4qi>Wv_o;g zft3SP%yRY*!7Mgt3~jS(uru4FlV%)yAnbO|lps7kGmHC-u{SAakZWd--%%5QekXyN$4 zfus$csGxTK?Ba~E@fg40MJ3p}IMV7=c4Jog5_iP74g~j7pibm4NYgD4b)aRBYgZ5~ z^Km^#HH5Ea>q)29Ia5d~Q;iI)N+OIP!y)x6<~Tz#c8piyrn7T`?*f#ld+)f?NNlI6 zOm+Mb%EN4lOZ~`7ptmdV6b>$};R_7z_#;>pM4UK3E}H+)6M)p?XH1M5DxDJ*qNXs; z3E0o<-GOg;DxEpZ^s;S$MkIfPnV6yPBTj;SC!N=STzh&jj%)MB_e{>rKE5+uI)Cb* z6J!$3$p&_we6z7|kEzU_K7hl}s&?VREJxocTUd?V@(o_=p0frMI#3nHhV=0P1wrZS zk6s0eYh~+N6$D&x8zn;91(n(%By3!LR2_@pQiMJ7c)9(ZINxL$#^|Mek zK8k0`|V5RLE;>`Rz5PA7Gc{C6Td-~5PuF=4^8m9H#j@|zv`b@LQbb6HiX%X(c zIDe~=EMYpbUBjLY%e&kp5NIf14Agf-7^rgYd30xTmVrS**o9c9f^1+6lFusLu4C_q zA?XnEu(&v6Fj`l`Ji^F`LG94U$-*Lhjt35n$aK>9AqJlG;XnQS5+}iX87vLbd|Kxo zVO>zo8d!WOia&Him3BxWORE>j%?^W*XY0UzqeN=BRpN`mt~+ia%-(>z!r%waQ)$lr zG{$Kf_~H|1zeTpPgws&$yAbS|&YlF_vc%TgqG_F){+>Jqb9s%UZe5R}tn(gaJu0IOmg!N&FJWj_BF2&A1sW zmVeSGDPa_17u}9&SAFm_6Kz2>FS#7$oJwz#+o5FG281pa+`f;M+EEEM=P0dztGD5r`9F3-{^AqcY;^Y2!SvaOMy0xI zU%q(yCpPT6D5aItyiZU=X*C7g`XY@cL;r{S^F$ zT95)$$PUR{wpw@+)!oJ#eHX3#35Or6CoY12 zx|FP`4n#?H`z4IozI6JbwFf!qxZ)m4%F!tw%Q+w|i2LXVb#nc_=u12Lt6R|&G@&0j zp<{WJ0))4wgYD^0md1uq8{-xvHX;~!RK9iyh_y+KLNKP>+D4y<@;!Gn`jw-<{_yzr zu-p8MKEThBSNRFiW*JM+`=QJh^FwIMD!r}BlwvEL({XPJ>HpcFhhmKP9H zSu2XZw4C_c!dx$PM?iXB9Hs(=lETE<50^B~X0e6Md|B6m;J|P{N;kt*kr+t)!Ua_6 z#A?!V#+48rjGJpg(1=vNhe_5S9sZN5O_tEvSseJ=w^Gbxxy>J~Kb97Pr7_TXX5V() zAd{Yacvo!^A3>nXPwndmIaEh!Lf}1)J3H97CAtf%x#pYRna}dupyoMoFO!_f~(FR+(Zi^Ot$9PJs5dJ?;d@g zXY%6THr_(b`uG-X!ow=Y(4<@k#xhFMGw)sfe@=XDVq&z?(Mxtvu8nb7jD&JfMa4Qoeih(wBqK7bpofa`@7~&W@bv?ln+BpiB0}Z{WZmGdli$dk-Xq%|$f% zlf?YSC}w$}a(2Y_0-H%cKWsKjE4JIy8 zU3_*MY(3h!Ff!Es{}2{>5l#s7O*9hYu4<&!S`}lZKwp9IiLo3WY;y57SqDm@atkdc z_t;?#IfD~TZmz4vKp3HkcUGU;KArqvK7-{k^W?FJi(}q#?7PyE14nU;pUXm)K;|k? z+T5PN-eMn%#e)uSeR~xyyYwlKba$vt&oIP*6i5Iq8KjHv5B1Uu;Jx1ORN1wxOwm>F+YUkhW92b$b-^5|qr}ah{#e^*}Kr6;tz&Lh& zLjbrA5)=e0#lTtLAYD?E{H@!`oRM?dpc+U884X4?K;kbOwgjY{ zgFIK08<6C}dpYX{YqQqj>w&yz;DznEyySD$i^}GNd2XXvWg5%ZWrVk|zEvNtE#h)# zlb&I{NYli~m``{Kkq0Bg!Ii?)Ja+(H3rP*;u}2VAlf~c=s?4VSFWt-jMT6oGKwIN|Oc3+07`fEMjfGN&l@= z5S3eU1e{2eH{=PM5z!6zy{3qvo=4@50+2XFWu=}UwbR#as86J-kr29?ns9xR9Dy0G znU2mkBQxB3!nDwwOLhb!bBVXKz-VQ*vRfpayNP^jk0Ma!rz1N!Tid#bVi(gCn?;0w z{^S7*n@vFGcP6o>hjxH{qSkj_!5X;C^=c$32CU)D{FQlr>On3eG8eFs@O-Ot^4Pv( zPnkUpr()=+FGW`_Od5OoB+zm{b+Q4$Cx%>NT#&O@Zk2-Y4fq;r!|7CnOi;O(Zk3Zq zWS$090FBOjX;k;#T|G0!%iEe*9?YO))n( z?z1B;Jw0qewA@Cp-J_CRx**t1Pv=071MeBFzLSVvSJE4!O`45A_7p|kCFHvLjcD{a9oNuAh{MKw-<}m zEs@tUPGHwUTQywLLG=JyJnsT7^s``e*x^H=959Yh6jfx52VB#E0eILOqR{P7!y_9M z2GFVwYpz_Tet;Bvc}xn%J;YsF{cr%?DDaQPK^|)BOkU2PJ$nZCdhTMGx}{pmG5-1R z2FLWm`P5E}=_sn(!INpy_`NUwh$qoN^%$8A3k)~ra*RMgW}pa_ZxCXau)&>hSfWW4 zjO#;Eq##s`i8V4->(vn&5Kma(4p~uFvAx8ug0f1G%Yr_rA$UP}T;ev0L>!%Lb|nRE zK>!veh(jguW-AQc!>Th#KIjS~_}9#y%QL8W3TO8UF66E&1Iq5@kxPh{XW)YrV({r# zJ4C_0+zWH9feCD)jhjB)mpz2QIr*3ZHYPDa2_J(-=~S)#eOy8NM#c^`7?elC<-HUhWOAgY+T>7+*!J1tLiEpn}{RA42U|36~T6oCpJm4-3+?h6Tp1 zHOSud3ee$2EJ>OUc*_b%$ohqJ;`PO!0>Mq=DY8aNDAn~X32}RUl!IR^?5J(8gkxrv zMhBd;+HNOH(>zEsHP6`et#6B{Lzi@RNA!b@gOn31 zPR3$-Y5XluH2uL3Lt~~eBB&8p3^(>~fk>;UAA~!^^*q*n>`xbQ-vA(S{|;P_^X^=( zT<&rqc~W^r4Ky)|6Le{k=<48}FHSkix`xbtfm?1C%u(ZH7`s*4wUXgKc1Q55AMifM zp`6pjEFT#MLJ?%eipAc%kpQA$Z1KtbZrsZzCNv!)-YhfX?9jn@72gTHdFWpc!pCOh z%hz7UY91!CP!=SIf0hsx6Pw$D@c<^V<#P9sTCw_PjuauFmu5d!IgSrOB`$lW2 zxg_(^(tZe-Ri>c(I1YJ@M_w@3KwT?X>w3kKYM)g>UM3}oSYlQtN47c>RjQco5|4|8 zHTe$4@Bno>iF8eVma*OA8!e`wj`@a1cInF?3tt zW<8DXM<0OG8yP{M6r~`cG8}5tK-w>wrSsUzYaS<=AJ0X++3(uW-|biFX8=9Sio4C7Pgc3>OIJToSS0>qq)d^lL&k82)- z_*`g;$ed4F#_HbFB>@b@$;!Ml*1I(0$Ufq+Fs7#BX(&2;9*{Y5DVEL^Ie-QNJdDsBh=c<#--I}C+`Lr3cMLlW@1+jN z0v_T_?5ZA=;z++QTT4&`bBJu0aM#yZ1=k_ibn%TRa9es<^_E<4PDVc~(Ay;YGT^E?ECW3Uq~oL>ajL)LZZ+0U7KL4kr)kUwFQC5006^<*M5Ln@ z9|Y^4wQ1vXTs#3tM?S_ryl)5F89{#Q$iE)opu(q%EiCeB8~jpi_SL7KsG}32!w%r# z><78;nDZ_;WOGEWB$N2Sk8@?6%_N9^I4#O0lqI=}>v}0j5W#-rIiJZKzB0Qzvkp0j z*Nx$ZUEJs*MjMTQ)m6Xb6Aw70V;KiRX;84yig5M=5>5?~AU_OY#JGtnn&oK)0wzAs?0n6N#1^cmTv$m6t_1R5B& zDWZWOaPJ(7on=g7-q6j9ZoOgq0az}X0=h@*@yti*5xjm--y;r3TE`G#ubP;{TNMXe zAaSa3@s*ID0_XCcRf&~>IS!C($~i7D6*%g!#<3ZZ@=TGICy#@gKJ?7wuoVUH(5=FO z&M&*+3`mQSSZK|Hez>pYbTBH!9cDj|>(_#W)RaBJyAFrXynodmQ9TNRa#%Siomogx zJ9^M7LH%+7=Y{4uE72gX(we|CD&h+ieX>WC%$}sq)MVcXZ=kg4K|zn|I4_jqk6;M< zpFEHA60`1N73!Of3?D`k_UUI1{siju^2<6U6*Y=@{?0=0nIz zc%Rk{P!N5&Oi268Ne)flVl4_QzpIZA26ma1<8&dG-XQwo@OpqVa-1zqKr(mAJk)5vy-38pCk`Imcg#G` zsvS!3o+z71JJ@ux_lRfgv-|PoEyfqe4@dheS5G?qpmtD-w9Muj7n`}J3XcjJ|Q;eb;D zlw-aCZJ5c@Cd36^_N~IqbZSnXPISTqpG&33jhWyqab%hYSpxX7mH8k(`AL}qf)g=GU^yO1`IJSM1cOXlggN*x_0M&`ZWUb3l!$G?IWDp-Xx5 zUP_Q*a^lBxJ?4fgh|fqaV|iBl;J%)Y#+vbSX%*?N7*IqjeJgPQs}=k@5j6T_U@tCI9ZKAglb$+XRy%X-8O(z_P z8%BBF1o2XLb{*ervQ0K|&0UP6J?$WMUOKYV4u>hNX)ft2{TVxDCe3gWJRA-0<`%xK zdH7|Tdq|ef5AHm7l(_a(aeEy~PNMN87iNNaP7Z%iu>lPuTF8-VF`Rf*cJEPs z!#;4d9yDD>+sSTJs$pd&k{6N6>zkXqs%C8mT2FH}URy`(K-;Qi-yq{o4a`LJrmAiq z+}{$FC2w7Kt>;OQAxGU#x@D*9Ilo_2&_n9Pq;6Ys7E+C2 zBz=0SwR+YgIi-nFiXBl`WW(!Z`p80iIoU4N3GT8t4Q7D7pX1zn3wumw&?wS9rL{Zj z>v6aI;Zz$NXU6V_Tnl(sb|R;DrjymANz|liy+i}L2~J%H2}jaL&V+{7(vX66W61H9 z++LAj^wZl;P)D(Gy$ubbe6P-*=0=>@fd1bCAT6TfJxG+f4h zJfx8Mos6Ipq}T%*HGvB|THg~PUNQksHy_F5c*-oi3;$=ZQ{F?dKa=P`vYEanCni{p zQ=Fkbr2h+!z0FojH2KTccPZWDb30wJ+tYpiAZwLMr%TcPLgH!9gBvNx$x-YIlP&9N!<9ZjGtt>hlz1OXeUYDI zflr%2xVm_*f&G|d>^3SMt;zMn`Fk(q8A^2fEk-}ge%;MOlj_S$Fk+Yr8c2L4-y|mKxgCTsJik zQo|yhCF}C(Y{drJ&1d^Vf~J1m!ZADf+uR(sWihpP<|1b}(Qy|P z^L({)gd&H*>O@b+!fR)(_Cu+mz6()x`axx%a4c?3joSF>v!jp-uh%+f_Jva0^;5WY zH;x^_mg$L;Zz5k07h80eDga0OO3YPPW8xc9Of0%!ji# z4kf8u*!!*Fxrn9lJuP6quCQ;EzO2IQ3o?N}Ep0;AXaOJZg#hzt56?LBIM&8!ynZ)N z$28x(Ku5TPf9640I19GE=o9igb|&S9shF zHy6;annRvMn+&pUJ?T|vTH$_hJ)JOGI(K|oN6=mlcBQ@r67_&esjeS%N=#X5OjUs> zjUZB@%WKRQteP4#c~Q3eWDf z#`FL>x(oGWe`locC;R;tD8D_7xdWmsXDw9McY24Cx;jxeQYEAwj;rl`(~@mUc4jX$ z_pU9gp<&5Ue)BIIW0}WOc=joHG?MQieu67t)l_!O7akv0dWkLYnpZzpD$%cQJA7Qb z%{x?c?)f*d(b-*^MsKmL}2DUREdHWY=nU_z`T0 z?>W(8RUv(~;%{aD?@m%x_H9$>vI+*3h*yjDxDXdVOzsS`KFQflJlb?pJN+xNM#=O# zjfA?@s$K0KQuXmF^wkUH8~{hULWtx~?Z7`j4-YJ0nU5n=^XQ9@4q{vM$5B_&JmQ1B z(&wXb+7lih;G6~iUt3nWd{tQ;o{mkNX=~DPlk)kbTI#CIfE}UwGk7+-korf8FKJiA znDn8=+==_9`^_`B^HtookVIMQu?!ciz%&2w=lsvvWF(Jdi}& z*>3w5_%$_2;^Rrw{H9{oOUtVJbYodvRzhlczG@8fN$#LSst9F|BV9Om;1#OLj!48~ zKUkmK@N^LRyNG6)25|I+zS0_A;f%wr zZlco`klR(u|Dky_cJ~K+o&s?aGned!=gFSgw3lUQDcuR)!U4&?OB7BQ*b)zO7oVKR z`c41IM0~tWW@kU+1&EMJ1aIMu5YBUfhRfRX-3Pa%>%Tv@oWMa>bhEeUZ8)? zP$?8hMoH>rZss@JxVO~ZPGsAA)^LIArUGv=^2X!&JTEW2rO9JT{@o*3*tFF z0|gBwPkN!PLviuG_+bZpkr=Gjo)?ltgB&{uiVPu{jxDeweb2YxXV?jEY=TC!)365) z+Sjk*$4|HA`%%X$W+A!JACM-Ap#9beKPP*Zz}QEPa)_&+;3{qC{i)8Fn9o5*GYs7g z_x(6`GoL-Ups)&;)35$=mPA@iX2FcXfo;gUA7IGGe0Wcj;P0UHxsYN$B$`EsNZpKT zMs|d;CmP{wGt$fd`@*a`<*6P2yI-UR>O(AwvG}DI4W_3@FaDM@=@IARpWYq^ik3SB zr*c-;Lk(1R7I!oOddmx)whVqIFDT^UbnkDf*~fW$n} zBlp->M9o$|| zt(|nBx~{lpqW)8(YY0?IPRn$(Hm|TYQiFC+{FZ&)-R)6qM>n5G|4ChfJB=&3U)T8k ztBy(r#7kyvx--wfv)M6t*7tW78rZq~_*Yt?WZm^<*PKt@CW9umfa4c>!-~Ww7(9H| zJycXnNIdOE9+p(AYz&ExfgC53_ibU&K0d3B_p`&i8;(!)pT>L;iNB4Vl>G5z<)#nh zdTW!8Ht!i@;-)8>$B86Bx@YyoulwVjc(1DZKebBJQ~eI#W>2?+wb%|v*YfReq+seu z|H(U;%2OT5yT&BjdcN+4J}c_?3Q~7{+<7db`!S*lKm- zV~qE;CSAQ$WW;)s_!s(4syU_;Ra4k|5UISsRg0|cVLkSBY%&K{oI z#{4+F>j&CkWxn+K=BK$i>0w_d`+OG{_?KDw2e$r1@4JnjI+)JY4^|(-zkP)B4Kzoo zUb~6;t8GqiB(oFES>fG$2kovy6Nzhy#0nNnM=f_lsNAv3cp%vLIq{hcQujsXL^<188c}K=_B!= z`<`E5RWfH1r?3{9B-fxRUv+&Wt4qhKT8yzK4o!@96?fC4xT}uz!&WrGih7gr4aN#f zH-c!W*@4@}x7!$d?ZWz6XKeG~@^^USYmfiDUQf!V4o{+*{-eXTGY{=)j)^0x?6cIY zddo`nV|66bA-fIf6`CF4RJBe1(*bmpqpWT6m14DxKqyi`9B@(@3OsNS6wp@EbCG1>F&vMSgkyk!Mw; zZ?cBwlg#|*%zZy|AHUwgIP+k#wK+-pTVcJfXW4ziU-UDJ_5+&UzxJkAm({|(ZSn4T zR`q-%Pb~4xBy>6x#!qvG=at6Qg(keexrx89yH^b%PWAHlXx0_-y9GZ`6?AeU>_`-C zV(8aF)^E+ge{{W{;dg35Cl>M(6tm2IEiw!7@qA4(J_3azfA9A98Fw=q_9ezX`Cg6i z$R2d1`t*?|(BTGqt)uubhO@tJh6WelmgCU#F)XR{E7}4IR7K@G!G+Z6X+;~T;&oM; zRs+0}o$c6w@$+u#tfivp_QiMY{HB3dJ3G@M{J1yqlnivOTRbLs)t~R?NfzH|SH9Bh z5Ape6S07UB0bN|k-Hwjy>WZC=xB+XajMD&mL&DFQmQ69X#oq zNv;{o;tS76#pLNdZWLGf)&6!}Td@ZJQf=rrv>)%yYJcNN{g9{U4gY_LyWcC@%H94- z6a7it>=LqgjbHs<)~MKpJ;*FHV9j-FH!e5_z{h`MySEUE&iLz@cZHd#rOWwc8ZzOhw&PSNhlUOY4>FrC;28`icC` zw~|^c=UT1Q%sbt?Gtp2p>zvr3HSS{yz3f{4ncL0IcvSR_5p94MsXtze%7F${I3)8_uLaBsR-b{$)y&ueI2LrKE%1vVz8doq3OF~?m51xLWTR17)X zx%&A(wPp4<*QpAT+<^h;>Ns?IvESbJe|kzVA~~85!_M2G-Wcy@<#<(nJ0Q)HxBZ{bl#;Kfi0w zZ?i9KNKQ``C{$W#2HzH*lm?I@k-QC{MgzE?{LBB5*j?lW^nox3`FtNZvp+oSMJA+f z^>AM0Pwh0XRI&DJQ8K^rDOWFP!l&GZhk8HW?Q)+iR~36Qo$5N6c>?4*u!sQI9bUEa z+!<%q<7wL0SyS&i(E%OUh1nOY=I?$oiDGLEU}GdE<~n$osF~aCZmk#5Gfh;?bhtRf zXvXt#4~Ev4!P8_l90M!Up)J0zI`}^w$m+r5#vTovIsL#-_nQ&U@vt*J=se>|yW#Nt zTxaM?>+9;+)-2O~jA#egUB@%Iq}d~DQH-w@JN95Rny7|WbjnRtb*QL^9t$@5Q#z#W z08h6hDH0p`y)}9tMO}}o`_chApufHRZOPNx62B$0uNKbh>YH6izBVLXs(z&AQL^m6 zDa&`Y=}z&PTtWsWX1OcgI;8MBwSm2t(O3Ew{+w%Q zNVl;w?&k4{kN90EGlTxI6qkR`K4=Jsdg7LWh0T*XYlB=d@id7Bjvn69s8VBo9YlJ8 zL`WRpC=%gTn$`{UpX>R)6XCX2Tw5J?n5_R#;bFSQr^@R>d_NgdjfE`pWzqaoR*&Y@ zz`1u}2kb;sini3jC#f3nn`44+L)kNf?W6DFS>3aRU8G&SYT>z+T+yx~W4iG(pKb^5 zEY{w&JnzRiW`B2mi3o&~XoG$Dr~0vpPBYF^%~5Z<+F3q3nD?rwo%yaRq&0WO{d{tm z{XM79_pURyH@ikUA3bLLU(@?ufq^q+gk_KJDW4CAzUhZP(r-u6>e4N3 zwi%vIOM1YJWuM@4v%b!XRVF=BqqrA@x`2L^i1bwQ=?kk4gHD}LMKVEqLDCWa9|2t^ zz>A6KDct@Hsg@YuS82b) zyYKg)rBv}e2ST1eS4&0i-bRy}T*q0L&eo-gm9IzA)o1&r*4HI|(G3zGg__f`dV4ak zt-qV0%{G32rSZ0Nz3%8Koj~HLc-k}B&8cPn8r*u8Ay@)sDW<-q)i>ah)-oXe_gQek(tHe>D)UE%pV!6-E7py4jyO@)f&z( z2sO=bMi|#9p9~ilIED^5oYr@b$1vx30@^*{tWWwh`v&L0%Cq3sC6Ml5x@SH5UAifj z!Sh6QB);Pp(xoEfClw9jn@rC398~ZWP52%5S~R8fZwOYU?o2ubr7lhUG;>Lf*bC2- zp*N9bcUZmQWZML5FwJ+1{Nhz-ooRhi9XQnx;s=~eLJoFB{8asT+>$k!I!?(INWH58 zg=9=d&Pr6fj8|v_*;EfYrb1#vtC}4CuEndy#b@!pyz8p>qk?!4l236rslPu=NVWH- z^oV%w%4i!u!k^pvP{F-7!>N&Y zd=S*?RqXu@wKtax%y^Q%$DOHmn#h`sP-sgYr%L!BxuN^ugyc&mIy#*n8+*SEbZ+F4 zJ?LtFyOqy-;-cel-7WtYd62A^Wg^7>A)mLSp+;B#1$8GXY=JpV-G>3t_FSH(UXI<$ z6@OT5(I?u4d}_O6}3LCp4WCq9GrDC2m4yR$I% zb>3JFJNjK4*NoNN-sjy>P`W}c9srwU(Asi?;dcBfj=3igHu=iu)9aPACv zvP^{D6L|S5+R$;dnijN?`mB^>XQzsHYX3%yiw*N01Wab?9k?%1S;;G%<9#^zRXn>8 zS}vn^{zJ=1f7wc~Ecp*Ru-%dsk?gd1XcCn@6+ImepBtLNF6bb=Gm^oati-EPTPoVW z4h@q3-=WZsD)Rh0)1yvt_j{qy_;iw+UkRPC-k3Q5Ffykihl5~lTZ(#_l~6AdF0(e)^6KE7ud~cvon=w z;zhKYnK<)a9-c*ITcD{#;daF5+5Jei)jja(O>>i|^3*R&N7`qNtfH1lvdYtqJ-yC5 zpq^B0j)k#@nd!s}lU^Jr`)#b%^mly1GnHUduRHMr7n;SUS|{#>?W z`5KhEl*c1obyA6KcfZ)f$kM%guwzpZc(j#_>>g${u3=w1?mCabgmbLJxjZQCVMyx8 zRPuiZ_HHLsezH-XRl5a*M_vkIV^Y_dlKY+t79Y~;e!~5+|I%NfDvI07aoGnRPtre3R=j|k)92(~ zRF|r!|ASs#&06V?*A7C}hZK9?iDPYCcwyR-H`~(!+LAf#NS)n0_QkQS zX$O1KE!wdV+IpleSL)TpE3ygB`WgSlvP!j{_%#Mv-TP3~Eo5wVVpAdWM$c46N!HCB zW;Q;~gVDm#R;mS?zJ_(%-m9a{X+LtRAE|bXHR*@$6JdCW_1lc){}VlN3Cns3oL*N} zpT)luuB~ik+M|)|JkfKOk*50IVzaoI^_l8rm7q)+i!wV_ z^ZCQl8+aD)#Vk@Mz1`9mA$xc0;mM}#p0==Mf3w_;CDxywGZ9L@3mI=0XEXtp-9}gG z56ku`Y`L0dD*YBJ(?b$PT;YrP(dsR7&3}FJ7lcVSqr_k*?jb&z#9$?x^$VkX&3GP0 z0jcvH-*$HBlNER!4jhON@AP;XN6tYzvtdgxFty~;=W&|Z&VFU;)n12^QUP#CF~3*h z+#xtIy6zC`aylCr0gy**hUi7z~m<;X|^7n8SOJ6(bXOO|y`R(Ooa=Lh>{y=iC zpCK(jBkBKORh6(5SM$;Riw}P<+YW{{MI$}&>}BpbU1LY0+zIG2b)i!K>>^Zjto!MJ zHj{Cg>KBKbuc2r;vG{kH$5d#z)$=x#eyg*Lgg-rSfBaO3nTM_r;UdU3junx}xKyb~ zt*FPX#0|LN9GDybO>Z(e-RUU~iS`}Y--K1Muf6#CJf;oo_cU?+bYV^GL?w|E zKhrm&nJ>qOiFmjj4fU`ZyO_5syocL(m3n&}p;B^(d$J7MkvQ$mT(4rjt}v4i7ORr3 zlvB+>?2bgo%rE9-rq7;5MK_aHJ)J$(0eaKxQdcT{IL4XR%xLN!#BcutJ!2UJ`HU@- zsI9+QKx^Pkx`ym%#Q*w5cGI#0yUOoVfqD{No`s%XM{%izklpV@h(2A=TdIs^3{M-+ zYG>KPOl+{@*TDLut9>2#mK@NkaIcwf%KRe!p?J}%o3k1)t|2|Fs=2FH__Au#_4ajS zOY^yfdENuEwt}zQo7-v-xw5%UHTl#^ja8O-fX`9vUB;2Ftg+A2SNKh%O@>P<-Ug-8 zH+hQJqtNCEbCoKfv%SwAVfLTLxYLK-T`Jv9Gk15RCAhtL$NkvHG) zzy8e6`v#BAJ4H|V_jrou(Yxlen9}vDCf;ohDUvsot^)PFuJ3h2nnFF^=v1+aXE@Py zHT^2R78<%j_^1hv+O#~?ViPs_6EDbo9;dJ93=4gdexa4wD%+u;Sar!#|EsKlGeke> zN{2|lz{>2rlJZ|^TA#SeI#=1wxf}R>Jz7t?JJh3*9K%zasPOafc%G^H*V@rL`c30P zCrM3!vcf;Vjqz7!6PD3lLc0~2Q`@<2D#q*!UphEnPY5*>?nT=7FI6=< zPR6>{^FPl``9<<;i}$pImIqR1EF_c~ev70*~|H>p3vABQ7}`dZ)KatfB@mx}$sF z-H3AM{}fho;w+lE=U=SqBI`Sq4fq^u?jgRZL}dO#m;KRvmz;=H za*fwwwMY6Qq^?~mL@r0=sX3c{mTO&YG+O=uR;Fgop7`fjdy}!I9)WfXd3-+OtNE%@ znH;Ar24`PRs|MYDvDfvoX=nAO#xp?R=cy^Yu%a-yJ&89u( zkqGF4aP(vvU`omD##p(Q;hl=RffroNeS}D~cl$mSN+N7h+IU`U9v-y@ z4_Jik>XP5XCyw%C~spM4)@_Y{yD&yyP8V+$p;?#~o z`Ma9I4X|Me?%W&-Bm*+O)Lv%z1QZ&*Bo%rNH=fjoIDucMF>ldg2sN(Yjj8xzHGbF{ z_ojaPMdVldd?gn$yQK5XYAPr$D;{fTwQJGzkM8(K_ni8{>wJ@FwvXt-pVM={LErJs zy=W!U6*INKW)+%m{9KO}I_V6bB<>+~O%u_&+8GvEpHw!=zV}o*Q}S{z#BZnI^5$lt zF;`$bVypR0f`;i(vA0y6|(Psvpv$eZg<|{Xg2$_x6za? zu@Z?Xi}rUvs(1lyMB=0?OK3G3;yG3$ao0x_zWSE%CQ;^Fv-?87=Q%zV@Wwc5jK43t zp9frXj_2#RVwx)s^vz{fEcxo^J74mSQ;X{e^m!Q^yWE<@ZcnsByda5nnP^s$ul%BS z$r+mBoA@14^Jdv&M(Uq-=;tU>IZ%3_RDbMq4`@wV&Fbr%!N>P+;Q*re?Ip5;0x zyJBx6ZwhDA&tkpk^Yr=thIaiLipcwQkTLyqHe;*rN#EJsEboGX`rx2J#rOxBzpK#6 zIqs<&nrrS{|JmLA7>=iEu-_E8SdcmnRBtwQDWp=lG!b?X&!9ud^fK zbBn)ihT~E<;C=G*eP?;6I7_O<|AFg&DXXDF#B|5c^#3e)@dY}lQAnVncDv~+ZekW+Gs}tFdYv!lYc&2jUXK0NN+eS~T%LZ% zCqcnf5NvIZcM^?VMZL&(PEM^B(|E$8iOxq4gK@z%o|oc;%dBhq z0QAQ}=~R?TRTn{xqfz}aK1q+aRAG<4n4I|3G@fBbgW~CN*aD>lJF0kWpl3v9i1)e$ zIemo3;W!{j{Fvhx74w{m3Tw@C##e%7H;{Q1^&}Iao1BDuAk|rpPlcp-HDk?Oit-Wz zeHBa(7bmiAg}F}k*Nx7$9Ok@lhEk{ILB9{S9%}si8Dl(O7dU!?G2HK7=HQIuJx+ysxMz_5`{Z11ZwKCUO1g>z@8ki78E=;b^OgfL#RRf{5u{_9K1#H{Sk@)_lEt zs_F5!6`E_VGopp&YgzQoP{ zfRk3ylH$Kk&U|OmCK+hy^BMcNLt*b-;OT+oqW8DNrs^XroZe?-gEXu6)l9qi2zUEknx3% zca!x?JYRI+Qrxed*y;u>q4mHq%-1=ka$l%3p* zt!jHbda*egfS1x2;3Axrx&xo`qHc>D_k^J>`1cR+_c)K%Y}4v|oIj$qRj4qr?-Pwb zk$RUE>(!Eeu@h=Yr|?efqdv}ih4bD=cZo!qQ2rZKmW~(Sx#If}C7q^Xho|2~ zV)cIYd=GWTGS7bG4~|QOb$V1xCX??h=qNUMtc_$3eYSQ3 ze%iWXR$7U*s9E?ccIHb-X4Af6Om>C-JCav*-Ql*rPgYX{uUhbW)F!j`;O#hucAqTq z)Kz$%b+A-~%L^bn4{iZc@uP^&2xUOU=PM)^4s(XSlmq`;lTFIqp+jmEE4nW;aoqr{bWU z*tgA5b4Q5S3ExB;J%9`t4=2+1a}p#?UeldcJ2va}q)N17M&n;6ip ze$flZ#m2rIw;oA@slc_Nl;1T3bysOiowuR>ncq40(45<&-3&u4t#+^v#x@bI|$hXIxHQ4#Tk{2Jk+6++~ zPdMk}MO@2TyOaNw#Yd6IhD*(QAKcIbdPNeaF7*xWHM<+x|BkFmZ`6_2<_7bSe&QGT z|6(|oDE{~%`fvbNK8SsGKeogUm`|Gg6zs9r>8vV-CoJV^!zM6CJUH z=8=4wwWMmfIR9h?Ht}qX!yEFw?TP1G!p~+nbSFrcSlv`#_!er-F_Pp9-rySdk-X{O zxdcajY!s=f{~IkVXH7qwbZl;2AF@`w}2^TARfe3j?aEd?8Ay4 z)!FZS3JqjO@_p+Oo;{iVG{qIulPA4`;#*Dq-4(9+y;=O&@u?&8xL+p+E);bed7WDN zsbJTi7k_{ixz~7u0527&u^2`!vZ7xUXZ(?7umxO9H0&PGD|OmC7r57$6iD>q_hx(v zzDu2u{vPp)CpRtD=sl<<6;4yLGxznb^~i2WA|v0jRu#D&*&|3Fxx3)UZC+i)C)Ljx z`k=hajWfNbQbqe|=rPqfqAg5!hIr>AkrG$_v2mo^Z7P?qBnd)S*%QpZay;$P2)m%e z>}>9gFE?j@CE7TeS}i-V$vR05q|&mgWd1hzVk^2>ym86m>}(Y2ciovr(U^A76b2*` zVoN+<$N6^g>s0PY9f%<9fnN7E&+$u)bT^l~lhoZl*YW8dvp1wjHH7GziHF(~pB(P& zi3Z6i6OWpm4;IV@sqe z_G_LGnAvm;8EKyHaW6rZ_!m35N^M@E=U~)*Vp{H?$qccR)Z6~j#U6uw{t3-x15`){ z`YlKIidMt-xHj-qU*$HSUAM}_x1fO{Qtgl#>V)^SsRkejqH>p^D#AQ z_wje{B3Gvo?RYCaZoY?li_qSEp2?T|*c^Q2*mQR(FW-{IRpwl&lK;KANr&*%mdgG? z@^G?)@D{Bsd8N_ZlPmQtu2}3GFZe&z`O_8Tc9fId%BOu7oAi?+LuR8`+;mY$K6d9~ zT-!saRIjN4r4qr>iqEVLi#4_RH@b5CqTjgsw;sRx8%^mOk8cZnUFp1uB3=!j{&M%# z?9=>J*x%J@IjOa~+^F6*m)VoM84dPCSIPPhcOF;xT!Qq$k+J4EzL)s^QXwVP+Q&kh zbPgYg_Ae_WSN`6GN)oAv^hYWLs*t3^Fel z;i&V>(k)&`8a`57EqY%1gTCoqx|`))A^<9E^7qik`|v3}M6=g%r*p&?m~N2Si%d7g z!*Om~T-6vRq>^qTinc54fTN5jor*IDk68KGve_Y=4UZr2daQd5)n%V0JHn~{6&V=R zXo*8pJt1ADE4qHXL;k08`w#x#40m4$6D~8uk2ym+zph3r zX+|aG>%_`Giz}a3=jthYE${Q(zDRR2j~2CLCeVOp`6L}|_oX`?M@LIfjbUaoz3k2{ zJoU-A=?9%+rDVMlGnML{$@ohz_$Kc7Kj%na`t2cGWBh*{J{)cgp_bGlUxD_%XI=b; zCgbr~hl=i5G+gfa&CG}LsL|Ic-| zz1{DnMm);8#5zdTvDmV|k#7ImolAYP?d_P>D0=Iy!K=xKOMXVGLqFnd4;6A}u`x{d zd;uMd#ix;V&0%#H>s(FL#lN(tZ(wB=cbv+Z+xcc^T5KD*-p6cYMmo{IFEVqfL~*Q88_KQ#8iyy{5InUWI<}>T* zlS}PZoX9HK-)?V5vcHwzb+Ma#6e}hjy3Y6ig?@LWzuo|3(N{zHTd6ziSI+l)le`M z*B7{>ba-84mVWWcLX;7F{T#9;`>}*wR#i;h4rXOf+S=vztOt8sXZ8|N+smA$CSFJQ z*_>sUo!%AZDG`u-ZgvyLQCko0>SeQ4fqJX-(b7q!ua@3d`c&zn(icmg^vNe`LcOCB z?Y}Y&zx4l(e!r_&kWHvUR0@mM$;-uym>O{q4J|vKV(P+gi$lG47(5tw{h+^OK9Os$E)4kT7&KC5t z{UAUr*HLtUnP?}qu)l<^a|)UJB;DYA7}y?KeM`@I$wR9VC)m=^J zSI-V~s@R|189@?+Ixm)V6} zY7cU*`1EP^&c-_506dvGwWIBAPOz)FqS96+)$Lz)D>=QSZ%O}>@g*ZmZZ8>Ha$Cs_ zC6||+S#oU2VRkxyb>8npw~rP5o{X6@3a@+*+;K7tI1!Iu&!3*W!CUNXj)D{C`E&p@ zI*z~nJiqGZeHSQk0{h@#pB_(pN@d*R@!PpL?_B48MEvS3V|`A&$%*3Z?)U#~p4S?2 zD$(3x=k!9ayE&@6M=DGX#^i7nO=8XXE}y1DlGBKNOX75rg4vag!0 zSKNDQS>DFqGRzsz^Ek=5j)zFeQOYjE4$hLQ5?jbdE`uXK7TRAt=6};?*W%}Y;AEm! zE5oJ|$kr5IRK(Y$&rv!bMf0j!=?ln^8kR4T1y@4zgQ0qAK&0-=fpp5K@E0?dKl`nRNVf-IM zpu-_Nmu^2^6Hm64_Z|Fg=T`@aQ%gnd(fDr+|Hv5JJ{5jFXB5+m z_=D+mkt=*Y*io&;mj3}eUM`<Hzf;4zFgbT=#nEbEU78&MbWpcHL3>aOndu>r*vu z|1SNfbUXFkc5}Tmq18}lO^)aU|KI11UvPy<&OhGO&ou6HT<}iCI9``KVMS!~+3d86-Ir!& ztU669@vz%kjpSYJMux}poG#kg^G$r(iPk(ZGE>Q@Hx1}OY>aoY`cLaTIZ#MI}^qqrw^Abst zou$;tzrrK>P2xBP(WFw%@hw3Yi-2eWxr$nTmWIm25 zIJmxruIJ@@~0<=_h9W5jl;77lInoM~B72V7}N>0u_W;0%$c28jr_a>g^3i9SUf5%(1+pXLf zG?$2h56xbpV}3B((V~7qF|VPc>zKrNckx+Em{Xmu-6JxSk{9 z%c;U^^cD?eA!K^Y-M--eN$~4^d-kvLLDzutWo4}$(b-u~GWM~~nBK zT}}J_jxMtt_kW0vlK1u!FG6aiOe$>ARHD9%t@1v-V+kGkYkafNaqHX6;cl~{mGXb%ipqAvMZiS z9orXNQ$jlKglik%h+4FQL@U;#|A)Kwz-#d*=KuIuQn{@L{lAvK^&C}?#7$jAsmjq9mH-SdQZ zCvIg49U;8}UUs!*?xVURTf5sm@q90Qo*vGLUrhzS5!QAnIvxul(#^RUPK_abmJrKVc3&{uXW5-0Z=n>JqjbgfCe^hOw+x*v7$#KDDbd>YE{ zPM_^fW*tW2Y;AY4D&G2+M9l8!I&`qg^*+Yk*)JSvgo$p5Kk+`-d%GCNm!!c0vgl3p zeQ+q`%N8ucOJ_?q1SCF_lx(CH)rP zOy?yU>-%`kLa`&gidQZgK=w$|gEXGmr=35s)$x+1&sw~cKhXp>l6mP4lHKWV%}XU5 zyb{WMPa97ZNL?6{9?V;k%InNks*NRHa6{q4-HHUx-tG?2W(U#kdy~i6Z*5I-$1@r~ z$=|M-XuDwN8jnA5Z7G!alaz06K3f)u)Qm2+wX5yqY`?p18BOU|&+iHZj%W2V(mP{} zPjxv8_#EfYCs)6B-gzkFIr`afbCZa?#8$rw#nUNZVqt?Op79L4xC4q$)+Ilb3&iBeEW$-^Uz3lgzl+iuUt*5RSRrwd27^*Yo6jb@1H7|Gk`V zXVyqll5jCTUte&=A2{Q`vP5$qTI}6a)RIe%aQYr>@K`U>eM>X{i#1M-zQiv6|5_8% z&_w#@4W)VaaMk4BjHGSG1Ns(fj)wRx`iPh480)-Kv8Is|rOqC$>P>g?04a7U?2LS` zOJlEU7h(+!A~9~s!EH%CACEEzqtp1RvYH553Nu2?z&>V(@krZ^L>POzBT@D3fYm$ zgL97@q|}!iTKI3SVR=38d<%S=D8#j{`6K+y z{&q)W=|ey2W8R~;jx8*LL^Yj{w#HhM!_ZV8e-Ad>Khx`~L*N>;$!g`l72_zORldvH zy^`Ly*tyrcgP+P0qxwJJbfkf`V6XKlY_{ZP9&YUpMXQ@T+pna>J4W>yS+&~ze@r(0 zjn~pEuNldbc+84;*F8{7qF@g(o=(>BAgg}>ZrPa}NtEUpMwwbhsotGCxy-#>K%!nq zH$2|TrM}%jS3cQ2ooV%+Mpe(#0n$hEOaG_dY$XwSfAH)kzoQZ~*%o4LR_H`qL-=~E zgw8yd$UcUQO_uMM{z?NJlb%VbCK~Qd zf5$prXQ%A|pB}}B)QP_-^$^C2-n@XX>2dkg6Zmlk^YM)1ow|k1br78{xiC%1z2I90_8VYrHa~HyBKS7oGSL;;z^2q7`?YXdo6o>OU>Y?1=fA*n*WhWyEvkQ z@g7NH$18RwPg%V}o@f1kCF4>p@rhzDCAoH~AGaS$td1WV!|c{*@i0_&f$wgH(_=`J z^s!AnjD1n%iKw(AE2JfKZh``jMYG9kPV8DL3hcuZ^go={g7)?c9C(C(WQ@Jt!{z+< z5}($U54Q);&Z%CHpy|9ptJ_%C2&$zwe5}lHYT^_xA~)jwNPnT~e1C$6P?nN$Dks%@i=@{$2Sd)ZCm@{8~MoJp}jq% z4na3wpxz?rdh_Vq;{Cn6vB}-}h{Q>(aw<^$gsYWp zTeeo$q&G^RT$lQ@AX#rlW%)(b8#l$?|po7PeAv@ zW+L6p*1(N)xLfYJEA3=2_FZ<%ml@3>zggk#-}S5HPquc4=fVFWm_zs0X7Im>vzoa}1jtXJfB2l%-L>Mx}&r{3B#h18k`=f5}pe<9y|bJxsToJ{ug zcT{#>$a$LyamO=41IRg$v6(KEfZH+Nb+PJ*h3|V#)1DwtW7D{~F=C?7sA5 zm5so`vtYvqyua_U%%0(wo$T=xKkz``Ci}OyUpHl;#b;F&=SJI#$2D2oyYagGM81Cw zzvhw96O3*mf7HF?VEkTpkbh76JHa?dqqW4OHKOsvFHo5klSqx!8$B2`r}q1$j!Qqw z_Z;z-QGaF3!Ng6ASiaQeOdYF^?jatfnnn}-A~mm)y^>wCQiz#oo@(+K^Z5okNdtVI z4oCGN;~wtkT-87Cc9%;keGdV@%j1f-1660sgJ|VY3$TgoEa!Px~nVJ zakd)Hly1ChQAcdfr`+2|<~~tw??8&5j5WQs)88P`x&P6U)01KZ+PD;EbV46paOqxF zemi(jjUT2NIligA-LJ^a&#n3Ag=ZttCh2vQRe#Er(wFKVk3=<9Vll`6Sm*z6w5Hej zSEazX1+np|qZC!Sz{*XjQoZ8P<%_J)Mj=$Y%SRO+g4f`h+= za_`f-;u%|ni)Z5F7y10}!_zO=Nqm`ZlfEm{XU-(y}eB z-8^`-oL=^{JN}sb`52lbU83MdUp=uY;%E`UfE+L5}_ z-{Jh@m(WQP1=t!s?FgUZNsk}6syLY+9Uq-48Ml9<+Gtdbd7Mt-r@F*0*?IQ$Dk=eY zgN3`%5_ZQ&u^`hIX$1`~d31^9OXq{=Y8RQscA;;bLf<-G1-pmw zVLH9Mi7%JiD}0e2m;COFiru{v+(!?;J03T;Hv{{){|2x<6_C>%yiTESr8;0fOPAYL z&VLB+T)Nz57k*o_7F{fNUDYv(tZqW1t?vF4rT7W2!rR4ad<=pG7__w8@}`l7>Rs*NRAlb3ymdz@UX{KM?K zi{N&&w)mm<#v^sTu5A~wF)eOyBTr0mVi7klPjB%BEUh{8toP|1D{*8zXNzG&?5?Nn z0zLsFo`L`$7)RCe<-Uy-^ebzurCCfbmgZz+UA%jmQ4PTzsUV(ib}yiwWW=TR`+QWr zk{(?HDkrKWJJ-qQO8tVHJuXL?rx|tIf-<`}YfYS5ll*N)^ZW;L)rQc&(?5Qqu~p+M zsBN8^u&R>Fe*MOy ztdA4$`?VzZt?Z3tke%;&4h|T|@0OjRnyjD5`^qSI5s93hEuWFeE6{FaVlrud@$Hth ziNycKWAQe8NvwOSoV{s0Z<3fZA;*{Gz#Ni35@8PM`4FunJu*i?we+hW2y{@=<}*g}qTJ$;;NmeeX~U9xvc4V5@MmFy|+xs@JIEqqr?48caf zo}zZkUCz`GhsW|Ng)U3^22)KcHKkJB?QM_fwNuT`yL5>}YHn%9;xmuWEHO5@lk_!z z?*C-yaC~(FdN_(?KM2KFBO!J|FRM}RGIX?r1WNDr`|VfXK%zZB8jZI*bR8a>L8{I4 ze`?HTH~nv)E%EwE9P*-FmKmhW9K0~c=h?&hmrMy~?}aB*XZ03n6McEKHMrLv(Jkzg zR7Pq9Z98~Y$Nk$HOG6{dyX_!Id?txwOC0Nt`0E|?{SsZ{4LjAh+n2t9b#)>uq?0I_ zz33a4ihsF7d`l!~ycV0ev)^&+EMvdkPWfT%j=k)AcOZkemOr$+Se%Y!b@e*inoMq> zZumB3KbL;RPFX7s=hxDo{J*s>Xb0P^KaG69(wVJMQp&0A4M0$LW1N#oxDKQYw2kg>FrG z=v&bZwuR=|BTF^5GpxkL#&H{|eYNW+$NMBG(HXY1fQ(J8%eu1oX4<-!OYnW_V4YH| z<3zL;S@=;QqkkxNnSUk$_rt@<{NIHvYeHIe#YbJpt=;j|b|lqitblYBO4k0d{4mGT z%-fLN=c4$;qhzP|WVZWpOajgNHYeV5#}=lkAY}!zQldc{DUg2e4lL-ob)@ z-sfMHRpiGcGwV<@l^o}3l9no1>i+{E3rbT=*C^?~l-gyJraqNg10nf-$; z$?3#F*7VDE5T+Y6>_~d&xN2r3b?W}&E&0Q}eCBR8K)lpNPd}SU@aiVqK7QH5ZAK*`$cATy15tmzE6d`$_zfX!HoHScWtH#Hp)L$`2$!;#iV# zvppJ3&d-yMNsQlN_UN;Nxj~Q1E@nOzINQ*#T3LZ;a%J?3?cLEv_?t>7Kft9$W@G_G z9qo!Yqo+ZhBaQoZ^n1BEd(>Th2BUJ;-#k|FeXTI^MEBk9p5jwYuK7rmcCJ^03)Dyz z-NfZ4uQ!#nI^v0Re`$nAvMQ-xlz8FfmyWbL7dlV&OC}UJe}_+RqVv3LRIizlg{~Tm z{MKq)RNd)TsFfrEOuQwzXo%q1=6GZSBoTVjZs| z%dQ~P2E*)Q%ugLQ<98&^WPE*^5gvfs(pP&O#0)~N@?3!C*3uCgn)T#=rS{NIENgfiUka)^_*mHyU^e!M3XNua6-uEfJDS57mJspDPFM%y%(cWEf z^#(LK#N%2koa#PzSj+5qBs#jIv8HSFO-6hazP#L{BYugE+6(QcF8@$m^nmBRkS3K9 zM$le{Ip-(nIF*npG{|Htrn15ftd$;gx8$c(qqSEhH><<%%4U97^phQ z`ay+m{D$);ZzLLE@(GsX@gQ&NM~t$%BVqSR*1VzdC(p8>q533sOY6qk2 zQdnxYi>RDT>s!L-@|{@Haz6OabnjgvQgk8h?Hk^hZ&-P+(cvc28`GC*`$C`G#+kPy z2R8M7XV>n^5+3Y1#+dFGPx-XC$`Q^ziC1eFjrbyxV?V!aPG+W;T%xklbvgUp*%b+W zA8OrFfxipQy*}#Q3omS>^;E+1`xk3^n0dM!WnK^8QeQ1QeaYBJu6i>2XZtj}r>U|v zk3E?B_?gF4z<&omCTcse-$Ck?Bz@|3#m-Dm_8P{O?is<$a`Uy)XO--3q}u3vg}y$= zRkBC^o%MU)xD#nU8nq0C6%%1&YG1vKUfy!G*Bz1AvoHDj|8lNn^xE_}T7|0KN8_nw za+Nzt@5?qQ_E`SLUGUwWxb=IO_8Lr!u9rB)^pfdC&)bWCr3IWEOrk%+VoS`?@A#q# z`O(mPHSm8G8q`J}ou#b0^iJrH%a5g3o>*YvV2FAdL>vrp$Iv5B=E>;=d-veeITY?* z>(|%0@R*dt#UFItTICJz1z2&<})WIbzqIW6-^Ua~ujnDlA1^M~o@ zcbbh6)?}$G{ad!VHLB@|)EM5y3f7|Q??ozp2tnU-{P(o4cgd8y9XG^`o)2HIDs=kx z?yjHpOpl)PtV;Gzy3pRw^yz7Sdl6YUtcXEf;a-n|op+#=L}R6gQ6o2*2Z^y-Mc?U+@A^Q7=sl;C@t2E$x{;r01Rwirc=I)!KD*Ka zQRHj%@7}=cybJ5?ze;y!A5Z%{gkE~0W9mDy zI-ah?UQAcDS6Ph<3e1Sd?+k0*owPcev>I*RlFyq;S?}W7be8^y-5mdZ32aLhgiSnR zk0u9hDNK3U9EZEpgEU$4FVe;ak%MQ_3=U@v9YZoTCy_d!(*0r8A+SB3w()Qx^|@m2 z&cXi+t>EWYE1kH$bH#tXPLygT9!FizkdLJ&q2h2OLUoXwFl#HT|n4KThHj4=yyEbK%$7 ztiG$s_T*VVLQYrI40y=#Hdjjhv@1?th5( zwSW%Rl0U4uS&UB5*6V%f49&edzpj-63_tfQE$aq3JJT#rForQk`!00(2wi^7 z-df2s{k3aX=nOxg&@bK9r#`9ZjhSv=^XT%aBbqLQ>(K7+JlOT=9jPaty5QBw_^nZK zb9zx~PNvF!cYdY6;KY0$q=$7emsbd)ok3NKFQ<=W3aV{$P*F^e;C#k1yr zx^taLBR#=drxVz1>zJ`u{m%Ev09g>srrbnssD&E`+d<)+ZAXwu}jH4ei)5CL1xS{`lkv#e3Y5E3c}Q;UpFY@Clu1;5iS^2T=IUFuBIAMLvX`5w+E<4&W0 z^h2$KoZ|sJc_r(thaK-30g}5p5;~5dHKxLO_IA!8Et|NDt=VQjqx;Y8fo%$nTUwFBc#0bMKXrai zFKnv*u)`?2SW6MrHN6x z%-khsG~U-6o%1Tlu`hJ_gC&=0ZJWC8L9Ux#yoaKXR9UpzeG6aH5vb`5y7wM9Fj3Ss*)wb4(btYhwVQNo zPOij4bd|WTbe>DauNTl|JgJYd@RD_%I#;96M0TZipsy}-{Cfq}KWv=$qLTqe9__px ztgK%C87=B%y47Oq`8<7PD73!Nt1D=oU-~pv<&qDbSh9-BB3Ht~=xkkJY&56~%a;yPApDqQVmHU`ij8bgu2P|c?0sf#;< zHhVQGxJ%Jds4WS4qvsg2GKF0Gx2#@a>)r$@p5|H0Ui@m&SDVA%RLVR;p5TLp*3^$h zSVsg`VwS$5?L3VlUZh*ZB2VA2`#qC$f1kgpIz5HI=>_wh{)}%GIz_rDe2Z6R`h1$- zk3s9%T{#vXq$XTyw)7=ok7En%Yt_rk+M@ir?zOYeJD{F}eYyqfRr|6z+ly@D%9Z+8M71qOn)deGtuZ8sp zx@%!`Jag(ll5nrT@if=N+x}N~_z(D2EyPpTk)@%#4J3ojtj&vrDr-EV7*2bmmdBM( zM>JL&d6e^5Yj_FY{0+!+3h_k&U=HmstN=r4h51(4NI&Rm%VNFU0hGI|j#Edg8`M7d z*+=cF&O(jRdo@)p1jaKEQDjvYO4_v3&dbno1yxGUO?fNH~rWBj{d^owKu z6vvq7z{A^hx`s&Ag1>OZF05e00mJP&t(h;NXX7uaS`>0<$1y7@n=>GrXF&v04D8Z? zCqcc=-Zx3+5 zMflo_*mW#0Vm|O84L|9M;%nGMAg(n{%ZF!7yVcQ^H`(?Js?4|m+?xikvL5#L09a0YVA8!N>8@~v zaP&U7#wm!2M&n%VVfE!;^<@wdg&~_=4(z~#e8)Xl{90fP)hu@h-<*Q^vmU5*1}H!^ zAhfGl2I_)rU~Tjo>6HZ`pKJz)qDlg)gE0YVspZ6iozV`BG_QUeb8!W{)@j(zah&BL z{!jO+{BP%`{dfoMx=gi239zq+_#O|YNq2`hjnC8Fl25^YZvq2pPLbAbuf%(Iz_%a3 z@8~`<58#)7;~JmBf6*?3w3qt77zx@{ZvLjPIog-( zA`trr>Io8nG~bc)c&~97si%mRM0mX@SnOD!G~G9lD#iX&bS}XYo((jkHeD5XBLIzP zhtl)Nvz!J(&cpT4y|LyZKYak%lP5UOQJ_XDFqPI!&`JwGJQ14jvS6);cFYLCDo{zF zEX9``T+3^$-o8Wz<|_Qmth|w_iyTW6M3$8>+UY9YP5B;(EYBHMK6r za}sh~#}HFJ0FJ)L6E-5_tl_@G^dv-1KafMejM%pq#w|aNlz=GI3%q`Zk==oQ3W3l= zH-bMKg*!N{g~Q|h*AIINywDy*a!AKG;{Ou|D8ns%wf<;lMB&zK~V5PKfhW^e59$*7g zq1D;hu)ahbbu(VH&XjhEn~0}MyXcGq6Pbn?PgS-X2AA7;i{~NiJ=vUI}N#ldE8!Ea9nZq&uRs|Ri{82n)rya=uAqWx;h;IAXWN2Vk5(;wgI4ubXY zgq!03l!MbeGW@l1Grcx`nH{h<@X%;YA)JCzg#oSr($!6X2Vx zzzu1a_FnJ}f8zd|VfD8Me9bXr%Pygc^*ZX=PXn2@fIZTk-YgdA8{R%aO|#ND?>33y?|StaP-=Ewp1TXBjJa5s_D3H zx>q6X*K!5k@hnE-FFaWq^_Y+&jf1cDz{&?8o@xyoVt_uyHK%IF_Yj~^W&FPh{CEfC zb34ORkHgwrBG4xZDE0sAeNEAWPZXXHzOxH5$|n`*e?bGs9e-Rzk?O|&;>-m z|LlnE=wBNm4^t1{|3bvL3rxlYk3Rrde+OT8aplVqgYLuF(@K|{z``gtn@VaQ)#2b+<ZvxDvz3}VfcP;SSLBQm8U_Nxu&Xz#okr>HQxQ>5-V*lbQ zHUd+w0?~_t-IoN8Zpia>KVa35fvvO;L^m*3T2p%#e(ePO>Mi{K4t_^#mgzp$bT&tW zSJPd-XvVuJ(2wpLPh$xqj#>=QhE~ffT3sBg6!MRC@lG3%(hkq@0+^TtWTurBb1^3U z5Vyx*RIZ@PeX}|cEOe3D9iy;W?T^~_?do22o4N`$@UMW2`EW0F5Z7JAr~ULqcp}=3 zxGG}b33yI)g>)C}Z6c!b;rODJYRB+&sZNS^16_dclVF7-a2Hg!7zZ5uqL+auZUE1F z3j5SuV1mgnWR)Y34bEorV<+Eo*h8Tu=>RL}hy4_yNFaFwE7}7tw+Oy^5-_6{eujg? zmB7Dhz@wK2-o*m_Lh+*6rXbiy{=6PNAHgYTbysg(*-%7SbRQ3O*G}PRIT_K z`7zpyTZY0h7VoAN=QS{Lw1%YxaHI^Xyl5}pj=)x0j~Na=NBHlYxI-5sy~}3!$B0X+?h&R`*%h4DD#v4ey_TGi}A} z-2l!?D~711aVM_-I9M#LzUYH{r8=30I8IG4Ao`gpn9a-b>gFrrxb^kt8twRY0TB_^ zG`lbh0`Y&k(obS{ z+{Uv^!`iqFw&g<16$xLThUa$-_fiMZx&z$o6fk!I&PMl}e+P!>1Y6AlTa44H;~h;A zdr&XC6?5n|o*(UgbQnJ15uPW_Cf~!~Xb0>4c_^_47CIGAun|`E!*PU*U}e>CwGDu` zt#P&e@$bdp*JtvOehgUgxIEsrH7_#d5iQn0>`v=#d*;p44wymtftUq>m6}!>k!%30 z_6wfZX^iy_csja6@^6iUJ#$#i3&$PN8V@=erQl=w;i=7ruiA_m{1!7c2NBY5y)tab z4y&a5Q(D1-s6s9PtH(w1BFhlw9q#Kakf34S`pF#N%_NLzFWliu%(OxHPFKCDO6noJ z-+ug!*7(fCNYH+({ecvl!PDkqR1Ra_oxo4pgO1h=tOB0S2a;~aF=(IU(cm@Rai;@u z-!XV1biJqtY_k{UUL}lUEM|WWp6^@mV~1847&r!>x|bJ;(f%*@VR=+>=){@m%GN{t z?l)|nDmgcvaw6;llnd(mpxCt!-SulsQv`8e)~X1%vy2HnM# zQq4B4H=hVUN7*aw6Z_v8v%q7i-fazLLlYd+jjOl`K0wzoFJdg{zKw=(5NmLBf8C2figS6Zbng&h<^Z*f zu!r*CUbJ?mEY>V(rD`PbJsI(o57_n=KTE+b8UW#&gI5;C+-iq;W{0n%*6-3xa5)=s z{{6s{v0$3Bu(sU?+@S%gy1w9Aorpte6uLxRi#-2cM5h)+tEEuw8iB~QD_9!cd2lxF zcqhgx6>IghhpriT{T6vF9ywGuu3!p$O&g5w5Liw<*ho2C!584xO^neVoc}Tq?U-iO zbG52K?1`|9QJ9akzUT8XDNc$ zJFNb1$8+n2csT~Fun1NmdjjeH!t=QVEWHk7P66U2X{|9jC9v919A~B-cdH>c*8xb{ z8u%BI#}ht)RiD7UHoz<}YN?3C%Cxv&y<@ftrvfmoG+KC~A% z-GQhBvdgFNy!K%}%*T9Lge<@a{2YS+%|XVEs(n1jyf#F1cLsKm48PwFGg(I*<7HY9 zjmd$mdf5>P3{5a_GNsn#_ZmTYW7~pm{C1P55!HesS7)yy&+d2g1UTr>%uytfH;fp&{YLI(@s7y z9)>3`P`98nVTiU^(iG>5$7;bMaLPiM7yH0*ri1B50$1sNLsVT`1@n>adHDs;{33k( zIsD5{zl)hU0zR`U-bH)OFTmWPee8RI6Vc8_7X1x8@Mf%#wMLAUt3FZJAlEw`z0D4Q zaqmWS@kKQuOB9Jnq7!W9FFel?u+#U*0Ix)@pGFd6FbCh_smCMJaveN@sztWr`x#jC zSwvSyaE2|gfp&;@Vi4c?;b%*%!u<>USPTAe0G~<0r|ZC8(vj_Tf>%AlJbDAGnT8|W z2Qp-%#yv@+JHGY79S?%t&^^%C0K2yW!)X-*jnpo{I{w?EcnL82Pgr9VtnCqe@FbkM zBH||w-QThi0~SJDG$qeUu40@x-HQwgU6uZXbM?<#HGF{1ZfWWqjKfTz!aQ_#+pjJ} zkGJ{ASnUE@oWkodzU~1#k`aeJQx~i2)cc557O35jEkBGXWsy2kU8eR_JLW}l6V-q4 z$$eNoEP+g0FZlN1h{BFybXEiXUV&9oJKj1O~;i#0sb~c7VZ?zL92rth{G~4 ziaavy9}$yMPSyr>`hmcX26)Qhc*<4s@^4iTKNbUnYlJ5?5y(&X-1~uNa19xEy7O*5 zSo>%^hgRUdbe9~u4*f4YEUlrW*^9q{fpiztiC{S`fH{BRX{^Fe+Hc?pP%Ik^z9j5r z74jG!SVRTrGun_=WD4wVFS$zgkc-GUEJMCwKG}xOj*Urm;(*uO0gLYe)QCaWhOST- zLPU@SybMD8Pz&C0400C-V3RlST7k^QaiGOsoToi}Z*_d8F!&)=d+Eq;8IY$D!5?Tg zk#?_+$92)36Sr~glQEaJ0*`4gfha_T)$#e&i1M!CS~F1>Pz1^O1k@$&#vSy>U)v&1 ziGaW5vD$6Q<6AP&+YjiS4<5V}q9M9_9`)iw^K>{TQJ?e$xs`Ad06j`WVnXFn3Dh+i zanuLM`~&f9Af9C)cqa8!W}MZIr$To&4#MB70e{=!sdYte zX)F-=G+56Op#D{$*%3Ugg?VFm46A|jaLw(2&QUn76^ttvY>HOjQ)T!>a7wC$oQjz> z7aVj77!B>RPAhz8gIm(NOsb0GVDDK#3##N7fqisO8|tGI@Rbg9x(7DB58P!jtb7pc zY9Mg02WB}{(#B%lza{uj4ItIXysz8v`Dl5+R5w){ z+=;Hl(vJLxfF0j3->GgPAJ+Oi0GV3lt@zOlMmglY|Jz+73^{MwD}(lst%dr*F0hye z_&Z&l6Oq#hMASln9RF!c>5h^OfM$(hBTIk;n~`JGm78l*b-8VKf3(XKY$RRTRIa? z3ZsV72=!e)5{g$@AbW9A9}2vhqz-B-Ta$l~N9#q}A+J0F?4~D>JD`=1TM>cMeA^^&=d;Wx_>jxY8_ey` zUsBC~c7bh1pKVvtzi4`~qV)Q@nYL<)u}!Rkz==@3Z%`Tl}`#N?KXV zd{cg7M}7*~shO37;sNg@SJT`JS&h@D{;vG9^S7A9zv89$E+TJ z*G>%FMCL%$llS`XU-Ucszkb8b`O#0Gt<2VEtN8lgFZ%z&;b$%uUCPWMbGBT$o%dJm z|GtN5^YTskv(xs_qj7WOxa%dK_w778qx-Et4i%o8xnw0Xr{}#n<>(Rr`;0Ps$k480 zk+;9~D{cPgxAZT|`~OfbU0Sia<>JR{f8JYBT%RrDxAg4C3zWZeW+^@X_AN6=(Ki3@ z-1_W0#Ztc1`MNwcO8izfVDNv(8#Jd{YwwpI86U;A+5LYm_fGJy9P&K$bkI{pcK>7O%56(8^wZ_s zp+y?UTb)Fd4*E@vUJ}GITeyk-DM8Oe2HVDKXH3NlG`G)kFVC%}jWoxZ50Lt<`6${b z96Uq0kQ=5|u(|^}1sK^>Uvq9%aH9F9w8hXjv}*oMLHlhTlosk=exGd3R8|Q!%;(SR zZB(;Z*j*T92vel>S*6haueYO`OUdn(x-g@(v|6p{Y@d_nUhDJIim>GoAoREv?C^K$*+FDS9?YY;f_ttfxgy40pW<}VP@^rYq`Ph z9~y8fAKs#_IB9PAk*J>)f} zpm0JSPEcpAH_^_@E!DI9L2jY1nftR^nEb1plMhO&)RwH~*Geaxkx~GANAnlI2#LOS zl1Fd|x9m4mIB~=a62iC|CCk%+1N!n$sfZg|nGt zk-gqTF0ufxn6`{oXQ>o^C?SAcS=dLIj)fw7ja+7<&{Gl!rmwM`ZItYPMdFiKETKa7sq*S`{qeK}k%xCg@MuCnQu4k!#4w@<87Z zuiJND2$9!oF^Fn5u-*9W{5-B9nL#e;#nn`)mUq3&?yQh|FuQF|QAe~ZpJ$*u&0%*w za2E%dWv}px|g`$KsK7-z2ciG-WHAuNpc_dnlaQEY<(LrJtU_9i5MKI zMXiil5h)dj514HG!_}qMm>&PAL_G@G78F5 zaj`esyWh7=tzZZ=pECVqc53659-4`Z;rp0Y_+1ScXCGo(r-k^Eb3SHW&;B!qaa{2{ zRgSR_ETe3b{Y1ZW)@J5XhEOJ4nX1$luKK#G}entte^FwlwarR58yn6=!F&v4+K__tvsj(eIx9u&tH3sOhe86+4dM z)hR-XSX!|9ri%%_Zeo8=eaDF0wT=j}8vEPq=XX1#M?r7oh{8&=96i0zt?15?J45dU zM+V2(UmJ!QAChI#U0-|W`_zyWM{>^Z296|M3_2U}Fgm?Rsp!oG%7$DDxWu0nuQ{4z zR!u*b9+LGbyIpqCT!(9$v$Uh9cd*!&IpLQae5&A=uty=U{a+e=Ysj zoRvAxyGA-GPh(@*1!@4Y?g_F(XeoB|UUO{|WvQ-uN^eV6YW0*3%n34_X4Van<_`8B z`QGNM;9te;B9ixOc8T;&sky1UvJzc~g?&=8a$WaFl;=QU>1ScAG)BJQ^Y=Y-1Z3aM z+>lW^=UuMNb=mh(oS<}LpK~V&VOKE2kcFy9?(z<<8Q0!;-&)SEgrey_CEL&0g|$Z&F3P4UHfgFVx{lhmJj7p}T_hwZuTgWp)2gY;Ay zNJFH$dL-AzG~Z^mm$e`9*8^RF3j^}oUzr!OzZ8pD!#CL5z_~uVM#l2YO^!LPrOG1X zG+ViR??WshGXt8KJ8NTw`rZq$=|;*yX}xqqej`0o-l_)_)B~}(oDK#Z#x7@OqheyI z+*~;9-QspS>N(CkYUPe|jB^G#dwItCiu>A#Zx)Bkb22@dL$Z21 z2Kx5PC75&kJwrYI8J7j0FxAk{y39V#kGCu|1TfpgH1`2V!^$A;98(1 z;DGhCVH#6Ny60~0oZ)EgtmW|&zbi>(E&R)J(;34I_OceF43j#@x0&ab?IFjbwiN$d z;&!o&$kw5aZOfQhzKGng)VS}}zxDbu_RFsyl{31#=jbUG{{S|yXuuZhIJTy2@LtPq zoFe`BmE0j?h-1J&YGX{=)bFZl?l>CpWU}uj8H4-$C-c0 zUz*1^(zDuo)zjQtUAfQPAoY#;?a=|R><{fj?Q<-VhJE}P!xz4bku^8x7iq7hhTbBM zQn{_%rKFi!3L9_n1@sKc4Jx1Sh+ld0D(<-Y+E>c8GxurEhwKqKInLD{hxexx$n4@q znQsKN3AqxyHfTUlqJNy3HQZtAGcCzWV-?$DzrO>|hhEKZ44&lw#&}a&lshy1)6d(9 z<3G9MLq9D_toQ4EhLro&^Fb=2yZC57XJANB{=hI>UBeVck*5f2eO-j>;(VnBa(Xq8 z|9YbD)n@5;S%2dt>(_jy0vX|3BH|-XhTkhVv`}!Na|L{1Ir)bNrP$_jhkRqxyC*pk ztnpPpy#DYa{$aw%Z;q6*Sr;7>JXJi8b3AEvQH1I#`R< zpE4cz+ooKTVjOQgZHP0jG&_s~3V zo-9-?=AWvS>PI&1-E>QBUzuygnpN)9;d8%!-E*5BtF@rX!tTq4cOI26;8NGzw()gI z@tJ|Exk|Z?_lF-myBqwhPSQE0NO*Lm4RxZ*w&E@)9sIEG+l$Q6K0k4UuOb+PEHn^r z%171wp2Nvm35f~*N%g<=`kL~qiDQaX+Osdk`1y5wd}7+y#b4cD+x+xTiA*Ai!q;J` z!=0~PD{}4G!?Q=GSNQ4r(llvGW;;(qVU_Q?H&FBw=ez&NIG!}@bL@N5i^Wf#zB=}W z{k1IR!;gQyK2Esv=3LzKxXJJ1lbWRz%1H1IHI4~sQm|QA?SLbsievrPiLVPkAv)RxmBgVWPfp`dyeytFIaL230{Z0q;If$d{&v%ov9nL#^?T# zb2zO{>Yz05?}6V|eUv|~|GMe>=_JedHQ(zbcg~uZ`6&Hn_EXn=*Dc2bhb`woR+Y@| znd>v>?Az2C%lN~XbP8xhC{L|A(B z7xZ~jn9nIUCN|?A+)@3CT-4V@?CiOlbvbQ7R)1$bSF*E{tH1Y|SWar>o9zDOD(lY8 zDU#jJ`M02{XGt+b4dZT8J6m&WWmAl~k>BaS8FquYE4N8)D_nN{;f%|j>ss%dtkhw4 z8gB;P4c-|%&EDO-i#s3{axKpMlHr$=>=^Hw=^Y>*Q3`71g|D8{(g^J{*Us-u@R~w= z@!iF1MvW}EJouMwDc4Flq_kEGkn`MZ)@yPEd=6b2IxS>v$lQVz3U4lEDYdp_c(!z{&YRV+gdJBV90di>Mpd@KLy>H7(B9UpGF?mIeeQ|w$C z)BewlZ@R&C#V?~obYh2Jn9cwDHDFrDeq);5t{hu*YVav*vB2<%@G{mKiyOtYuGr4k zs(*d0%I?^NQm&}l`6?M(sXfJNQVT7CdlSqRyI5v*g<%ysmU>=jNWNNz=3KPdYdaaR zKd4$kYgDzUm_pCOnaRjBPL?ya)~nC9hMUIwNj$(^OdIemeYRS6Yx<)t`htFT{rzvLDe^A=*7VYjt|(@427)zx6z|-~E2^ z(uwVd_Z@Se_U(cbdOy`@NZ~ogup5;Y&PK{LVd)u$vFHh>n>@fFSIZS!W1@y*$xCe4EBYL3~m&sIR^v)PhniL!sTMf$A>ycISzvV7F6VyntFsj#GCRHdyIT9-N**4QM7-kdUN3x57g z(!YiMJeFBdSfWm6POyI%KU;JCY<^d54*N)dF>q#Z{je6n$1MGf&A7k$?FNlE^82_i ze7J3TaKq5*`Tatz`68`z*xAyxtUceaeE#^gS@NQc7P+e9ACDgxN*i&G+b%>ax!PcE z3s;sMs9B^KLH5=0cJZx|3$RnnZT!21ehFUZ_uRbP+RU15TO82OHq6r4e_80(@Xt}7 zqIVbU8(4!M;`x}o_*3OivET0hOvt(~HshDr4*89>HaDfR8=280oIPR)F?`@2v1>Gg zGE2E8Y-06*0ICcMOs~< zifdM8;k1z7%J1)K6LK@XU&H|NlG{qU$x}V7)04A2C*Iv#-o$9ebLL9sc-Ew6iKE@K z+(o75{+pG^Ad;7GoAt`5#&&LZwwE~9{TKE9_V&2L2DAO4N) z{VXy3ZDzTiEvNf<&pdZ6-wfYpIZGL$+mP2h$%c^x(u3`88gHIxK4ETPTw#1+m}r0`^tI-c^~HIupzZ~`78)2{wm?9LA!u*Dq`>k1#r?jTij&#OOQE-WXU^Qr>)9J}$2waI>(qbP zo?wfcjUx;N3=%=#5p|{%D*h>s74Ha5$dBlU{pl5h4q`iCFnZvBhoLCis-$PrSAIjH3Gxm`6K&KzHEq^Gqy(`X1N5wgEPR(O1YoP7Tp8!2sFA2O`J z>m*-DxtKO9XNeHNB=ENl&DrI6b}f*raUxntaOZlKdVWg5sM0UT1)7Rm%>mDX#r%Ck zTL;w&XyrG-G>3U8_42HAPIUHjZEzp-+88>?r2Am0QI%$GcOws`N!C-=_RbQ)5$MV-HiRX^E+f1fQw zZ2EAyt@vDs5yQmd@-oJatf_8YWZ7;WXc))_aM4jM$ZFCx?0 zSmWQS#wnAv>x`T0X6$2ZV0_3Ugkm|phGG*%ZyE1Y?_*!MbWt9!2I_yae;cn`#@agv zYztTr*fOYczPI)P=6+m5wXJuqBO=qC)*)?C+Sl~U+1uTVeDB1nYBu9zm-1hX*_Hve z{nDax8bb8$u`hWDT{cTC(Q%~J^KmQu8>`YiQ`lBv5Hg=}H>=^m`= z_0ozmmAPA-&aUHU8Q$={xLkrgUF9@!u5?$7^=@-1XKO>h9yI z=xr#T(^m6ct&#rq0}JOn8xZ6dYVK#a&z{j@C7U34CLn(`$yviy-kmK>)nz{0+|_o# ze@9?u;I_cfzySfZ>;Fg-hRE=bw z!ECK{pjp2felxMkdhbHl7uN{Hz@4NI3iiiF!`>^#=7#IsLT)&p#a}U8H)WfT zSyosNT2`9EjqlhNx?MRgUsh(xcHdRUnVb#TWgRU&zr@2@Lw>xawRM8+z3nesA9IZ1 zC)-JTB|hoH^g+=I>PU`{jlG4TN7)fd5@9hC+T&i+uq*p+3p#hqP`J`Jqs8fSSr~j+G6d) z0@eg31z7z@S)P%1LWZMp*8Jbye}4S_?0cPGTQlCemndJj7uJXV%>#l13k8m{r&-z< z%k%zRE9RU!PYG3$6n|9f6wyXNwH2i;W(shl4SP(Y>99#OPA~*=95Y)jCP}_!z6svP zo{^pxp03_mzJ}so@+xJ%S^}LGfPaSK)=BoK_C#9&()*=cMJ8R~iur=8CWHbAj zoku?D1-0?&2xXx>Mq$*wN`Li$c1-Wi%+wd5mNHmBq5ZAymiJ4ok?Yny0YyVt9D-cE&LFcO10Gfq=PZV!di)?w`m=}j!f1L z=^xaAKBISoFh%?&iE3k1&u3v3zozy`orr3v1$r(p2zMsBO>xUYP(|&H7;WV^r!M$F;pxp3G!>@x;7SF zz>1SFlCIs8>xt*Q)4dmkxyn>#6Z?#N#9iTh><7}B-NV-89uhlq3qI2zC&|;)=4xxz zpyZ12!ZPu*vP0{_bS5p>7g(pB0X{JTYL1emCeuMbsrFNfU^SH|wRz3(oKImTCK&Y} z>rl&BO1q{$#b!W}T3N=A+sF?x`Wa7fvzTV;K`C3z5>q9&`bytMs&koq3crJ&VsIG# zG;T8TrkchThN=7v*2=8YH2E9NlE0O9WtW|eWSL^BI9@m+_z#2#rDh=c7t=40(#1T&WN@kz!% z%*{>Bjb{u-Lr1O+xeG<*O?8u68npm%sHBc0-N-0XoB2muCW}H5p|_xlgXCRmlC~Ji z`&0TARND+krD;0z9Q7QZQGxlEM6wBF93ugL|BS0q+_qPjv^ zq_k8jtK~7rPcVsk6>WjiR4yhh7q3Wtlr5;Fd9J6TCbS#so!6+%lw>tkpFyhg_02K1 z9k${&yEVXcm3^-DkP3OHyF0@}ofm7$Yt=f;V9dhN>{D(Lr*q@D?Ieo%Teqt7r68$= zv`Mb3q$oF4PHU$}18+HQDwp3-%P^0Bz}8{*sFKt`+9ig|J5jqMD%0h~m_3E%2l7<) z94ZNJYkg61P(zsv>X>hQjw*F~4Cso5hUQ6H%+-R&FS@pihx!2-`iPE77&b^F;_%?~%s_-rU6e$#{xu$h=iQDo3#nCCk61 z-C`^8wn${7cAuSOx@DPY&9VG2S2MAO)7(`F2SYP8O%qlsiMVP4Kg?Tn4oLQTsbq&x$yh+0+dEzObcVU6#l3gba# zNk#vrtijhK9}TZcV&W6 z;*ha)NUId9R)87BPUXXmtf{zh3U6kgF=zC6ElU5Qj#V#dhnbqJfh)pks9O$Ty6Yve zK0Qb2rhHQjnp@vbnsGh3hMYgvJ!_LO49{f4`hKEjryeS{YQq*gq1LqqR&RFe*HG8; z2)zQZ6P1>tRzh!!RaoC$go^k$XkY5;k5OG-26bbxK;y2=A~Kil%$GF2F_bq9;+JqW zSdqbAfND=AS}`iYN@t}UK0iyjFTE7LdB=M134Y29tuA5tE{4&DBgQu7d1e>x`Jmwo zC$U%ADE2KWLnbi0^;E61T3#M59`$wf?G!4@eY9~TohxYEX{u%3Y94PoY8cLC=+|Yl z7%4RK)$six^pzefSG5YLrrHnhGz+o97`7o-mpAZ)-^nd!H<7EThir*es$g}g`d%Bt ztmPIMi8>O85!CGxD{C0~*s!G*ICe>RazQc77vibv?&EIf+3gFEI;tMMK3m9;Ym6{k%qvXK3|CmFv9*s%ymCpU zdad!Oqg#r~|C)$F^P?JTJ(XMI-(cO~y-p{8JT;cQGmNsTiKq8VJ~q741)~`{BW)r8%UtbR-4H|@(X!^ zGE3!ALER1YreC19Ox3bcfeF>ZAM`C+8`xuv zxL?>Ny6^-VNdeLZX{}sT?XEW>F1D@Vj;V|#&HTf($}ok^&sv+x6iH#Ix%FpgU+b4pS!bfJRa+>HJ3~oZQm;)GvHRKcYgpiCis$nm9y#ry(0->f3U;3EN(91$XAB$ zhFtDD5%rsDY2~bZ5xi)F!lJXoT)i%d;-2!=47I^{j)K)(SL(^r#M0swv8c?V+d(pP zCdajAOg(l7SB)>ipFwPzO>$86I2GK{rH_O{L?wIJ+iVGTFIj{&;($E$+gt3@yG*|g z6~c5>xBdxUP!S5MpA5_1V4HFExxU&rLVeB z|3#Yc{S8A56AZzIRs00*Z#I|&Ah&W{c_FWmK8u>TL%OUa=xbSj!(h`d%K=-wEzssP zuQ7}v&*Ix7lkn~BY z*)-}V%)K;q0P6CFGBLpKPpEHiPo}d|P}R_o-^BgPa`2=Rln7~@&_Jjpvhp9QS-;6- zkUnfSYvu3qS=<5E#M%knKQ0tK3Yc z0Jrv8-v!0ZFy#HFay);JA7`iopBThKHK{a}6ai}*LYg!b&-Xmp$qhoTD}}p?`7wo@ z1(Rv1T?C8YfGWy9s9a9aA2HuaN#qj3`4?OVt{WSViq@H0SLLg;LgM6Ba(%2+uR@)0 zu2uz#3`vh5Pr$E}aIMgnqdwW7tyaro29$v6Q_$9-j&rB}5S7o9ff@%eFQQO^UmR5$ z{R}@0*{HXw%r+(qQD=W!Z^slS@#GNui*3lxW)5g$lxQhPJTB&w>mjcCz}#S$aQivJ zeI}imXkAv1D#MglidW6lf|#Dj01s!Iv0tIIxvM9lviSqF=FQddYAbb+T2( zU%2DWdJb0FPk}A1R{WH|_xk8@1BYwXNzaM7H*-q zfd$<_b@pt%1f!w8^$K}~3gRcQKo#dnloffsGDv-fJcNatovLOVxL%Nwmn9l+7A?pP+jC0vXOG zvU@q1I{*&s1qXSLdU&IrspeNtD(&&A13kcA?Tv0mz4txLpRPGn&8(+i{!uamFSl4XYS0O+Af=_z3o>eqQ|`zmUpGcB#8` zLQ0n6w#!)sD1{WLL$|1 zN=al(=j!1k8@bX4e0468Jk`RL!Qy3Krgyxrh&Wh|*8G@A_8!-ePv)v~&xxv^)OfX~ zqR8D48(vpCpf2Z}`d;x@8Yqd1OOkd- z)KaoZ&EyQFwf0KC2);j<{RyA=iTlFc<%VV%rIzu_G)CIOxf)aQiv@qxO_e?iZj@`g6M@}yj+I{Szq_tG1 zE0^Sl;0@!HVd@rmgUw7V8IO7U05*^V-N`P*{qK?W+JLLsiL3Yk7Bf}3BDa%^$vM(y zxwU#w`vN=Z$`0c0Z~|A0i)42&oPI-XrQDS($RTn^`7}6PtY%=!5P`kI&E*t!1iO>$ zL7$D)K&!gYk~pCdj90*4q@wbBd9^ZIt)bOLZ=fjhoh)ZhBG*5HvFN*jVa>pvYof1& z2%WeKdXSBJ3H>S51<}C1o%(e2tG1J=Xo$L3y{7~yO_lEO;rE#dTvbD`@q}@hVFyRp z>r7!i0lsIdQdN1Oq^ftcg+SATP-NKA&*(LJ>{ozVygMq*YCt~`ipU^LtEsHPIL%b5 zYTpn!?;s7>j^H?L*fHz~QjvK8eO^I~=v*iS-atXvUi$~$ZH`6|Hyvh>yI@h@#Qwz= zhRR_L)EY0K7yYTOQl`m`r9@Ga9EgE0X)LpZXzYFdv?1KEiNo3}kYI~8RUN4ekbg;4 zZU_;@_9kW2-Sy%=pOdH ziAP^wkx3yd*rRN3b{W}-c))_aJ9Vh1=jeTa*!R^EY8$1XVg^sSpbchrfjb6qJJ{3k z=tK0MU?Vf3VR{9v)+H#Swm>DaQAv`ID33Av>(B*8L^rsNI#oQjf$at<-Q|_?J5)Sw z0sjhSuR;BC2HN5KxDrmE0S#6GsQ;^Ajie_OWFxVgbW>p41D*Ed^fJq!9k!CS(Ee11 zPyPcV^Z_F_1U~V$J`l>8O^8R@Am%y-ef2L~Sr_yY$PZ0UtGsSbZ=sC41f}mZy*WJh z19-V-u)}M(K194wHSL4SYY<(3)u*6q?N+Eg9%HpNKVrInk>4ANxmgQ(;}>`~{ShVZ zMaO}exQ5>tr>{`NZ$QoCV(lSxDCtVFvPm^S#nMzi3T0URO$!a~Il!U1L zPbgI4NhtI+$ZoS6NlEe<-t7j|VcnpzZx5e68o8+gP^}b2ZmTxXJs2G8PgWwi$V}y; zVk{kVAqDl+f)W5F{xQ83^rOhyl4Rs(o~dnsG$Ym4sIIOI9<~`i`U_^$Oz^``jDgvV zdASNI;KS-Lc+O8y$=y|p;b}?QOuYhAkh~|`*!k>7@*X6cVI5v zP`%1)FrJa>-`Z7u0Xf39ta?U2mt2g2h}^`>K`IE8sW#fViD7 z`+h++c2M5}#;_Ea@`ec@UvacJbXPcxK2-D7e}QQY5R;fOTW3L2KMb7Y7Ibsn^@{o( zXn?x|dk2#utU@+I72lMJ#})5T%VVwQm%0hcq$fH7Q(KRk^{&vTZqN^)uiyzgX(hcO_~9hAI(lARQO|1k(0STOMv{NYadMLU z4K3PBXh{o0joJ{XNcZQk!^$s#Nsd%9WnXSRX+T|%aacCxDmrP(H^{9>7>;7EgjT*KKO z>_PT9dmflw1FQ<&xA1gVDOpJ8$#P94TiJmc%6iD(xS76Sjy>3K=u!0%%HTs_2xGNB zRTHeRnEG684i&;leI<4}*aCI+HDs(qfavXkmL1TeezB@!#jX+t8&X6{uBEUx4mP zh0xh;9`vRgq3EP)UfR!T8c^>JvV|k@e6At0)&>0$PQWfcV%=d2a)evZy<@y)2maCy zTZM5i9_Vo6@d-Dy>k|=q?SSsN6EgC>p^!~P#Kmhzq065L<$hyy&-jEa<|AnGH-l5{ z!rZBiCzA{A{11v=egLyKXj4$NS`du*Exg4mH3FQg4{ZH3GoCacPoX(CGxxM)^iB8$ zjbCYCM|b$a0C2d1h-;sL#l>T-U=H$ZVbB$>gssldR;tgG%}PqXGDzz?0L-N+@y>H;uf3iO1Bpw=$}ZEq|zh>x{-usjX>|D-bGU@;}2VZ4gi zp#if`UyWW1xoTmxqFNJ(R|Ll?f*JP*6xM_Ay!JzFyjm*`y=@{ab2?_-X7H>wSk+pF zeNR?mzAT3I^g|?B85;F$Ai^iaJ|jpP6q(mC-&bJ_1GJXtQ!o^I=WkjAD2bk6MddM+ z*?V!{20WQbu#Z2X8b5_hXd!s%bLbz`4d`XS5pH2sxCOMprBMTO8Y8(1_jneTWWt@t z1KSrt6`KGnECQAAEA%WFf}T{fVMVm_PhaS**>^y)Q$QfPr_y-L-}2D$jt2@4BF~{T4I+Oq$1yS$!6WCQ8&oGv zLGK(tWLac=0pm{|la}lf_9MH6-A;O7U%rOO#U!bn(b2{NEx4|Y$A~t@PDdBfZTA1r zbQWM*Ra*n51@&4eNJuLxUiBAI2?fQzDvBUTNQi<6(%mW`-6=iNHFSgIzz_q{-7s_u z65ssqyU)GP_k4ZMj;ufk*QXRULQgE}5Mf1YuFOn&M+tjSw& zo8_s-kVTsxzS<;ZU#v7-t5C`Syzj5Zrec#Xjjc&}1zV*pQgIhjZ3i@33+}fAJie3i zEP1(|i4m_%KKeOoYn{ete<`IA+S%8sYhl6FNj;4$-A7aPggf+wnqn!1kPI1t!DS%3 zh1vE-YkQWOluM8cP2ubl;jhPI>&V-lK}DL}@ZV}sZ4>G$CDHQ3=!EyMhJFXP{w79! zEY$$6A|b!yVTFuu$ofv7ayXWQIw$L>;PgDOSi|WKlfm1YSe_%08~MDCx zVq&K-`BZ`GN$h)Dd?;26tV2DHK44}$s-0AVUMhi0?=Yv#jH4=*7w&TX zBJPMo7N24+w^@an)QflqylsRA`61Xlp0P}#Dp@bI#m8bJ?;R#~W+=L8CF-pVM$4)L z4kb~=YibzNHYj9Uu=cE%Su14|dcZ-}w~WqzTHmjtryAiAIPIE-j$D#O-r#IKcx0c3X;1KT2w*SdpX>-7&5Lo zx$wWhQC6X~mAJPK1RMn|WQKxvQB~|Kup|#Mt4_*qaJV?7#@#H~xwY<2!qZ$MWjK^# zUF!rYi~h#^%L47e@T%%y-iOo{IKtjdXEO62$AT=wzn)T*6-kmAi}@Tl*@Ib?VQh7Q z{Ksfj$I(0cKIEe5%M(<^&Bi)+ z;>5x7@PGm64F7;1NmwqmV^x53JM8d6XcnKNRlSxvf9ln#{zT(|Z&6Vo#(4V!*)=I| zLSeP2;`I`H4i~86beJ{G3FmAF7rVmQAX(XModGR8k44)P4`O?4sP5ov9Ncm$w0V=c zSYu@pbFD!IpR#;QL(Px9;k4_P(0P3>PM;cXm*$b@B+Fw6;jAYxtdOFvBIR{?qJT#8DZm26HSEE6F_Lz`WTh)u@G;1P98L zx;}dJ2p~KSEPDnyaTr{x$)gu_(v#R_s0+Vo52ue~&OgC_+EFoNK2;s^vPvD%!M;ef z3(3|Qnf*9C_c>(#o9Im0pn%2D_;KV?ejt&FGe^#o*PaEqk4*JJ>R<5@ql;4Gp3L)df5UdRIyV*8Bi%o>`l;3L+p<^m4KT0B>by@Sn4&@ff=2 zZunzcU_6#uP3x)Lbnad(wku^FT===vr`a)k6g_we5W9nK;vl_FhkK_`Md2*_6*-vs zChGd_pf1lK_)9rPk%GRI87lb@tQ!q4{TZIK1h}V1svLk;CQ%*eCVuKPNZKNJV2e-} zr3SoeD;m^&vEKJyL7(nG+)sS%iMhY%VRiCv@ z0IG+f&g?1GV=K_cPTqY0OCS?FlZ~j-au{DhIy9=Jdw){Zs4Enj9P>2Qv$31(_&kW6 z`Ybg$E+G>NLbDx-YH5i~*$w8sNcF2~U{?}rm=Ryr=otIQK>r5%-Uj&J^4PPi-W+to zKHQxjyE`5B_9t+I>u`hVv7)i-_g3I*&P;`@cF5UbaM!fJy*+IgN2`AUO3p@q>)6pK zjQsw0$})7UEZ9Xo@0Fw`R2OQ>e zgU9cs){_331YVWJwV|yTE4F}NJg49T_}VsBsv?@vO04=;R98)l?ve-wB{HV>;dd*6 zLk6mOltV_{!tc5O=~N$ix1JTR1&vr2DG6Wq2Dr?8{4-Xx9)aFa9~>XYAaKHEoV37N6x@?*JVV6Vv&blc+#8=ks07Fs9v)eb=EUC=YEfb;@%(TaLLt$}0$ z)kv;^WhL-sq@}V?8CGUI^{C$gW3$2^W}+GHLG$xGI?q`70cd}Qe%lK?+=)lL2$aRPRyrFV z@eQlHjtHbe;RUdIZ%0s z-=0E7ehH2DLl55pZySSjITc$3Um69K7Np8aNg#M1^fMN}n`h0BfH%b>frjE88wp1k z55K(5uXU-wbRY6F2O7tB_@HZpvGK8gQPt;3r2J97{lbpkS=!nH&9{fn4+DLx_O^s` z=0<{-;59RP!96tZH`Pw?65nKRqareUF&O+YIAEn+|J#IqxE;OtZMc=1kmuee<4H_} z$}XbGyomSa2UdIu&^QmI4j}J#1Bu~KY#OwL2hr?Hg&pMQX@3NM_2G>4A+)nOaH#9R zrZC)oAlmL$u%RK^+`D1bPU3ybiN$$`x-p-j58vhg-l2+6MXb`6^q0(T<#SNUvtZeG z=nU(rboB?ToEB|p5}5u9oGLRk-JKbgWUkxb0&k;B9lQ4p7Ttl^K(OBWKUWyhr%>(B z=m!7bXUGU;7J?dS7cxStHNDC`6_92X z(cCkkyY2-SPI9j&ulxEny-wjC>(B28b2rn|DD?bgP)u6*+;8Zoi>ccx);&Koe+ybl#hRu?<9~uWOINX!e?+R6fzNs7>@#R?a>{Sv zlf|IKiqQT&AhC~rJ=tm+vtJKYO3^W>0xAgKD3HF*tePCWXZswJP&^g+zp*Mx1MA*Mj2_f_dJzk7A$F4$vY&*C^Dv`#p_P8D=LGmzS0uwq;5Rqm zLL+8d5iP~4!PTJPmaJqG)_N7RzKW~PAuWy}FBj5hGoaN59px?N{uVeshAVDjAwCMP z{x|aJ8LDUf4j;-w#WJf<)~oxN@BN_RO|1KF@Uk<%<%Pp1qRXUa2l6@QRu(7^rQeP8XFcdgQx0Oyw_{Jg zF}6i9AkrK=dK0p<06eY_bMOqr=HOf@`0`g^$b7W5&8*yE^!9UnUI-49pdC-cd=(sR z#Q2kfZgCd8ya+B0ghD2OCyTf%nUmO_$5P0Fu73v{J;>v4R{A`=JPrP&523hb=sdl6 zt%V%^78z{S$es9dU!W#q_SDzWj5@>joA8{8mbHPZTH`qdb~-$xHB|l}(Keolc!x7N zJk$E~P;g0xm?$Vz*9*D=nkSr01$wnbF$O zv*A%Am`6X>pfi|kebe^n-x-PGSqDc*!qzFtN_Y|xamDn1fmctC-UpXE%!-^2lo8MW zp8vKN*sbC90IRPK_#;p(!`fJ1+Y?YcAG-*8)IIJR2d-GLehOHyme!|23!eEfo?qix zi*3v|JyiWEG};*+pl#X{NM-@TyP%8(@PIga^<3YJaJkd0$pZS`%WscB(^h5wi{IMA zW2?fS>oCJOTHZ~&8EGQ}w)Gii;mJ*-xT+r-$0WGQM#i?8c^+kUuIx#!IR!s{0Zx$@ zxaP%H%!J=%0-Y-an?$H4X)vdB`1)vbD`zwtj8oYl(TT&kKvhCKOX=e zcpiGXAHTy{=y)L-*Wbvc^H8^y4-3FGa>G|z)59>3O7m6hHN zEj@w7R}GEN`nE&iRlR}Q8*rPu*d)0b$s5po4s@Xp;H)*Eou*L5U}$bJ5KDkhO@_z6 z7WS@2!VyN{o!bI0FTi!Jpt!coy*+wgF>IquDLe4J9AT$3o_d!h@cQYeIm`auiF>!P zTsq);Plx7GnCssI|4PFR%Ap^84Xqd9X+W6gC>f6R@3cpn;~6nc{1=g?m#bfTiP-w5j34=&zegg3c<}I z3OH-EoFne`Yj$maVSnfdp3M1ZML)oGUdF!2Nk1PTi>)C42%5xA+DigoRzMHSkP~yk zh2D(2GuW+%@glvYLZW;XY`*z;LB?aZlthzx1746DO0pv5W7v~wD%P;Q2SjfJ-L(7@ z!_LhO-M!7s9_E>W+1VKul5oVGMzdQ?H9L(y?Vtm_Z+4HB4 zz#Xi`DPNT*Wk-hO#e;T-@2CE2KWt<+3*jBJfv+dW&BA_Z2gZEEnp&H-GLP4S466Z# zf6AjOu>T#Y>-o3pu-a_4wg^7c1Z=1VZ}A*o&ocEi%S80VzZh*2Bl3*YLwsJxXphj> z66Eegc*hse{CjYnV(^2f(M7VOy*&wMc>@mf2BWAC1~|%O;Fpm(WMr>rCF7mW7<+)F z4e{002fG@gIeZ5{{v6EH>nfdn#rUP@{7_1EYVB`~<)i-oc{s!w?5fLndir5ERKv^D z3JbA3noVbH%6)Lc+p#ogpjn~mI#8Eqs(nvijlk%yX!~vS&3m-5lUSu6*>i8j9#(HO zmZ4}YBk^T*p`v&kzj(Ifd~DP;aFAQEEO6X(*lAhOF$&W{R$dFh_X@y`3*h}J0Jk`f z?thUHEQMou63}LNuO|u(!6vdg?XFlVt}6gsim@`%mXO>J-lhlN&Oj3jp!HUP-fFNG zajfb10Kb*2$YSQv8W`1Nt=?xWx8NTavAWjK<27dc5`1eBYqTDm)AGKbHg5s>75p{^ zNv6NPCjDA3yD!+Yn#VG@^Gx`&yP5g0c;!f`nC1VW-{e6SoB)!i(fw01yEo|J9iZu` zH-QoR=;5~Cp4 zl9d&Hh1TEZ`#a3q6OiI*_cCMm+<_DHwh63R01p|0HR0)upMfpkgMUfsD{?n0{?bL zc;!!MWZ%(V6LhhEZFYwtDiN=B(o>MXn3>pL^Tf@IgvsPL0Hst_6 zo?*q_!f*33T;NkyxGJ)^Ik0aJo(>I~OB3j?Fxqm7z=86CVTGWWS82if(jk$v(26Vn zCS0H+cdGR_g@aXvLJC7CEx?FY@U?HiyYHdpkHD1w?5llM^u4`ZK0Gqdf z>l<0iGjQj$Xx#-^?Kj|`&#w1122A`S+ZcSrG?HS{*U}zh@&Efw8;K3!Xz67o(fcqo)-yLLa8Bg_1lX46$vjJ~P zQ@GSfVgdTXp}N6aI$S6}Y#2u8dQ-zo-1J`5H;#WR^GI!};G zL?2FwJqf?5%9wh=3tEF;O_-bYwp)QsU(!osd`dO>{TJHn3vXG^__i{uJ&gD;*f1SD z{)YD72B)8ARXsDvbJCw@WS+X0jmLZNwVFt*AuT(Y2T zJ%rYI1q__b%Jt{Yui<#=Y~6v-BB*y2y&0Eq8O&VEm=5z;2Yo2l_cO~4yt-y1z^p%c zdKy`G*1t8aln8c61t_{UqxuRy-2mEa#JcrhhHGhi9nUGup#!u34qASVX9=_b>!UXW zzD=R2HE5FaY5Om*j5V!SX&F%UjPS|O!UWox$GEoA=SHsj3p`5-9BdyeF_WJ4 zBWZu11XnIV{aKjb4Y<*ttZx_aeKI`9Q?YtMVGZcN9k^T)ynh-W>V5FU`{0b_k=hj) zT|V@sm%!0C!Hm+-)sIkcTV!WVR&6qPHj}lS1%6kBk{a^=lhAou+If`~EW8#WeuILA4*AFt|X}DJ-{`G9Tezap-BU#0H^so>b_zV19h~AqF4%}qD zq{@S=?l$@xOe+JKXGyr;KD=L>8Rr%F^98t5G2rnbGyDwde~WLA@cm0*GXPl5VtgB+ z`rWj03S2)0o}XhSlfl0;;Pq+x^(-a55!b+wlRVt-%mxN~(v~L==E91J@$Yf&)^77u z!gEOK6u#eKmsff1IoJ|&_rj7pGE#E%GZ1i#Iz4)6MJPIFj zWapu+jO-~~g3h#mFX2nQ#7u4n+RVg%Da__fxX0N(geUd^bkl#JSZ#vJ=q~Tz`T7Ez z?On8`7r7!mF{+#CF^L(SW~3)*?>HQFH|xEOQFdiT$AT?A7+qZ8UsJ&o&rZC-O6cKE z1Y*Y&}op1!$EN1QEkX=2X zw(`u5XjrtOvOM31T04OSBlvGPdj24KnFK84P}{(V$b&ouI8H@wO%Ag0Kg{(RxWGj; zi>>%*hT=aN1Z-wwTU^B#v>tt97EkSi^hn(NaDg9yYHMhw6Eo2loB-VAgF6`AczT`} zc;5&hF&H`?z??_&I*jqO2SOw1yAi()03vHyy96MW#HZ*K=Ov7CCL`PcoRl;xfRPxK z25*3rrj*^xdQSug+$Z(irFj8+JxhHGGN?B#YipE8U#JoEfcCUClr`~Oox}9Cp8L1b za({jwz#4v!6mAQ)H0CNj26|j3b4?O+n+5;v&4@jl@fSvHbkuBmZp*hWw9pm^w1bz9 zU`=L+RhJe|LT~38>3(E_r-OQ0;9>ebz&~312|%JJ-xFz136ubKPv;*`09+Y#feFm5 zGtW36^&6w?fJ7d`s1pKRPh(6?;X?gchtGj#eRxwN=Gu}wngQqG%(Q1%{lP#s8J|hI z;K|4dmU%AVAtd5<;QkxDVkjK52W$E{Fsq49@;bQP0f@8!XGhR`NBU^Q_qsf5fa8nc z#oKtOOUz@`ar9OJ37~!IIp{sWi(i@5U`DOS>QC-i!E-VgGKDdTiz|WA!LaUIfJqY9 zj7L&6qov`hel)fE*mtwpJs(9R#zMUHow3_S z@_i&WTz_oK`uOhSuo3%)-6qdyTgdgpxVmT9LGQ^w9fB>`8~^`My#1rFEsHRs0!V?o zL1uW~%Qf(74YMB)4US_C{{TOF0omEWat09W$TvNP`h?w0`GaS3FeD#bECbk*2fuO_ zY%=ZX=5V(U!6Em%-h#iKhw3-r!PpH)I0GL@juk@Nco%)41FJifl@aE1z^ujeFr9T7 z%6k0(?R5b^w5f(b<--|UclsX4+B6DlvVj%f2uF9XR&7bHp;nndYm=b_<&K_askab% z#QVtF-)KP{dl>JZV*6EQO-q5RpU|eKp-uoBqz!WeJn23&zKeh0;16RNJ;bOz_ooQ6 z%mz2lLAJ>z{74US&2iqZz|n5To`=(?fzv+^6y)+>pfQyPe_rC3?8ppHTe=y0Amui2 zcmQ4LI$oxmv8U0L-sDk_nL7HV0V<2Y^LR$v94!2hQ8j@tS7D@W7`@uD)||Uxo=f@* zFlmT9tIk8Xmu7A9;kD0@au$D4S~QU@tilm|PI+1mm349Y+F~;Yy&0SG42&Vwt~M~;EhHLT@TvE?a;D& zY{$6nC|vX}{@2n`CppKjhv8>>Xgo)3H$AJ1#CTuL95yh6M6~XK@T%U7dI&RY29)~K z-hS@g$WtrfGTO>Nv~UI5Qdio{zq4uWJpB7Mzox|xl!7Gso6i@SK_)br{MaYY!wnii z&);LCG{P731zwqQyldrTMyI(*Zbgh1)DP3sMg};Gr@*D>*VK3`(qP|ZU@W(2FNxXifC4u&zs;;qe?HG(?fSttM?n9p zfR*Q{pG5O@ci{-nlfZW=SI4o!Tfy9AKyE9mI)oJ+6k3=SH15ho&_XM&HOy1Wr=bpLWukb2(+>GTrx zng`nbfN#dx_J=-aaYYjAxRY@u0EtyWXWjr6{tAUwKsp!+Z*I+tP*ZCBo+qJjHNQ9D zK%XP|eh23_z+I=)&ML-xgr^#hddMG0C*vpUK=nT%3#CXeZ83W;1doe?|s4shUx z=n56#MtY8OGs-;BZ*FKmFJmYV&DP{U{cXkgR383U39j0JR)+!s_fcliquRq>Fj}8O zFX(d`cl2ca8o+7FLdS*SXQ|*0sqx6?;x!LE%X81}#GZl^>W8h(watOqbXrxG8q2Vc zH9E-Zscr2HtD)Z}j(M+y2djzHX9oHSrl5Zhqwh&z(MI0YhsHxq?fK96hDMCH3ml;j zkeJT@i&(oEXaH*hKE#2SQ=!6vXs@1FmjPa!5x#v5X|oHfF%`Ua3w(JCR#*}oDhW+= zIehOVJa8M_@F?2)dA?nMFM1khR_2!%t@Je@dXnEV@OmDZC8vA?){6qhS93=Xam5EtEUT=*m^(p)u@vlgv+@Pbq7JjIxI!y)9|7kJ);gWqH>7vcD~;pOfKxUXuwOe)6fr&Q=*cWC_{Be=^w zmw7+OsJAhPO~4`v-^*s&(U#Vd&dz*aA<{-)@KePn-4$e&2t*uWdN8|6`t`dSWz9m;d#g{d352QpOMp^(OUyv_aXk_7r~nnV9kH< z_15BEqb=spPXc`^!4seky*#s6qa{41^Y3`@c^=q3f^QwTrzZT;GZxjaq`!1{MvQ}P zh=!=-na3KeWYpS3+juvwqa&}sL0!Y4rtWAy%@|W9bf{Ak_#+seAT>_KZAMCmRG! z>CG-rYsG;?As|r*URxF#D-V>~0Vge?8uax#qsajOd;o3hDsco?iHBk#WA1&F;|e*s zM?7e2O$JiR-2wF5hFR)iZV!dlpqDp+^qb7&bsnBOt@j`&&{B7Ni)Sf*uS7f6oEgN3 zhcMb|^#2L}6+q`qi_aoGZF~$DE6qIG0S(W&9SoNI0k<%3V$? zD-S6;860$<{5t-mbXY3cc<6OXONPQ(uJG*o&0y{p}hn};z!+UlPDsI1hxw1oE!;L<`yIVE8E!~n;# zNWQO`V+B^B3cSr-&i?~?)qrE0U{|Qu#KW7nGB;0IoD0Tklg|gMmV>n$>2C`dyp`V< z(zfvt3%O%9{i?qvaPJy8i?K@5qLdheyIg}x<>YCI-nq#w?cWHT>Nf3Mhg&HluF|eLZYFqY3hiFwSAFsag9daF z{Xvhhl)sL#$@|n>F95Go=&9?OZ9gcyGxOIgQ32}Jif9e?wnlo1U+usZz4x=A%<15R z@ml4fNTY>rT4Pwwg170xxr|7Tg2>9J`Tr);@gA6!0S)14WN>z9tvp!WD(J;V z=X?+SeFt8C&bk=y{WcU*jZdZFF<*kSxr669EgDjK^osOQiE(iSxvnU9`we%rfzr)J z82~=702{Zk@^Y5#%u{dAQa-ywyc``vpQW>E1y^Ye#~REE$MGD?{01|#bpdY@xNjpG zc`|KngsYt3`)Z)-Zj1ZQXMv~Zi7L@ofE)dhF>wKcebC@q0L>AMW+3z__xTOF=|}5o zi2C67(5gDAyBBM~94%*ctI0rDT~1!B%}c!~#yWu25n645R8!#8nAK?q*Xtj!)Yvus zz9+#R+8nO!~LR|M^BKU&TO^pqs5x;t=}2e3TfL35IK%?#u0&-gm9vOUpK z`@v-r!o0SCNw=8iJ#hCNx_1VoAW3xea|1Z&KfcXB`H^J#upn9ySu>g4;?+bJb|c>A zTjbu0SQ(FS*DY}I0(!wARzqkW=PCBz#uxGc-pBNK9Nclrg1+Nv&;Nkmcjya2#=pw5 zbbL?4^Fe6nDAZ-lzuxZCp{>)5=@`EYi9NiUIdOmgCa(0G`$ewr`Y@}OjIAs9I1oA+ z1NOv$Z_D`4Xy2J&;Bw^V9I#eysi(CQSl666b!RNnOg~=ToBjg1`z0D~2_$JrK7Yl& zPG7LJ3;zxT$MwgkKTHOrr@#}J(wj4E9Pq6vbLw)>65ufcp62~mo=${eEf_R z`I3I>K-1;9z6JkJ2B-fFoY;83jf{ON|MaHc3V}nlVVtvQQLm=j${M~+p!dN*uq*4- z6%NyYRjzrgk0q32x4f_(U4^24j{Lnc7 zT;Sx6{$^HS74usFG~Lx65nAm@50&6am5}vs(Lx%yRtDtiek=;BubjoENC!>6h=f+> zuMC&_4qjviT_alQ%6Nv+-|x(2XpqtM_}&KFuqqGz{}sU1Pk9#M)3?C?yTA{cGS}6} zM>*1Vp4zuY$%qf@XlnxhO=fL|F^9f^@*A?o#w&HBg(j>(OIAh>HIOUJ-JQm~;)7Le zWGhwV1MQ!JCetBrq`pmHuwO3r@vM!oFJ#0C%=8fd7&+&TiN2tt@UkO()5m)iP7_1#_Jrz8?5EuiZ_9z^pA}6g zFQ4*+UmxRJdiL3~aYa^knzILQ&msDVWb_((a8KJ9PeZV@WfW>%MhTzi zlRH?KfvZwwHjp-h{8O-_3=%gNvNH`hcp{dGZzZ5Dck){Rm!>?Lf&V`P`M5w|N+wTp zKEMp9`-y#$nb;wB)6T*9&(mLwxw@ZeY~cldOT~P$!o`cQzxF!bsej|sE`~&KXFVs@ z%vB_Z-ee`mgIG_guyzV^b#Zp&zQ!^t!ERhpyizZ*+x{s3d%AcUtX+M1!a14#c7P#r z!0pVF(7n^D^K(3AlLncMm7R|pif~AJZ>piH-PnS4&JEu)r5B5 zLeG63Ek(UK7ZUMltc$16HJ(BnPXSUHkPSJ}WXuYv1MU3+^^69e+%YnOM=NX}vpEc` zjxef4P}pF&i#aRCue}NEKLXaja`#VgM<0EVE*;=Q^Pr09V5oM`dazAQQy1Gzi)PUn z7ru{HcLIe2%<2H^Ed{P(Uc*?q-t^F$RhIKMXB~gyw;r@V1-*9|BiY2YyTFhmw6u#> z+-Y?WW;MO6<@*u78{@v0HD3;V6W|nk_`H;H^+m=R)8?+{0D9>St<2>&_qpTgPb;qv zur+>RJnIn;M8@-Z67=>5-+F^hje(juyv5<+ZvfMmdA@-EDSOaUa^Wq03{LzQ_ov{2NsC`S3mi2&n&3_RFZbbn%1t}w zLTyCL*#wW>1gA}n)pj0TF-9Ayx!zdKi}jP*ji-9zz5Cvo)#qNi@clS{*o z^YB^{s;P{mXbN9!4IJvSUOm8phG0`a<~@g*T2ZJM6!tCh;uS22!p!XnII@1dw}XYz z07#91r!QbdW&>*_@=|DX1Gu~vh^>OlO$MTqfY<~eYFt7kaPw;(Rp`4y(7)9Ieu76g zU_8ID1`}C-E$0>B^(0_E4UFptwCmGnwIIdh*;WgYBGj|g^$$T0>YMA3F5Tg>Ey3g- z@XglDpcVMpEbJ@%&V6!^O#u_-Les#xk+i>**^UciHDf@1L9d;f=6*0q5BPqtbq~L| zb9ER#WQ@!$bhs?gvV1Tt8EwbV8h4>DUV*yxO6P?)7$0b~HjP|y2{|?4& z)SF(GE8u7vw8DSk0gn+Mt`A@DveaU}SsW5_NT8~*(A9FVdmZCB1ofO{$NNF z8UC!Y_@2t}OBo_5>*JFj&(7L!?4h+Fi>5tKJ@wT?v_U?6I>tk$h7W4f8vB=motFZS zqgWo}JPGu#@ykiBO2hopbEP`~SNWb1{g)Fwc%A}U$C#PE?gPwG3f3M~j_wIuV-qXX zhP6tB>&Z{_i*KM+t<9sX%r(~MB%Jab*SlNn-n4#Kbw9nl`g`@>>$|&+#-AVf7=Id# z=nmHB2}Yel7JW8kSyA9pgpmo&E1`#7V8K2{roY;3ANSSG=2H(ahBxvj4WKu1QJtqX zm{d zI{wVQ(?Vh&eddax4^;5^n)k}&?lz~Eax?B} z!~L`13UkO?bLVb4J(&BllsLa-w4opPQMALSpzw-t_lj_d3OtH4`eJB$>W)zyUlk}y zeYP{SWvpo*zPWGd4rOPkv@7#&$M>f2hD2bs5f~X!ZLFNxTgE!B;i*Su8<1Sf^Dov# zsegoP5?N#Q+~H8AUN5sx=K}5C(5l?GGJ14?#sEyh@D(Yg;+3s;7rZK!${8x z^xu@88-?CG@#@&tGMjnKY(Fz{#^!6yp>6va25&Vht|ucH%qSNDj}^cpp1#!eH*km1 zZiDD;He)vWLVy1ZUgMEES`aOnO$#2K;i7eb!XVn4$$e&`?gGD+sm4la)t%totOWg4 zep1iU&!jGT3Mi|2ssG##w3vpB{j<=Fob&|0ssU$*7WL6Qf$ozY{*pP^ujX~#$Ig_w zU8mt(%V;y1>raIC_dt6)p(^*Jj{pVr2B}P&eKo7T4(LkBN8qqV^sHfiYq(<%W8Me+ zRs}BHAN-lc{QIGcjD?Fk&!Mc;0D@N}V8(;# zp%;5erRs4+EEja9!<|WUg}0 zavje#;GVXMm5JOr(2kfuYr~+DKfovLAmfO%t@JcWAAhpW(x~~}zQ@zvc1Evux|V*{ zGL~iF_iwsX81n%d zN>%hBvuCBu>EMGl-$Je)LF>|%)@YQiHwCCE3q}JMJri{quNfQWt(By`a`4pRj95-q z9d1>N8SCY46J~2pT@~!OFZp#8Yox8blJ?c3<)Txe1bM_L_`t95ma1ssKVkQjL?18C zomv3p`2GV8YbL9d4*#s`i9gcAQ|2$cd-e#PTak<(U z;;7ot270%ifzeH~Xx#|A1pe3V)(a*^8*_Xeu9ZR*zzx2og%cTP5Jia|feH>YMztgP ztMBgaOk-TyfsKKHkzJgy1+~otuJMdlJ=OJ8`=1D%nfa=nF^PBYUljiB$2ts#@*{uo zm*Ai|lW(wk`H`9bhxVTX&uvaL@+U$VKk48irr~4}-8BLqb zSpEKJw_noUJK)#b{N4g={srAtdEuVkV7Q=I-3SXWj^DILw1EdeyMORdN(wz~Afw*2 z-lj1Jqp|1n>%>6GbD`gHfufDTp2eqS{9}A@1cgbAV=6p%M1V|R@U{WkbCgs5Dl^Ll zMHj#`{Q@{#5!%zjE)RWFfcoAB9+jY$Dqv*S09$tn)P-iy-VkQo6JA>bc$MSn4y+N= z&4EH+t{Tl%?yI_DM|nR1Hkfz3D@ZW&P{a}AfRmtL^M@C5t=vz)u)YI#G_|}_vHOrC zcq6i7)92!Q?vO8>1^RZ^;qjpVrem+>95PQ`_)5U7zXQkApR^LlZULsoL9U`ddAf0= z$I)b!ERHD+JGa*T+RtcK0PQ8fdIlI)nHg0C79XSe%U zGOcU@Hv8fFE1+0qzB?d#5B3C@$vdnWpp`5qbY5w(5ZrZxk_ zu=(FAPh(}&IDZCf`vHrc{HOk=J~NGHJoK5!`^=zOYLjjOm&}|~tC1HO3nYKq#qfFV$`RxCZl=o0H^h|xa+7lPKb!D#ul&V z-{aVBcj4piTRa(ZYx80`=E9oF6KK)ANwfQ+=rvcW>yxNE0KG_tQ^SEZ+jy zt}kKFW`W|3fL{&goWk1l1{3OmMauNz*kLbW@8k!MDuN5{5L$oDY+${)N?GHAdV@Ri z|0ZBfGrlzmIQTlaSCn0XTzG4Yk+e$BHGC~QV~6pEZ^kY=#WO8D^C4t|@g+ss`Fj=o zwAya%KqW;P$GdoVvM`E*DVNz}JdSr~BhM|^W*3MUy$`>OUK=SvPi}i?r8}6T*5-cN zB(wl69qR-11s*-16t$s`dE}*^65;+L@bBVK!ZYChk6dm20rdc*=gt4nt2G!%D!YZK zl_LE0cVI9Vo-qUbSPh@s0Ct!I{xkPA4|LEGUSu|nR>~Oe8ICqBADs%!7l7q@$m3|+ zm{zMj)n$b48_6xq(=s;N-B;^vYS9S&N%Y}3W(Phzm@zn8b(A$=pEk};9`1gg0FR8~ zHZ#Mq_2$m`jKF=VuDtf*x4vjs=1A9Nb>#>@veK=iGo;aYQW zBe~&x%!E>R)b>3M1w{J}hxlENuBWRBb29#`2d|@n`VxM34|g{sTFdhIKnJ1?mW(1SE*j4y~<5!Zom#AfaB1k=D-XDxKjys0(n9DbFw#>vcR#c5} z9c_5;WoXvye``Ay!7Khe-jdhw8ofre&y)DRuESs4HBeWaO&?;@ROH*PFyF*rwH}B1 zjS1KbSK7z(437&uFVnuBp`7r7a`?_m;CFuv@5?p%Ue8DufHxC(PGp1=0%gzR*KKgb z^V}KPST})Z5@R1nul)jEs$rNhrX{coh`9gp7x?)*_-p=CBJ0%!*y{b%7Wy5D+a4%t z8yY91t*Sn%Rvhh!Xty5YskS177W1yossxRy0tj#Q1%2N}hG(xU;IK+r2%t3B6-OfWu65hSA`=adGa|wghwBEfTwb z1=IC?nf243`(25}+`F4Tto3Bni@7kffUWqd-ZlcL8I3Kq>xZ><{ri2PASHeas7igO z3$&%o)U&2HPM@5zL~CEUX=LaC?$@8Mx4jGSbnWcRe0(X$96zzk*y0&HwW_rB<-_jw zi$RXdJ=%_N%pB;VRv3%&>@UJ&v!H|LCT_bJ)b|nNDuG`55jN+4c;k62bG{cqXUPYrewyDi@osi&g}}3wQ~iNv-vGhh%tOv9M^ML9!&nTSP62NE zpVZg2xva))uIDaZPl2hYfXC9nldOWbm{w-+zMQt)5-iI*nts)ih%_c!S?3#t+llh%#?b?Okt$e zBt_~k+6o<;b*E*p9N3G+MqP{ZuDWs8dQ*-=>1_ItwU`4dpfpA#1wL(j z{1x7f7rn}7W1qYuH-7xQXf~P9weq1U6+qK@8XsL29*p_`d2c_!G(a-&T^)~ZlO683>tG-GX=L8s<<>t7Ii+cE2ij>Y20_p}b&jHIzK zYU;lmSx4i;)fQ%e8_NS$tG9}K@AA(s$dhX=s@^zzhcY{AW>CL@(BS&1fsm#Q6ZyhW!eZ*Ew`4CFA z?wm1O?ZRr#01oDUeGR5WmO*)N<6E$|I+l8Q-qoVM4fygo_+1j+>dk;7Z^4DKV&A@l z4)zw$W;~m7r7=#UpwvaoL1;+JQn@rJx7@{O^~{>twvRR2hb*(g%mwhtJR~X7DjloA zbfw=PP?Hd{;*`-{+H`718@PWz5OMEw3lha$`@P_q5sHt2V=uA`WSpK_%xoyXGc&9N zS9_YB%lz!2-^U|2qkkQGEekcYLIxV?`9shfW)bP3Qjt;dQ|Lt=HyRpW z4BU*I^@_pBj~a~cUHbZhF-o)V@^0)%HK^ANzIv>y(eq+#6IyS^a{_ab0_7d*nUiTb z;%!PheXqvT=+V;;G=-~%27A&So8kc~&Y(E20eY&kQ9DMNt27_N3R|yAZ6xCBiQvBY;wmev zoxvdPXwCe^$7+nN3_5?lfREx{mC#coq@j^;?ipwY>+==>a}<9%FcR|~jft$r zvkZ4v4Hi)g_}W15L@%0gHF`eH0g3$Z(wrF&?q??P)S`2oYCrlPqNo||TjSYI@!j9f zfZOTG<1zbLJ?}EF5l^yyRt9Q<>EV-e9fynE;puK`jQcMJU&T%CxWa!{-!uZ?1Xie@ zF42y)Jk?LNdyNFW5-3Z4Wz7bCWa_ZSnZw+qI8v~W1u>#;TS{uD>AV^6tO5!sg&n#$3bXNt?(p$2?@^gV#UmDQ2SKZ-5-9@ zpQV(KB-AlRWA3{3x}FFT#i_#BgqwR|X7CIQxT2p!-K!z%)fRl}#&aZNk~+);RIcpe zF5geGqkM(=Jrz7%SD^f3%*p8GdqCk36}&HOuXy-Ktv-_={Cek|P#Vdlo0 zxwjX`c+@}SXTC zn?WDifqK3s!TE>qsLr^`Vi!Ncz1Og+x3I^#H|&jWX9sf?JDFQz2RZ+I3y)3cbcsYj z7>9P2@ALU|mRfbEh)25~docLG9gq8(p7&D|DC(~?d&p51M&93vJ&LB79vaHe2s06t zVNT%F-18DwE9!Hi*in!{&f57mt3@#`d3sVDE9 zm~SiSu`9e!f3SH&)(lYY`Lt?*)p)I2a0mR5V&o*3@Pb(3QJlyQFaHq~RkQUt2a*^tz>mnbOT9VeiaUyzh)exfH z$;blGR@2^}4MesfDbEGUQkq3o0S_>4X-ywl6rt9O(ggNVflKVcDTWy~<`aI`e(>n@o$T@huyRZZG!Zj;d=Q zM6dIz=kyq}Kf*V;lk0aYtm*#1%XS2bW^GV2UH^dJTkEu2z!4*%joH&rX&jXN+Sp8a zeceFwdKat?H5wSs30P-M6!QkmrqkP?+#DHNXa?@;ozIPq@-kSRpI3MBmG=et)jCkh z`}cUr1(f+U=)D22U((OlP-qn_sh0ztD8-HU+>aGy?fV?~UX1~LhBmA%XGO;Qc&4NM zM*|Jz;x!lalo!4dX#u&o+PZIANXfAbp}mZ}W+Mv1__QMIFMP(B-$YONfu|nh+DI?; zseFvwYoF!X96VlS)Sn?o-D#;D#%e_UJIvx69@UVmgVB@B>De9dFFldk`Wr6*we8S? z)dFS!$zPzB#=y595FSOJb@&wRx9g$)35r%;OTUr#_J6u-w7)19SN~G(Zv!U}fZfVM zBN^4%)G7wD9#KDf!CJC5)sWc@0=%n*&t>`DoxnOg8Z&O!(Rzz|6pRg4>PLHGiHu%v zys>fBStr-`H6SjhdV|>)0~XB~ZwIiXC%pPwS~EI-8kp-o+A-ve)T-Cks*msCDSZte z&U4VL)qs?a=9~=T5eH^krP}x{YZd4lN(?Q?i?Z^x`eamf=ym)*@8JP1Pt?qFv}h!# z-dp2*Qc?3w&anr2*Vf-cyBnE@x$f3}XpGKLg6CU|UA)&vZq#jRETOc}Wm;%5$}Y)9 zYcXIy>OCmU1g5W@+R$@x3_>*Sil_Q#3NXXWkK^%gIWm9Oo0m=#s1lc zjQ<{9SI8z*DYz zhJTE%Fz46&mdGMgS4n~MSO@PS_NtK#*0HfJd2ZrFJq6-lc+;)tBov*!yv0*tjHxo# z%BU)R{KlR|F&@?i-W$e}0B^NwiG0=w6TMgBPh^eFL8f~ak-Owdb|r6Fu&EZ(?R8|_ zhs20lkNq=#*AmnNX-2bt-d23?g8kD9Pjfx)s0+54HQNN*Gv7U`8R%}$=jhJw(3TbF ztQ%1Ydd_9|b4KJhg8nPfl68GO(dggwqn%z3 z%&!i2GFH3{QoSCuYPE*3VOD0O3=F%d+UHW77F}bw<{+@HKX_+-3->y#T4%P&AjZ^| zSyW-%?*oJK*m3XkY`_@g&AqwC-8`>s%b2Q%o}Y!2W+ckt8ovI^vGjbtg|?9i{XtG> zje|$vfadmPf>Ww%yv*!N0txpZj2|^0=?7ko{B8pzt>4s%PwshD&q3Vjbi*-8wgnAq%?4J*U%k$BmSM|5OhM$nhct!`ANne?19d$ zG$l8%imY5L%JtI9k4O5=0OPG0r2Nx^v^dDep{&_#S~SOV9GJBU-mf?O2)sf{$_~GJ zohZiAR8Fb^-+GhC#}e4e#i{%MF^>cvAsXaNQ~(J7h`;q*E^w4YsUF>@7Q+b zN2!R#i1a~y*K!*pzSYcT!lRPmK9c`ZP}M*oY;y0ZVHu*KX&N_bw)GbOIFw=JME#m34~NFA=qR zE|xas0#)Ab#ZqHE<%9!f#Ut|!^j;WBx8B{~SYWABj&S7;dcMSc)?@jLk*ue$9eg{$ z6-Kf@1q9w?&UKh|b2x;RMO>qL_G*olM5I*bDefB&}K5dSZ(trEvBXob+@O2 z*Z3*;NOm|uK0M7YQ*G`Y_+)8xy^pY!o`<7lhfh7rFR#N*3-g;A;Pd=ijLeq@;HZzn zjnv}u1x}V8&ZIV_o^(A>_R;WufO!~SZ^iD5^nDl4MRwXP!f4I4F2;PG!AFxfM73un z!oWQebyD|;UFG5EsnMRubhL4!B(?y3_dh+a#=QV5`)ajm`(D7`Bp>@5zVi>Oa|W)o zk5&JZ*35=8<3u~({Kt0i2x}t#3Qo2|KJ^arMZdHWGRbiNP26j36ze@4=XW{NL4Fgb z=0mw=mY7dzbdak8;x))WDXGS>0`%q>Le6HQW>v25wJj?{+X2BKp*XyxY zH#9TNM;v%;7GdL{&((zb%Fw2^)hp0-wEt!PUH*$&12)}&pSwsK(Y6zT z!UEcLuWMT1m&OkX!&c}k)!1hDO330etw2~m<48(xcH%e*NlGk;i=45kGW@FxkF&4%1YMs z4=~2+tj0SSl{}x<1w598IgMc?{a6Vju^PaYtTgi>kSUDCRSr1o(^B{OhW|WysTE^2 zzsRTA^m9W?D;bAT4n6q4F?_9jShswvQGP6EYdT+ud5X*fe190d=_S@w9j+=X>=_qt zp=*6izgoKWz!4+v>w#D1Wxfw?eaR>qgRh<~^c@dl`90Cf*eU&ny%>p^te#vF#iAQu z?7qA)2gd0OMg6(bm%EXAp_71$HRiTMPkUJjEia>i6M&#m?(WjMTf6~|X;!)wfZgTr zsqJC>lKVuyTLVAJ^fBw+h`7!`<9p_zPFf$T^X$-$te_PSXVd-=cy>G1z73Xb*Z<;; z6<>_nF^EHYfEo=9j)Jn`Uv9`h( z*4k=?GvU_qZAa{mm3bd}lJ#(FBi;=9&RwigH9+I%_VLO69d|1d=&=hp@C8`lo}!fp z#1HMPx@i1$(RDv%HET1U23*+_$cdlRS%IaDBpTUvdeVm#oe|_N!xTm^h1HqL_@kY~ zsr0Gza*uO5aO?zz8qYJFSuTS68NIt1zGS3~T9cNz_Otf2nwd4atf{K@rQhyRcy2nZ z%P1?;>J3(z+6Q;EW~Mt$yJ%C5OFCM|ujX^?WWHj`3Lq?G7k~xs&RWOKYWl{|nR7Lg z|Ks`OZiZ0^GXlIP1v)Vf-ZsqtF^kWAK4ZqTq_wTpt&MV*d+Q;!8m!i~JDz$Qqj)z@ z|IoL0iK~>QdK%rCD-v+Nkp823C6mzerP2oT0qP z7(BDd>$8gP%4nJSi`9$u=F|fJquMG`jXAt#AzO#sDopA&MuLpt`go{k3J~rC9$4-8 zKj7Zi;GHLI*|KNyxO>+DOl*inZ3cTqD9e1F-&rH$W}>>w!-0UYQNy`T+N{Tw!=Ox~ zy4o>UW8FvsU)iBvYlCqHT(H(F|*@t zK6`@x#I2@>6H2brr{K+8~j>0h3yS-QB=@b8>~VCt(;-JA?cFWHd(P zt)g9bLfvWfM2kd5Hj67eaEFz2-KGC3>q3XOk-@6$TzMI@ELa+E#ZlB zMod}7!5VX(7ho-QPs(!daXsUAttYTr#zC32-vgL-hjOga;JF)C|2Dq4D%kiN<58P6 zxwGe ziv7yM1>B*nZe1#C02vdwhu>WNB>GZ9T4QH>$K9O{4 zw3Rs~E1(ed8{>6WF%EM9-HYi6c6LH1Dh8E(2+ipqvBtpbP+Wazr8X4VA6m1LOn*ja zO;zJmmAE4r;{?Vne_O^{j;H0{10H&APcK>*YmI!=ws5y>A@{Eeb9ZbjxpNJVC^pF) z8e?AFCo&Ghnw~uv#n*UiisE%C3g^)C@*HyYYewa+uA1o}#$wis5u;Os?xR=IxUb$o zU=;K*p79CAI!KMr(AM8Tzt=yS2Tt`E-eS-0Oot!Gowm&QQXa#8%S%7miApmE_er!3 zPe-xNt-C+Qx){kKUsen6K@V05?hH-37jIOuCmompVf7C^*iCr2&te_wpBaz!k@RX= zElNpm%GF%IMFE5KcW9|uLwqc)Eag)a zPuiPx(|4kr>dtC+r%$b7s{{PZ7&{_Q%{!{f`08TCxLfuEBh?e67ynzv+W_flRLd7g z((>ShReOuW+pQ*Nog=-Dr9qc!mQK^fioeK;fP!97+$w^l_{uJnX3vozgz{}Z}c z2F$c~v@6XM(XzJAMLkx~vzW>To5dP!RcT!-zb|-drjzx>k3*|+ffI1BYj6Q~o!ymI zD>jcq4q|TJO8B#pIqsU9>8Nkj>IGM^RP;3I!7YG4=Y`;}c><5Hp6IOn_6i;wKf8yQ zj;nIxVUbT*>D-Eux8QVl;J(HqTK~oDApJ-W;@8Omf6WYkO@%k+Del!XAJv4@e`U4e zqgWW8sC0vG?v9)?&Cv zwwPy8T7Awa%ne*)%{$?7H?*zyEd%Rtn*WoKT?bhKD_AXHHrv4=>$&J#(Oxp=PHDOW zuCg9nv~rY@q(-|$UY!Mu$E>bF!HzW!W*EO%FLolo_6!&|4Lt4@XnYV@N$w2XdFkcP;>h*V3f=5KzBi1=L z8#k(TXZ?X*jLjXeD$thIA3x`wx8c-ZFuIa>tn`jqoyxkARwS`Tr2BnVmNW_~5C3@@ zj}?Zhv19i!JsZ>c3GG>*sy>{kJp8)~?{%T!mWS6LUt0=Qf+V@>iM9M%K1{*3vx&P-XM?xI&n(z{WabA-k$d;?ncjq zp8Kw}?a8-on4hsu_2@@l5Lx?bJNm*RiRIkou-fQmMtX|KOn8GCYdx6J26{HO&w5K^ z_|2$PJ%mwZGp%6HCeR}nd4{9PFj~2rz&iIOMlcVf<;?~3mq-Wu1-R+k)XE$eAmf=| z+8)bzOagDF(B@*EYxuT?QO#qV)(1DfdLW~2#NFnxv}ZMp0U3kVIfz&FXJPFejT9Kq zuf|CKuV$t7DfFS~m-`kDZp`@?yneyM8UW>3hq|o$kKEM?SnDgC0oIxSq_@M!W6#Cg z1TDD}wH^A3s_tv08=>gT3`~V?vGpBBszcC2qj~a-9z&f zDz%go*iz=%y3>A|d#n#Cow^ftkY1x0Y-5AAs zI~cK_{LlG#_T4rhwLS1uTTu#ZWDSi~)|0Jt>x*RR$DE=yGJjaVuad~N+`ac{zvkNN z4Ko+WeVS=}nnn+k`Bxcb3tEll+AU|)+L~I)o&)ME8NALe0?QRd1~ z-XqUOw6|z~>g>0{eo`o`<>&%kT zQ#_A0jCeEu(~M8Exfaoe{&-i!HA&)=l?(Kj>YW$9dL_&*Q*!PCioUJ|0(y_#uTef) zzs*m!V#y^n_FDJ_N3wmyWLm4jPq%hrcdjXLzy4Y6Om&~A=$jH7y%>R#4P zHuf_Ye70QBL-k{;XO`!iK7!tisypwVv(t+fC-bU?I~6)Mi&`&&IW66}(i3xyZyU=D z$8ukk)n%rhJ9la}X3`lsr0-FQwx3sHQO@ytHDG~Qq^v&%{zY-p;)8aYShgqNm)PRN z-50Up1Z_&6*1A=WsdpG>FdsO(M=LKM4Mc{4FB5@_k zrp}1RiOkddExq$vxax~jk(^^`Ny|gOkCyQSR@CSNrAD*^D3wRmfaE_$^oSSgKWdI! zkSykkM$|lw&r7)9nl$=^^aw^?!S3)erB-{sYq98eiQ=)<8ssMKELy8k-7JbySBG@o zMjZZ1YrnF}4baO=qw|-B?`m-vE9cXFaO>@gZ}MmN%6sx021F(>$~C-B<#jb$pm_@F zEBcxy^KA(;wW5xdSO*2^;J&Exbto+>r)$#h4}4N~8dsuqt(L1!ZjGzG@E)@{j6=2V zk~~trVa+M?ydzK89NKoTwP(ON&-VD8{@uZLXLt*v`d^ha^9EPK(UQQ2?eyjjs^@{2 zQK+xnT+N}hW?e8N!_9#9{7_HSb(H26>Bn0^tLkR@7Ed!qD?M1x*4?H=My;iKB;a(U zu^T(37nnGRlbO)j-5}3#(EH z1@q@U-D(NzuN|w^ISgoubx~gYIPlTh#{IzhMf~Qzg;vSv@V}Oh`zZZc5$pF_okQ=6 zS$^sUTCjb>`l*j}3V3c5rI;}e+_M6t6-v}$v|~0if_aR|NP(TqVI5F1E=MS9KU<^G z-_68J08$bC&JR5NIC56$8vSNm@AV*EJlo?8W4g%QW+do;amT}KpG&k6Wm@Q|k^ie{ zpN1b8jcdl9{b;>dpKK~)v@&TsFs3VXWDKmaN}d!Yd?$vHdzO%OnYPg4me5z^x0Hr9 zgBALsN7IsA(|s`C%~UbILc0G0=nMm<(Y~1b>+%TCIx@=D{XYF#M!gwVxr&}w0JA?? zTRpt;9lZ_KIx)9ZEzoL*)`it}*UxC=y*p=Cpx4`Jw3WF&zoOgxg4Pvff}2C9UN)Ke z>6aDv*3!=cplD{gZT8mD?oC*Ytbsi0rq#e)3iE4PLdnN`5D9zO;Tsd=2)THJx zjv1^@T!4j~U#r2^J+m5VM#{l_Gv^ktly|~)5OefM?RMkS?l)K~3W(Cl&W}N%c z+bo5qE10KQtM`aV;Dca-sWmVc}R zagF~j1xixe-3gQ;?Ntfoeup%X1PrZjV`PUrGRk7JbhhzHjmsL?2N;zxchPIK(`Nl+ z^EPHMkLk3e_ry#|V+B`&@qYk0Js{R=R9a4h65Ll9&2{U6yt}{Fag?{YFFlTy%*?W~ zfqsu={41o*tDGI=fR?QM-0Jj37foYrjWjpA%vfkoj_l7WdzPFL0iGS$lUHMYwB3|1 z$|p1G`_ZB}X1?<ILMC-DSs+O=)>1t+u5NYf>7^UyD7<%Dfw$`6E|oBYBc>6kDLg{)GQ4;~%Wd zerhSSH?xhJ(_&rvG+I*YOB>i&+TpwoX2#|kv_ZS8_FoO}ZD?P+{TcQ^N})TxMGnxb z*w*iJg&B-r1%4xBv|=Nvtp*oal|tX#8SC38v7Vlcp-!YX%d@qlvZcIRSwri&2iF*x zG?jOEn)Dxgvbp+rq{+LBcMV^P=i}Xk+ehb}=qEOtLmV~dLmU>HPl9LrxI4;#F%-#bMSBP!(^GQBgBQ#AaeMssTp6zaQmHJ+!BVVFLGhob( zIm_OHRS(Q=@qATtV4lJU?`c!%h>cInXFXe<2W@@fv)p@w_M=Kx+IHvRL-IX$PBz0E z%$YYv#8cC(@9D|0hxxpU(IzoAtMt1+dyrQ2MCnsCi&K0Y2n}iBjOFgJVAd~Sg7hhu zaZlVzFxp7!66!c|b9WErMqXoeiPf~?&UiOkYDZgE&UH_!6SVv*Pirs$r*f^E89Bm zP@Rz+ec%q=8J+ro^#qKU^gOCzw5J6whcY+LGon3}PtE(QV5jK=)8nOw{d0ad^3j@> z+1Q6GMC7`)Q9i)~X0(#$Ubw&ZTi_m^h2c(Wl;_wG9$}<>6GmfJ%eS0YS;5EN8cWZvA|5<53sy0U3 zvs}&OZAXu016U=pP8dN)b{CA4s1?4o4cx669Ig@Vdp4rlhxH3RSwSCB&G25GUaHZ8 zaFSzr!f!onTlYmuvAbaPgE~M=pQ4dG9eC&qY|Y9H0oKmcvyrV5r$1y6BX!@{l^)EM z#wGP({krjs*USwM_h}NK_gUbEQA^%quDEAnD9=aooX)GBG-XzFx|8~wSi6EYR`6*X zcxBa7_s5L_(aM|7n2b{y3x3Rl@43@H1D@i^LCR9;+d3t;p+~*Zm-$`KNMr-+VX-2i z)oIl^)WEdx)^n$P#%#7j!E(L_9a|qRI&sce>??dW!_ExaGrU`$_yS(-bmY{UpLm2` z_9M&n=UeC38hH9gtYM^48s2?#ew{*EjPgv}X-@ zCAHqf=p<>qm`c(mV9FnSwho87uytupz-c_cNAJ>YMrpO=n^ctbS3Lmw2lVmDnXKz^ z2|guctaK1%FL}3>uH0|57D5`bUaV|je91q2GBV!$mE+uLgyuzlS0ZR*_|N_DT&0KH z-FW(QWQE^hRJW*qmzy@t?fHkQA7&Ut*)r}-Xu0Sq)9TpDN_tL@($QHuSM9tx;K~xP z!o1TYU>a3WaSumZz*<}W{wFXudO}aYN+1*mZ&bfEwobd$lXfC__6Mf!3+bIz_m1L3 z#9Hk*SJ@qu(|p!frJboqSnJ61XC~0D+O8UuTB_CO+^f`bFt$X!SWWW(c{&fc8|(g$ zpMCD(zRd{b$CPK)j5LspKO}2!LQZgbVD|@@S=h^4F&j0hi zeviNVb=>zk=NiB3H@?5`_31Jpxx2par0>7hy~gPqm^ORq2V@FZ_4c|p`8>TOd8j~z55Ap#;|)hn z;tglLvG%A%Fh^Gbt3r+O{*n@4N70$kuaMm^iGUm^b_Kc1tYn!}iq$8D2u%2hBCD_W0H2M(IWv1>A zb68&;L2WEp=7?r|r~`TP%FoW;SZnw`f-Ur4?>$So{Jzv_gKD974PQ zQ$KhPh{>23#7teE-k+dZ1fWGX5ww^hS)El49Q(x zt1DsS_mEs9GD4=%AdMgyIG`|k=zia8e;lSKBx{F?P~artOr4`E;2Q%IxkBGA*Wc{1 zcvSjG=KZ0gyEu|^uAYj#3wVP-f)3XUKJrbPks!Qh>G|<_fF0W^4T(DLWZuI3gpG_x z2Udk$dL5Jogw2j6cG21ECmNIq!LZXF8JtQOkd6RRA^u^AR0UV3OnfB?k zEt;u#4nRDCY3}FfTVS8i!l;FYhXi|%tPuD+$npYR3?kbg0+IMgxnI<~vvf^n?i}e4 z@NI$tZm;#n+#^EQQ`!>#2J%MW%EY?^78a}gtXhIh!0&*DNvqN3mZ)WT(m{z)c@o*N zRP81sjd{67ZG=4xOa`1D-)e2bBtV`rtVa#if^NDqIv*Mo(f`K!>r-hO?@IzurvTYR zwHCZPYxF{!LQa7wM~kA~GO`TL2lTmy`e~uR(NOwm?SP=dPD0wQ)C%9?tiWH@8(_SN z{Or>_$2$ea_@pEQ^B$Z#J%FE=OdIfsAof57&eJ*A$@qT3-=Ea6H+`qr9T#*3ay=Po z6Fp{@5kFz3FzQ!y7Px)(G<-(11Fe3Gu0^$|6MAnLvwqQ>*sjmcXoj)N5QEOuo=t@% zyne@YF0m1+18ve1?$t9+RNwTId^9=XL<#Ub!TW??xR1X3PLc(^v5%e!uVZH&Jy-AX zp`p2gMkL0FzYD&l-a4{}JV_$r!fEW2w?qRr8-AS>{TV_DFD)ON&Hq#njz zwGO);Ogh#DzB1++whpswn`GN~wQ9WD4u)a6o@9gC2D=;b3C^Znx(>c4)~r-A@&s@Vd*5Iw!Up6W9_1(w2G$wg4)RHK}$ zYwplU{Gs_ky(h2()N;WeW*Bd{FGyy*i98FQXP8RS5kPg5YeQWJ80wH6#Q0#oB+Hts z@;qc2{h^upgRTuz_Z5vOUROAnF6z6#^*=EV;t8vDHnp7b-g8I8HcqxMUOwU}`*W`c1Y8Q9p2OyY!pLE^-sJ{4re<9euM}M#OEp&IgycK%+#huc3NThZo<>m)f%p z8{A|aO^gMUAu+rIPRp4uv=7~TjsC~;Mx0=oMtiJg<%b$Y_zyqT_=3i7uUQH{X{h=G z+mbv3_RH}aWjytGmhg45=9sJ0SD+pdsB`urVzf8ELB~U%+oe{4hlJH^q3$wGQWV~W z>6-g9v;t=6KClPR(lOv|@l9d_U(~sbGT63X)i)=cPkBc)g1rn*IIudj)Zky?7agpt zuhf0Wh9Uw+WSPhs-n%)@PKiGoy98hF5Bij!&AK;IWRZ?sq_!^7XT+3T_~_z`xSR=mG3#AjZ+>$w$X}xukK0Cy^)&)poJXkQ9soRT>!ya#5(% z#|mpEdDBxzVEdu*F#on{hL9721!kf-@Vo!6cg#v6YxD~BZFo`;+`}CkmN+c2IChU= zl1QfVJUK#3B}I((jCXRLu8n2MezZ|z#2z(K^JS7VoAKyV<7a?o!8ejz10@y71#0H( zI3LNAH%haI=mow|82y;T;6A`OoKioWR8N4^fsKt_0sE5*cEr(;VSi~}f#Jb}LaYk^ zJ6aPpb;x!*q%m8kIdMkcv*y5Dkypa?;6O6g3Ep_LfCKvefSv)FX)xS)uw!5+fx$ycAq&i&gy$Lm9j)PWb3TkPN7PC%xa@6wYPi;s zC+F3!<2ny1^@p^j)A~UQ@K;J_l5Kfjb|F{AhQ#{2B#&KKzeE2!lB!|oPqgfqo@}4a zzNw8NQ^NY*M5yq{p+zF`Ox%`!!Ydw<^@yjA961mYM!SPulO37f0Fhxl1?)3a(E|(m zmu3Zwy!gN9C!`;7U^s{u=!K65e{_HCpHz#Tr1{&+kxf*3MQV{P0AF1P$*%Xc1DdK4 zZ%XTUOZrSh;b|Ic9(B{IBI3|P*BY%g*I!ow8~wIo`wbNZZY-|~KHsj6Mge z)mVNO{J>N|Avcx`+_&^OyA*7eSgGG@EfWP}kHb^Ku8ZXh%Fk3Wo~HNIU&1FdU-zG* zHsb*wp!Sc}la11I@}6B0F3d4{X7o4WAo%CTXlCI>Mh25*g{_0$H%9-H1Hj!TIID~7 zLiC5Fn!8jnB|?Rj%X&uxn4(@M{sRh*SofQ>80g94 zAf|vl$4DZjsP;#nV($^LA_ovX7>P%HBCw`Z{=#QUW)iEKbq}uTj6P+*N1I>cNC0|b zj^3dOv&VoGB`;~U#tHnwH~Jjeg;oT2=PIYSiMyanA-~Cd`9-5mMVf``N%p)&k`P4E zs6Ga7Fn%`r+VCsVZlo63+T<_6L4-B~vj;IBq5!-ndzF10sdiG=TB2*AQ<>+6S&H{t z^n><7ln|YSnsiu7Az3kJ^-7Z`8zvjFl1j>ho~KW2NdZ_OS?jEDJn{#e)^1e0L3>k$ zli39}m})5|S^$3pb{T&78(P&VS!@wq1uqc!QfcxnQKir;#6GzGpx7X&ev&1hdO!?b z*m(G(I3KGFn~N)-R?B%}XAz9Gq2 zS90-jd4;G?;Z^7 z57erMS-!W*9iWx202@2c{DNBjx?b-nX7PgF*HM4LEJLRLd%7<>1y&iH5nt=Y ze4;J{vxVq6eiW+L6063$gBFae#)q-9gC!yumrzadWg9!`i9S z6y3T-Q8d^g^R)i(62t6?t!g|zM8~LAWblUgTP|r;gFFK*Xguwpx50se+TmXKbV1&u z&#u%OKo8rcPxk6cWT0+UJO9)Q!shuy*Fl3Zb@Px+CmfxDE1gxFk#q;ON_XfTs9mHI zaW*3CtXTZ&`D#BILgZ_o)hDc9OD!btC#=0IKKc*c=I#00>$8=VZ<6;_Ynv7)WW6)Zg>oAiHy zJ}J;=WFf_M1>&4s1w1?%K5%$}$)K;v4am{icr(dT->ak0%kWK5qaGv_{QP*aI2xPl zcfCi01X1+2`j{#{SVrjQ*y6{Wz5^iyf(LX0yWvloE5GS~Y7ec~9Qs9bjyZ-G9ofHI z-%Qb%qSud--awpVv*srjAA1;nb^M~_I8keIvLjR8*ZL-7&R8qdMEX!Oyqnf3Rh)>s zA+=0S34ALDb{euH8tDyI3AqLj7xzNyfQ^U88zlE~y?Ej! zPVa4T^n2`lyki4&{h|85m)b{`_B)b#R8ViK*E5nNphjWh;MG{t@GY%>^7rYL&N?0> zJzC8Gru25=N$`SAUayFE!S@bd6}5jBR155GPZIvzVJq_QU2 zMXA}aOr!sY#+BU3?Hbkj`dO{9#isy9i5x|=O|XNYoY-^NdBDPw^)peu266=S9D5RV zt;gvvn8bJLUeh#VV86n?BKPJ;9Z7{TBrrG#5DsX*#J8!12YV9Nhu4AF3dnzWB+S_0 zr#IXVBXlPaD)6H%(AD6OB=%t}aQ2arx*r(_qx1yR^!`VEOD`;>!(a1g*9f!+u&2$j;1bJ%rYaSewyIrd~jqnG@t&+rx<&{_l! zj%OXtEo!cSLOP-&(cBDLoqSBZgv2Y5n^Y19sdPy9GEyFU-rzm2m1or0sITA)gNF7SeuLW$<2IPVg#%8oW?7PM+mjaJ|ssc{c-)I@=Cvd#L zQi9KZrCwN`Xg(lNLFe-X`z520YE(=>xpH4g6P zhIWH1+PUr!*IBXu)zqk_-_@MT36BYbQcnDf)XUBmr&&O{dqAI{wNt^HtWnr=vAWp{ zeab{-N6A;jH>Nhm^aDpMv2DMDmZ6{dp8e5OZiP+KWL@AXxTx{jucu-+q;9Oi5D^U_ z>U~y6fH1`p!Cpt2;AuIaJ|;>HTJwPV9X|>_B4!eKAggs2dk>i6UFscX$Rf=U_5i%8 z#(u)40^1305y`YqSA@%JyY7weg`FG?XNS((rWbY@b%ltkVez76gWN=SX5@d?xm1$f zqWACvfCv6rt%ePR7zU?5aaC{L7A@^vscA#DwPj-YJ+SSk(L70QP z=qQQH-|T4MUPkMQz`%mEMqU!n1?@6SIv$!gY_;UilS7VH2(xK5{Y6$N{)4vKGg?W1 zY%Q;SGaX4r0U4**(%@Lpg7I-o)Tb~vH+9~QSi_*dZ(?BT=;y67rxrT3EGh0gk^+o z+VfV;5^(b1FL@EO;5U8*b_K(^1M&o091Jg*4!!}q3L_hFG(4Sf&5<`mtn;uW7ZUce zW)bZ2V0&S)7@_$yNq3{FF_;OY>QVhoP35gR zqw$6$C$N{p`c3X;#F3z&H;fk#Tp-#h7JfuWdW4gM`PJ`O`=HpebhcYkqR>I&#iaLR zt$Snxpu6%a(D|^HgO(v@5=lx8I_fuKIp^q%d`TTRUn@v5fswmanfuhSC?%f8GP+w? zVL>YBp6~_0p<=v>;6P|8eAaj;sYG;G_d~Dcna`&I?zk# z3}ma!(8z(nB#u8%_hW}-A6=!ZvXg=n$H#;eyrj`1mjqlSBgq}eh$QA}qzdtgn|^Z6 z=S+_0Q?Q|%G!L-mcm{(N1!*u<@37j*JshGIVDGbn(Q=7ipnGl9eLxT+Me%)Y*HhiZ z%5!z%l?DyKzW$q@+xVV{fvnUu@bb_8-}Q(nqEGeK%mv>^ERGcdo(VeuuOK=YGJA>I zwpuUlX<|>XT~Kiso>KJCUXnwg0DDNXG|`HMJr6AwN!Cn~2HTqIlAq{Xu)E*t__kW# zaN?rR5h(_%NOfCynqlsS?}YK5>&RK|zEVq)ykOb z>|*4D!__xUM=?7Pxg5Hfp@k;&`zK;(r7e{+*6p z?Hq|`l2$Q;sGJBF9ZyDn71%z|sZ-R?AedlC1kXYRGUhv(h0HVN9JqJGpo!;;nA$S6 zj(iWs0HlG@$7$sl%{lr6ej4T`@msX)eHtgc1w>!LmVg5$dxb0rG&ztK8zn7RbHrcC ztOrOUG+BJ;1~F*%9}xN=kibTL z<%|Z}KDdsqnh^ukKd{E}?MUZ}Mz4pn5Tqd0&d{|P1N44&p2eDLj2k!E-P zz;Iv-k>xu}^A0}KuXXG!%@;B#sHy{w1?I&A>UFr9uB!*gfdIu|>KhQTLBA%WiRe1q zacH#vIA_BRVaT#q2ytyA7HfaM5Dj572m$eTm&S&e8R- z(#L9yVRKoomtkAs$*JGYDny?lT7=ifL`1Q54LX4qo2MXVP8IjHY6WrKEzbErtNrlx z;ROQmijR+d5|1_hBVy%-Q4qEbc2%+*K^rmi*$LsyB1?0WEH79LjCTVz#O|8sc!;Qt z0ET#)#*ir4TJ;CoI7|=p?*gZ-uqa9HA9~B*n``MM7{(-$;^oRnPX6ujVc3ozyR#6L@){)T+Yh!=4fT;I;N7fS21o1%6 z;dg--iv9k3^}#%ywM?He7wAv4P4pSoqls`+lh?$ck7%ut9e~viVtJe9BQY=V$|m-V z^#rRHv3+7I?C{&vi~Ds)v|P~J#1C0FL>Sotrfa*Bqh>$O(ONLj41teXshLs*}k0VVd2r6#l5zuT?)9 zD*-#&D) zd>h?^S@)3>w?q%`Yin2V<{+t(AFB-FJA~ zN6DK5B4wiPguk5J@`?HsJUpI9)*F(|u==t-;76lYe0TNXXOfKgfV)W&zs02m4Qqp=au_u-+(1IaTXZIBx4bcFG_!N~>V>pDFZwcePU zll1KvjUAB_EFH!Roq&8QxK|BZjKRSg4a{J^LFA(Wp=Xe(kB1Z)vR8K?Ul$F^@-62MA80^pl~KYFKHfh^ynSxbZyO@5lQTTWF! zZPazQ>iYOU@pgi%;n|7YQJ0PO{-H?XHT_L)FAOdFbY*m3lRb}BfDHZ4v75U&WB8Rb zJ}_XjqmtnPViXpZeQGyxJUEWw{(}*d_8*hH0m;vd$7{o}SVN#7uujmZhzth?jEsK2u{=j7DqNUOD(R!iNvaf)N9;p@8 zMK82TIE{yE_Omy_t2A5h@x5XblM867EMv37^Gs!FBJ5;Ynp`#f#_&TBeJARKHn~zq zjnf%evFr8Zj4@IKJ(xMLMd!nT4ciqNKgb>YiJ)^BDRQ#$2*N4Hd@x8!^i}2t2v%~^ z*?+K_u_Qrj6075X){a@H_`a!Z#9n9WxYJMc3Eb^VCC83y)*3yCwLokc{t>)Nj4l>6 zC`Wi3So_RE_Q-R32T~HQ8H14qYlMG-oCBgkAd~PFfc^wc$=-@?Nsj3+`kScJe)Yl< zy=R}t@3zdDb40dy89kJHlHF&r(@YK>`wqxvBGIe#qGkzEFJl|6(=42)R$(!bePbf| zSm#Ir7)D_CB<7BXkqFW$?UV2s6S3e)!EjTJ555eOXG|6|xCPcVUI8+m`9}$A5$c0I%$9jrc6hK{C<6@SxM6KhM@R z$o~Yl%Bzkf`fEajnA*>)&rY2K0`O&d8h$OSWzN7n*4TjH~ zDrVFN@1!3p^KyK19bZj;_5UhE>jU}AsZm`+Uio^u3Md?|4sxckUgUFu`vph)l4I4p z=(MYz?$A_MVSnL1=!$qmv0B+JOhz19`D}d;%P&!oPt__ijla`#gNnijLi`=u8eAS$ zHj;!YC~#0=lfX8E4TCH&xdqIsKh%$SL6>VB@JGSwi%h~>&Kv+K0XH_epJW-5|9(c% zF0w(XtBmiQ^#o%K8r^yQg+~Wp0=otK5nc$qI-uFmXLdX7!nOwkPyQe4ji~0YTJ!kK zu&7wI$TLtk^Cg>*9!8r+bEck5GxZu6Pdt%mGt3nDMCa&e7>|(gGjxwl`fHBn7Ii9M zPXK3+t`DXhyK1re5zIS1ZnS0ko4B;`9uY;r|BU|!wj3hJ=*Y-pIN#ykI;3YM`vLSb zeimZ4XY_<%V6b(tWIzPsjlZbAfluU+BO}gAGQjHfk5&|ZF?34$9EM5umcu%qxG$@l zIet)5oV^|ixk$aaT=SghCk!~~Hy}O-YHh4=Gzt0z+aCld83662<9#7rj=DpzF4xs} zpsPNT4oTdnyN>@xSHY9OE`b(fQ2KD4AmQknAJo@a$`f?0FSH`R(v?At!W|BGE&PM{ zXJIpfy&MU?O5?zM1XGNh!V8br5H<$v#?yLMo_436{;2wanSn3T^cLvHbQdvAGzMzy z5*MbYsBc0YDHFS32L7q*nb^l!jSBr}&^*jk6CVN5Lsbx}Mv=90Tx~Lp`zE6cEn&GN zEV3NwMXhh_RD5RS>ai}+lo(~MXrd+fIGH1OaZ4xjtl{kp+T(?u7oL zehJ<DYU}XoZrAvs%CR>1FhN z^!dq>xl^2#h>ss1%rMPI))+o>*h2BiV{figJK?%Qrzg&W1ppH;b>IeRR`!>i>>%k{ zUvl+7lDkhkC`YQ^KPUN0+_tu4a~-XJBzsp$b~tgX%11=Bx3y+rS4juMbJ;@Dva2)G ziCx0#N!*y|1F?c`ny({^gVd@*R?Yc z*?mU$cus#2?IvP^4zf&E7qJqo88jg9-y_s|A|If$*cr)C#^y&7;_<>}#3jpv>931b~=14PR@eF}yKOpd`5Q2ThTWb8Vv@iqDuU;Q=b zTksC75&Y12vB^{*M*e_m|d*zIg&k7b$#McCXxv|89r#dE!0y1i#17Y zAT~&w@Fp35INm0>!pWeZhHorYTK*{X7oYHo#9&k6Me*u0kFxTq5lZde0_imT1sWQr z->AI#w*ad zd5uK8sMf+UN#5}p$pob8Mty=z12uyr;dAyD&{C{aVkP?=9S?gAKiL}13TnrY|NN`^ zil~TTA|pqfos{f$qJ40|!mazI-oZXNL-zx(NL+&b53dVZH=A`;GQzP;vG=jlHtRUD zj$l)otKP>GVc59HR_&^JO0i zBNgGC!dHwpo?KnS9Zl9fo&!7qpu4GULB=taUtr+vs~=cI8p}`9U2+jA^tqlAzdX#0 zJi}DIh!M9|h5}eTL5EV85sqYPMf3Bq-A< z>v`GzKy-qDoT}#_ri>IuCqTpAslV}#QTveji4>;FhRKel3du5!8TAT5Sh7~Aa6)_t zTpZpMbR?5)ZKAe#0hr}zQG7#QE&d;HVnjc;=v-FBOs%6`dJ)|{A^Chtv-_+hH8Pzw zgw})4hJ7AP&nC$W@Rmg2j;S|x>e~a7D2Mbpnko0dvk3-+SkX_;)2`6dqX&@7@SaAR z$`|iyq~4G%^tN_TYQMsa@uj2)tBF~>R@cIw->K&{7WZ;JEBhB7QQ~oX)eiFaU`wEe z5befSjW*8iLN2hW(2fR;WdfcB1{b)sF6#%W>vwQ68A9~r%0F~OPX#N~70G2PT2z!Q z4vLeig77rA3RjaM+&&dm%j(^W`guaHr&Kq(f+}d#(k@V->y_2tk7^%CiR&Pqx=mMr z8|#LoJ;)#Sk9_@=ko8QPK;om_T~-eoPAr44$3BAh01V(|{f{MeUB@6zb0pOnh0V@r zfU(1iN-i3hK4w;L_0wvNFV#G$_ecgBYZL@0{sSs?&el0(;}Q$|Mk^JTPPmugh@@5v zYmFE<@*XXNyuOk8maE}i!Lv(T9#00kE52Q#6dQF#JT}NP^2+cV;pzHDpHRcIyX4|> zU6E|A@y<*^W{^Ea^c)Y^GR<{-4_Kbq?&P!^wEAHE##aqjH&_q!njX4hH?3b-5nzV} zKSu^hZ>@LkNM0&YGb5?-@sfRFqB7))fRCZJ1t=NhP-nehvHL(*f#(G+u&?A)3rVam zw3>+nkZtM_1-z*Eyt3v@KxooJjO?a^UD3X=5$Ljw$YU+8}D$>6((PpX|h zX`r=%r~W<3pa-Q{)|MP3*a~*XI$Qjy99N=BG z3}hm;%J8~VPYzTPeEUQndBRWiv|sDn&U&_<`f01D_*hTJD7BPMPKGAE(oQcl*73T} zOlcHI0P5a>3!}0C@&GG_c?xQ0ypH`sEoT1&&u_GI@&)kl)5l~Vb=Oh&g{Uz!P0zYT zqk@z+xx`pRhC`M74rDHN5ePWExI`eSLtacdbiBjnrlJA4H~6GD3d}Xp-LqOt#Kn$F zb{^Aeho$1GWWWurUFsNv14MFElpa@6^%_d6UPDmY99-g-RyXWfToE(|J`!r(_~Z%m zOHLqv`0ba}@aW~z*%`w5@mERb+Ej5z6NK~JBZ&gjUranu0r}uSM~5ZRVAQLqSGNBD zm&2=guW#UPgJ*?`3%&IYtL;l&7uEqX4mbZ#mg0>e z-vWM)^_s`%Y4i>`z~~I~C7oBRZ&pk0!eNYW`X_yg+$P_K+|?!e25oPXBUAB)EK=Wb z#SL1o)6`}nh-e|?3xKS_H;4}m=5??|J)G4{Mh?umRIb1i1`8JP>`8i=99Ph^WIrRP z(Xwc%shfsfPIS71BRj|^Cf&4DMoE&*R zQe(?pAQK5Lz;`vy@8~@hc$!Iul3Cu}`5*a!rH`}%4+rYa;0N)wgBG8zXPK?B!;1!1 zW3_%8Z5Es*?MITrYuHg|(wm?!jn0PMj4Z*oggn7+1mgg!8!Lwze})OkBM%i7Ua){+ z;k)9DIXD}}mgqC6C-f++CL+QnDoiZ}P;giy2h_5K`u(Gxf*lcklD2@Vq&JD&Add&> zh1G28oK19ec~I``c1_er_$5Bq{szjOEN`$)x!&?EL|9o&@`yrM+_R>83Z%cZQwxwV}@o!rav_WVY49P3apx02l%W(1mSN4 zA#_O^Hr(pe1tzrr<08EYeVD$de#8!qDr**5LpC+LAa*YNWz2k-wM>No za>>>>D|L}$b>a`dnc0Lt7v5=fNg{_>{|nSx=$!QCe0_(O0b&SzBk@3Vf_54iD*SbH z_GlQ$=|$EOo&%y0*fJn6SWnm_V1kI=GE0ag8QX@)GaOFXB-p;7#$l`in>16chHV;# zNNO%GRNKhW#;axWG54q+@$%wT=B_)XUvE}_!Yu`si0T}N)B@8Q&|3Js$dtrGy+iYY zSSL}aZzXe$ca0XCR^t&Ss>*CRpmt*=9n&4Do()zBiD7)-pjW{3ArYw#G)6lctH#KA z{Cv!K;&Rh;j`1Ro(L0#*u#HyfOytIF^}%pSP|zk+HURekJH#OEUwBDh)q1ZhpXmG2 zn?BUf+mh?ZMNksW97*}Uv&*4j!R+2gt(dEOn5e302Yt(K0)iDU5D_@?B;l#XD~Ft+ zvK$sW`C(uk(BRMw84okdn4iQdunwuoH%+YsGYj+oK#d_!&ROiA;H;LZMMxX6VTsqH ztu50C8M$~+^TBY!5hpR4)Oj7ny}%;V)5Z=4djY}{PYzeX3q*V!dAd$x#R}xSMfwh$ zI+CAy?l-xh$m{(}cl%vO;4cN&x<#{!JaOj#Hl4v$(0!=2f@N;nK`a3a363f_?)Pgh zfSCjPxkbN?uZmd#eiLRNuE9DzruE9aAzp+h4{H%L27XfT+Q^V!bPSOcP@0=`g?So} zZ?w8P>oItOQ~y~{0lImuo_w{gh-Jwh4}u;} zI>rp^ko|jtW(XB3kkX*nf6_a0_Q0%BK?HOlJ1mwQ8F;Jp$u!LN z+CjF$^VwE22|mE7l0rkBwy<*8!;t+8^|Z`8W*|EOXtREjTwkhnaHv!F02Ij0bI1%u zK7OTMByK@&Emi?_%I7-I3X3E;QADfBT0o!ds3+~HC&yLx2}#9 zsG**DL%s5uT11ut_7!;eotks(IBV1{_^n{BgVE;;T>-xw{zi5@{K~L+gRwSTZ2L9W z+24qa5nIDsfd*s{JRsSLWm0_v`~tj3#PUF`? zMU9S-pURX^JVURtPF@u}N#tq8WHEt~&ygL3zXfh_b`0VS*K{vD?ECaRbpV6H?p@RO z5qa#hWjoz2JBVu1E?x0fy{A&0OF3FZv(alKI-BfSvcr%vApO9m7U(yuydH-ExSXu0 z;AX8Wb#24dfLy z5@1-yH;DFvPC8q2lo(c5&8j~7pBTs|PIRP&qu;@w%^aX2z)+1X5g_KULAOs+e-jU$ zuFsGb?AY)Zg3Cb@C!hN(&4Zzu4L!7GVaR~d2%iT&XgG}wpDa1;#7|+Sx~dl9H{9BMTo? zF4`tsfwMG*^aZx?EL{amCD|rauEK)?mXS;!Y$YRk@rto)qSawhq7%ah#rmgi5cq8H zRj?kzp+sA!>keae#h&sfHIzK8Egb-lJ5r{#dSbeMIA^9>&I}l%r#09b{A)-ja@>j7 zPS*SH9Nmq~Dv|rtm}|)A0s& zRzK6%L`%`6;i5*m%}`G>PqAvT*6_B$3x!7$J!G2hf0LC9?9Twb=%+y%dn!KSuLQjh z!yVQMG95dSSP63ui4VgK(Jrh8Je<5hL!*5Wf4~9-YeT(N^84p$9F4yl%^zt=H7xwC z)G3>;))8l7M@Qqh$z{jB&aMr<1~fW*^%nJlVeFWt>(0@;dAd8}kMC}f=ILOyg!xK8 zA=N>`q8)?PBliHk5xWmPf{X$@oRJE6S7Rm z7efd8P11^9t&7R zY4tPDzfu3AZLZeU@qMt9P=x^f7bXw9TC724kU;~0*j%lP_&LaMcHb(@JItb$> zjeBZE_0ko3I9`v2^0haVhrN#ceXu}%Ah`k33ICvZjnV1@&xAj%oBoG=ifAp~YhLY~ z{RKvPGWb8zXWbmT3Qhr}E9<|G-@KAT_t(DI+`_CCf>|<>}iHO z3`7dD@JqFf$U!|t9bVRc`GPD&(2%#M?sD*t_X}@RLp$gP>KpjgzmkMv2PAp~rx_?_ z<~>;{cn;Wuu-riOBg@fH@#EjrGSO4P$4t{}veQ=H!SVq21f1R+iIn6W%%t!?tXHdv z3zAz#E9leaYAvkS2I=#bK7osYnKD#uCH68@^4#G2@%1u}pUA7(QO{uf$W#beru#C7 zVTq-Rg^vu67s$#hn%OWJ8^jj% zSx-yv18WX)V3+PiP8a#r=(&A0S9l^5OIocbHxdy)1#*Q5F}n@eIAprveaF58N6B79 z2I{wZw_IcSt$Kj$ZF&IY6jA%``VAI_I2M%x(drlzBm=SlX@_18I_IQx=%jR15OlD5 zos&c-E`iQV{M0aU!FfWg0v#C)Cc*<4Z)yUPr)x_1w z)ywsntDCE*tF`NGS6x?aMMZ9t&V9SeRkS)4gw2Cc>kJJOPooTCxLs6W=4TQF81|h6OqG*mu;^f)}@$^r6=@*DEXT zZ|cHRN#S*^hn>Q!PFl6!g-GbZld;5&%2q%_&ts1qYm2OMaj1(B>CUeh{K%HK!5Y3t#q%x zx(2JInMS#ro&^ix3$3QFb)Q~ZU2W6{CQAFdt`7%TNBwlxGxyZdEwnR$6n|Ac^p5(V zv-+sL`m?wCj5MyXY#W zIyu=$u+JE7#6IfhzM3UQXNSX!Spbf)pY8{U&f$qAtPXpYti(M5dS)Li5*aB2VO@1y#gN~^32&c?5Z_Y6Ip%o{v^ zu$kh2Fm)-=NU_oJMZm<3?G7FeEB?5y0+Rbj^}`Z9A9og^x;13P(Fbr@Z`R6(`wT{Jq7Pt` z$=d%%=N{ARi0%oq6dnNbiO37wqpRSXL#BbCo~Y4Yq1A<_cZIYm{Dx@v>!n-eXnZc~ zWmwV-_S|61b0w4UB%(J`W0D*RFdtwTR_Q%4fDQU)zwSvMFP`>OYFDnF52TDoevxe9 z5b?Zth_B7+kT1fEk6Ewp*0u8HsGYssCrw3=TO-xK;Txbb_$diA7a=@Zg-U(&A7NP9zV?F)5uTmyZ>mG9AUcdPfn23Ao&f-xtbfjpzH z9NC6;4a%^mS`D_EIf=I#j|SMBPt^{pAHnX27EIimnoC&nu>N$`Uoai@(JZ4GwTh$I|!DB-??-3oaMHvm6)L}6(~ zlH*Gz7o7a-9K`^=LdQ@wC?L(xtNY;T^$FX8N4B``jZRuY8Yh?+w`5gZGA~E%fNdI! z*6{Eq)P}Qq#)u@>ANmaJBl`k5N{94Rr}f;_`2AHQ1ww^rGa3i>7Nbet(Qw}_3lZyIp*HlAJO}ZG{}gTKUCrp4(mvq9Z>BTBv4D0#W9h5a zM~oIcAfDSlofVC5n3iB8tW~QQXcprSqm_d-Q?a0sSHu;1Niwz9dL#dVybP+9A+?^? z+ImheG*iP93p)x-OY~$nT^U|^BEWFz6R|{c8l4M70@2E5l4LLHYWM1hyS3`8YBj$o zdC*+zw7Me~(dy7f$vz#X9(qmZyrSi26}!%cN>dYg<0u*DN~$M%v3_0=m)B=1^E z!gX@KX|H}CroX|sphFYGY^i=Edh(?{r^Y16B*T4*Cz0Cn#3dF>Dx%3^RpVX6Ux1a# zy$!DQY57DSaa!C=vI-m_b!&{>j2{ooI$TOru);S&1Rg#`q$heZ2n%dHu$y>#;Vma$ zj@T-eHt$S^m+@emnTn^KI36sh=d~{X)c<&lU?)IVFbq+6jbK10PoJnGu`F~_B7$fZ zdvta5JL+J-mq*kb4?Oc9#3~vFtUy7b%E*TR%>q}RS+o34Yy<4AVV}SYO=SoetiX@M zMtwo8+U=}6GOAcRSM*s(E0RbENZ6?J2{m;=xFK0!($8^hK=_dG_2H)?yB;RNnj`#> z*#;l-2bxdtr*+moi3J9RrmK!+2beFp0nUvK3y`)T!~RkW$OSyEp5CTsMzdM3F+?+F zq>(Hzh*7x##>vi_Q)4uHKi4O$bo7atY8{+vc-g4@jnxDK2Ik`KnzQXS|3B5U6Fc}o za~wnuagafp%N?B=ibr|4?!-8Pr~{Wv)fGHu;J*gx^X{6zU3C;}5wo>VW7)#00B)4^ z`lZ%`seaZ_e|@FnkOp7t^WoC$7V9-jPtUG_4;jBYadK7$-nV*M8O^oVk^|dDvZR?> z-A|1J`!%|r@X{oDru6@D2VPCQ%wm;>OMo?KAL4jQzEg1^WUR={W(c4tU^3?E%bk1 zXFb8$Y_i>%6L?y7Yc3NH1OW>6lf9X&4-=&(MNErWV|oX%7JA74m9Tf zdF_N`1XAmiTr2j?^IBo2^}3|<_c${@qVMqHT+^D3r*74{g(cD!?=3Z((el}g$rMWH zZ1nJ>l1b#x{NSus_BQZ+_=`*pTG#~OJ6xj|m1DqCz$J#?pKo9^F!>DF`Cx_SYi7c! z`n6`#07+JAEs+lj`go;!8vh?C3S<%N$7}SnO=r<(c$=}%@ba$EeEL{D&&&kF2xrSt zJt@yle3T3o_z&i&z4#HSk;t4}uf8L9eyv_0D|a|{4tO@Q84I*Z_v#u%N5Ehq@u-`H z%!Idue5PMD3Oghb2I$)4TCUMCjFqu6k!9#Aj0xj$Kw}H%GS@>6m| z#9rZ$Bg%r-yji^t&L8$7;?FP<{HhVhyM;&Jv_Z|z{-mM0qvoi+Ot%nGfry=R1SXEUg~`i68K@s2PQ)r z9tV6Acw@obwbkhM)fsqlz?Kr*Tp}3)6YD%Z8%W+MYAtA2Y$;f_;4c}jwxLa6kr6Q@ z9*MVimd?lX&2IF)#%zM51liqqZ1E$35TEJDMrwU=9Joj18{R&w%#9lHW$HVkb3f}0 z@PuGp@p>};;5&EgYDDy4lOQ(`PxK7+#58?RE;ku##`8*+$q}8)II!~^*T@qIyRPe^ zOYe8q>N#DJGl{a03AIX3$WBhw7v_8B-aOqCyy+6hJ78kiV>N~|bZkRORI(h1_AOA~ zlC98JQtK^A#AhWxpO-{KLc#$|hUl}BTOUYfgZCV-=O^cb>_=*X6U_zX)ymlms4mh^ z{fvbNZwGm^pzraWqdySk0595D_h_c0>PxPDruhU5Vs~91R$y#rB>gJQ592LstP2`YOWZ~YUwpUeOkU`c(N5by>w%YNIhh6u$>bM%p6A$vglB^?g3jFfxP#N2>n&aVtRX8P`F_pSqc2vNz=8EYzuau*I zk;~w7{6EyWyQZB3Z@<@x1n9KXz4G8!(si)Kz~*4Y6c@6u)o3l65%zt4UmQ@jpT3opKSlITFA=B zI|iTML9J1C0oc{>L%}(MH2+C6e}Yyp(Rw&G(6l#dWOx!-l8_d(=7he%GXmBdy_~2h zdlIsPpYxg*$SSHMvNIT#ELd@y#6&k?K*w?hV}^eOe=wdQWcL`I3u2t?2z%81fLz4ig>A%>f${~B&$EEFfq$O(%-34sVEK(t1}%Q7dg*H&Io;V^Mmy54r^dan z+BQk+9KNo}YTaNRF#pBNS(a;&SQ+K;+cA3ZTOR$=ersrRV8lUWvQ4vPwe~YmKkRI1Bj7)NP)p$70bw~+S0HP8m;T?QpW}KN zwp8%U#PZRokT0N&h=^195#D5a0yckU2QeT}66DDd8JwcK6SX4h3CkFsShzQ5s0G9y zXQ>@Xc$hjs>3}yHr+F|+V}*}ls;*5AGm#{G2P<^F)p~ju6yX9OyYx@(ZJYF)iXJ~| zrk~gIQdI*V?k=qqko0@BKCrvcr{EVvzX20q*ik@Ikbiny-;rx@Nb8Q?q?R^FcX+q9 z>o?2t?l(T5UV9xE}` z{W^|Mu@}kTxF*e*3{sao-uc1~g6G5bEpT*p@P&nrWB_rP&|OkGmQ1Vy{k|?)4o{$0 zG0UrxM`T-3-w5WwNa_|>x@6>4N%L5$gyisXeOD-XS)j-!S^uS_rI%1{s#|-&72PAI zc3pIy98cgfC0d5+mQDRkKF3=aO>I zsJ8|eFTM)6SU=PIOL{GIuD4zDk@}${Bx?pZpX|`Qf(>N4)+Brc_}Tr$BxPG29;{K_}`e9o4qG#p3Xo>!_~qkc<7`L-ovidJ2$Z`O-sL%bH!0irO3O z{`M?;pYz&b*GTvBj|iiS3C+$sWl%)*aTX zRvW8(^7-V}c&o&*=wHz`k=5Z~=;y*e3fkm9oPXs;tK4u-_VpoG@4ousiN@W9y-@sA+$wjo;}z2+_rPaFLb&XxU%nZ&76wY+_{5p9L=krAI>jbSh;X+Xh6Oz zJfzDo#gKC(hvWezr6a(<9s%Lm)%bT z*w4}{p_h5Lx_yVYrk6Re{^l9({(sEhP`hp~jtul#+2Dcdi~r_)b2V50ty^Dgl7wWjL{^1v@*V$V0 znF`~t$6fQ@;40(0t$zQx54XL4@`a}>>`LEz*N<VAfr=O1p0z_5805KB?RP`A6?tS-HVI)$aTCUq{P4c-Pytl65XU)!~k|Wq$eh zq-RIgtoP`oJKlNFRpWt|9=fmpZ5!@t@$A}XFZ_FWu}S3~d#cs*@7&SDdLy;=&dE=1 zzUzp;Q|jdk3m>j`|FUwYGKW=s=e~)zSF(Q!|9(r|N9sM`tq{pvQF8NL$NrO7>YY@M zZ_+Jxxi-bd`>SM+xovmlABuM`%)OEk%Sv13sT;}2yS?x|*YKdv`>4cIJ*%^4k@aC{ zcJ8~8bpHz9wnY2zlH?cu)9%-D!$*DpR0;L<^-C_!nSZS)bW8GTQNQ!UPxUE``3ho> zoX$Kt<;E~~4}13IqC-FYQ&e~%apvmf6En`&E_|b?$)!OjU%T{RvV-gX8+-mad3t|A z%$gIKaOKju7CBQx+wzKCx^(G@ys1TX^BPgT8O8|B@b z)8xvdxx)%?%gecZ+lB0l&*m(Pl#9NRe>mso8y^%dEZkVQH_y6JBk%RR{a4PPyZ!3E zaIr+^!fw~9U%DgbTH%9*EpujFKAl%9I-{`t^{Tn|Mw%r?ME-lD;M$>Z&D4|D;Jp5q zuI3yG|Bx6SKAb6Q5?CvEPkP zE^Jx&boi0@*yP&S=2*h2?)i_qSG02enf$WRipfY~MC{ghjpXavTRJ69B`c;@#>QQ{ z?cCoNs$9EVbZg?C#L4KzL|JcoaBW&)u@WU)XEbmhjDH`g5bcuiSdGJd@+TJ+C8nhg zxgYhNb)Sn*2|XEZ9&Q)?KHezNF!oa@8tD}OB)K@X-ttEl6^tra7!6u`@?SEPoB4gCt}ZaJsP;vf8ClA%?^)fhJu?~B)gL%GL?itAk5;rWxsqJ>wjl`(vpyWKu zk=~(>p_jvfNHTIQQ8V?8ebBz@n&mwj^aOi)+F6zDQ{L5SJB#%wHYc-d+F4)Rok-LS zFVB7VLdzq2_Mh6jZ_mj;NA5fP*YKkUFP)G6H{~`AS z#MSI6?>U=#)AK~|mW-M*3vG%Ww>x_}`|k9&^nV_BHCQb8@4yuA zTDx9iRqV6a-|^$IuVb&o2gILF)=z#KFC8lzZ50y=EAU?C;bK+NQ*JAsihi81>~}pS zeR-~R$;T3RB}ONHjL(W@6phTgnm;Sn-1|*hwAl7ygMtmbHC-$0f2=b0PHT2@z4ed# zr9e3NQ*e{FD0wZK84JcwB=*{a-P67Q^)C11yJxrxt!mM`Lsi4UWE0P8!D8uQ{{qj{ zR7dMzVsq^I(2?8`*9YcihO64e)9%ZzoMoq-@%`l)?!6IMlhz~9#kbJA$uq=V#$D6( zTrwlNFgzgS4i!YgiPDO&53?7gid=&|pSr4BwGy92J4a5&3avd_1!q!y>}|<*R)bV! zt)FpLn`G@|Ei06I*)9>Pdad-uf^)sk|9yGYjXp&KlRJH17yGWv^S8bKudbDvROnph zk*;6 zfjXHv8P(H=`+KL~kzJTMx%gwnUoEyR&7J;H#;{<(mzyeP{hZjVAo||45$TNrpSU|E z@3%4&4dQd-uS9Ey`$d=7E^nE@k7=9y8HwwmU9rWk2LeB*{}U|kZtL3VTbR)&t914q znFBnJS(U5_$qk9$;~heU*T1^l@Z!Zwf8|Wg4~O%uM|`c*UM==!X4u=&ZW#F@^ik}w z)CA8kSwo5B?&MYb$<%INy1#4E9r~{QnMX3ZrIq$)dcN^wdpEe|CIiVz?lyt;rBapd z|M#u8WtA8m=xMzYaYx2R#^(Qa`Lpvso$7Qd{aV{to~KmCe@nJ0`AKGzj5+?JR-I^e z;ohs~&Kx_}?sDC$Lvp?>^v7;Z<)*F5UY%9fSI#b`gs*i4cjQkmsFwRn^fvcY|Bmz~ zB_ApoNxPDLD!rH|oH~&RCWpn(M8<`_iq*Ddn8}>eUt9b*b{i# zJH~F4+@)nYKd~n|C~-;?Q)aLoi8yxGdJUU+I_`7$v&Ij#(gU0kH=!=W6NR- z6r0IN{$Z#27NmuWO}XV%sZMD%eHZ-4T&qHjLm!5#N7}|K`JXBNYN^e)tSvo1>!`1m zH|9I!y2JV?Q8~IazjyxiP}S(pxNUDsy`PN5Ub8>)p7n3ZdZP5_WgjkgvUE=ND&OhY ze?n6tk0yR_HSwKv{SbY#;Ln1Ug_|P%?a}TIuFsRf#P!rWfscz{xOKrTt+MRk1HSGa zzyGU@8X2Dl%XmJBRJd+kt$O3j+!{A3w|F+CHON@wzij6xyJ??0 zpSmZlZ`SeRU5Y=IRwZeLPb3z)a@}8et4UV9uKguyUvVw*x0l>l=PK*I;6CJC@3lOq z?Xlr!@}9nW%jJJv`S$v?!a!`c>)*wGD4Sb;K*cMSR^K-HmTyZ`%c_w!&AY)f(DSxu zXs}|bd1cp^{4n!Y|LxZ3@PWvo_`QjJ)=+nKcW?LC!EISnOH9u4r$6Z#6RlcQqww#% zW_eRWyAv0!%GN~J{hmYamcb#}(@V_A`my+`tVN!h@gtFo(tAF2@AFsk9ZEgodc@nt zzdbNJ<6!nHS;vY^%6_m|LEs_ZI_u56`RAvcS$(eOrQvxmCU^TssgGNh_&8&a_nXA= z!o;=G*R~gIw!ZXz7MSku=Bwme?Afu| z;WwgV6P@id$tSFI&+Yz>{;R(I-k#R(_;0a~BNxLTN1jT)oto?_bk*{_?#~F04fOKP zbd60-@P3$97<|$5c+wR=72A~D=pN#4mewl$ZvSfgCFxGL$(~;*jMr>CVU@P>t$cf> zZ*kV7lHO9D?D}bU`zpJdTiX&JCwHY5c&fRq*ejuM=#S{QME_**Sj|wCqB4=i$?NVn ze6GMU&&c>^g`X5IOVoD_@!jEn*QLI+R;B9N(`}bLd*x)gH}$PZo0O52zQSL|8WX-f z)T^jXL96^)MMENGljq%a(-vf$O1~rRPv1kSrm@D6ilOGAmtr+jBRw$<(v z)!zMI-^Q%Gl2uBTC~-Qga@r^E4epQJo!mbAj1_S8PW5(=@@#eQu=*z6h&>!SS8#iH zeDVp`&))wA*7=vYciY?SX!6ncjL7)V{=zY#a>;t0lm7DQrHlVv(x07?(cVARUKH<| zxDsCxaYfd~!qM@e=8?k0Soi0FMH$sHpA9_ZQP!Tl#7?C4x<|N1#&3ik3wMpSN`B>@ z8JwI^HtUb9shN8MRZ_wDkkGus`@_ezGcK?zr`o#KxwoZ4_S=bm;feW|@(vU(3oTAG zcNh9%{;I(?!OOn!p1SS}-Z_DP(k`UE6lmwG=UeG5?k<+}MSqQ4jP0|IyY1kdtXE65 zEj6Op3%;Szd3oR7n4CML=oNdbuU>FP;E31fo#+~3zZH%ayizzm|A%Nt>yqbCMtYgI z%fDE*Z^>}x{`A`E1!?8|sv>LsoGeTP66Ye16&}xz7rhgoX+3GbqGuySB}z#W|6-{UQ7{TA*}@SnUZx$c5a;p)-J z(V5|Yhn5$5LTS-+vGeg-(Qe^zq`ke-zd7TPVy!Y~Wt<9r?o}0CD?Ro`bf4A8Q^7ww z?Y^w-8D+fDWcBF&qSl2c@?Xh6SCEX}=WCOGE^SNTb8qF;p6G?}{75kF(f<6JtGzYI z+T(iE<97FR&GgLmzas6VMX8#%lq8=_mkzOX4 z>wU@-^gQp~?w+odRo+v|*Us10SIalsANRfL?d>j1P4!g|RtW6({@~u}t?A$IFYmkC z{gwT$Qa_HQhI`z>N@??a6;mDKl_HfQx5gig&MUelTr2uf>`?NOr(v*e`uwy(!6t!* zfzcVGv)5%mk@C*B6WTK<>)<9x4q-b)=$EQxQ5yW;+MPQ08|kT{i?6+2w? zY|-yUd&7NVU6LhIr(N%OKMFpc;m@d-{$kp%!4v+zo`1Weis$XNu1T{hvB2-YDppv+mmRob>BU zau0-7Tfh0Ov zeZ>088l89{c3d8_anbB}4f_pu3vYA(Tz@xDSNlKq%;Znep^`n{MsH1MFVK^=cc-TY zCRfK=MlVJ-$Brk;+lm0X7JGj6Hu8L8{T-g2-#hnUUO{-itA2WwVlxAO+K)ts6-^A) zj&0OD9u=RHTx-p?SGeBtPWCLb`zF@J9*lK}FHgQB&Ac%2Z>u1lj4TZADtM;g-l9ii z8FnAn-M-6#`9UkN%2!iotd5orwJW$$*d+Q`@^)8c&lA3N-gA=Nk9${n-j@aXv^6)m zBJqpWRJk`t?U{)-iM+%Bd%Ejc*IV}B#NXC~?w{S|-R<4QJv}|eJe}Qs&6Gx-nf8yd z&XLC>KgJeX$<%dMdvCeG#k3U}6*4u%R}eQ3v%XA>tAxmHEj zub#(!Px*%U8wIWhj``<#-|-xEZ}s%{PYC|*FYB%wUmt!naxz*v@vlTiyf8W^(Zh2% zt!q}>;yGEF>3?}DTE9i#iQR9NOC7i8B$~#v67ymcqpiaAL+e8yMS3MBoy%Rq!@E4b-va^vRaYH* zsa4-K+uJ+1KdoW#0e@#tCHE+AX7Jv$34vQZkEIqR+a>nd|MfKTg?;V)%e>FIPS`)% zCEX*v)%=V6|MAxgEJ{nyC?2fld)@tGVqK(P$Quq6^(cHiUNiNO_fp_+u)x3G7xypn zrFkwXzCKWX;gsvV=S_c}uZFu#vRSxsVJNge+C5&&uIj1fo8o!FbBFhot4B(G9RDD8 zDt^g&&0d*T#8nWE($jZ^$b^!XC~KKJ6&DeOYLiktJVo=fz1<- zSre0=Cl^>V?R;ylb+x{R&@=thQ=LX4}`}>WAPoSOivf>Ccns6-cRxOYgYSsc@VMW<`63J}(*_>*s3fYZ7=g zSUngCmde1J8E%>#oYl2C-R*H&T928Q)xQ zPglcKPfsuZS^o}S9nV}lCsr66k@z!K6m1YY7C##OcjRco<$2s+*1yU-F!fGyn7!MT zboFuX@GSMs^gZNj7Kr#adw+J>aeri0^qlp#_pC4K?d<#9v)8rR-kWS6T^H&bc|6)W zHZ9TDmF=A>PsL98AFBHX`MdjD`hNHP?%gXZc|x$BXMEzPrcm{aC3ie3vn$|P0*<0STRnodt^2y|iK>UGxh2v+HYD+v^?xj#1$Y$4+rVe`E(unk z|CZ9?*5dA7pcH9=;%>#gxI-yWio3gOaCbwse0bOO}88+6b$l3QPlOZ3rq$juR# zDXxb*M)mboL#n0rT_W~q&8k4FPiNjWf4~28PMbSoY2u4X7A47^=yF*zr zKjDi6w<7W+c#*JPqNzkJ*4TpazK^@!zk5IN<6oafeJcDp{_|JCYqpucQuyGoLcTUi zo)dXX{%?&vW43$$n61c;w7#s)74oac&97#Z$!=zN&)hktwi@kB^SAbQ)id4Jfll#7 zg0;+G8)LlSl;8=^uO1OQOsI!>b%_=4a3k$>|Fwu+;pd%zy=wllQ`8@h>+DdA@%oeN zhx(pAWCjHq#FdNR64YK5FFx4aJ?fpYHJyVxfl6%Zx(|bA+(f3D$z~Fof@X(FVt-JD z^+cUWN2m|#gwxgkUHDsnJ@qu$C4O$)<2W7o)iqvT_1L-KpC0x-R?QdYY9Kig>q=f( zwO-Fu$!)-07~B=C=p8bLOh<2=JKIcjMuY{zYWj@X?ly9BdN0iPY8w3XRJU|8I(<|V z?_#_!Zhl;B@TsY%wC<&oI{9>JoloyqTWv0{e6VRCpF3EM_wNXQ8x{cte6Tz9W#4xH z8(%Hm+PfNv4@?bq31p8;@#)WxmpWht1eDGr=Gr| zZc#zCntFyS-e5(HaelJ%-Fd+rrmX)5D65=r zr_JJi7wqT8nh8X0vzhkZF>kNh?Jq{8c28J+-!Qd4SR$_5C-1}mKJ|+GI+)8e(1n~j zDrjEYf1EGE2Slt58|v&M3w>1;^nD)|?>l9e$B+N~DZY(yd}ICfd|#QY*5tp$Ar3oHYN{FUjtI1gZyWf>o9-MB zHxY@#^6Fox2YF*>n}KfafDXJ1W>-!88Ny@23j4E>vB+;O+Eh-sf24n*zapBpTX>f6 zyuNp)Snzb*g19WqKivMPD*%I1{ujO{&Ko-i%h*RW|D#t=1$at44&JD#C-6-A19VJw zW;>-iIhp)>ed(Q6DpqZ9{`F<>#j3~Vpts08v%}R(wFQ_R51t8}b}O>BswO5-{_}-T za{~EH9kof<)6?xjFRA^-$>%TaALDcNRCQeE_63~_x`!%YP6fXXW^;YsMDpx)-AJ#I zJ)-v^i~E?!;AQ;#D*?|g_C@(RI^XCyWb)-)&xfY9nM~zk8{?;DrnhNKly|V3(!{7c zPCnlb-^B256Lw0tFk)%g7UvJsFW4D|slL^mW@A>HcJ?d%&^hM( zpdXqcZgV$-$)Gd%k~stIFK*$$qCjnTo;TIY=iTt$n4!w&d*UzX|ENBCwcR>y1+RhE z%S{#h5Lo1HFddxl!?Q&^_s6KB=BSqwo_Xu74SpH;H&DXuVRAwtx3S+k*(lQCj&GJe zY5wQ_6Wr>KG%wWud{z8y;oE3i%bOPLA2<}>IDUEHlKYC-LvI^p%9s^qf<2x`^;y`F;5le35u!q5=__d>`y0v(sEQdF(ePE*OY^6?Y=OeDI@t&8zOU_sW|E_NZRq z%k1w!R$-l!&ezrVNMAPD-CKb>frG&Z?niI0>1{WtJ9@eAyg!@&C#Qp|N3Bz9UECSy zoJMw(GZ)>??qKg%lf-Ov;{%HW^N_|>h{gP^BJDKjV+mH;1KnITFo`|K`_8;E@4Oko z%<*O77Q_#9%i2Dwx_;!O2}>6-GU9#MPO?uc1FPd=;&KNb1S8!J?q_$0t>Wt#em;CJ zlB1-XHy8}g@&2>}$YeA!KYIhc`F1whoKAyhTN3wRyvN%dUUa$?ah3!RP58eeYzF{iQed#B>N#SIQLF-`S&r?wNV ztLne?uTCD{x6VDYJD4$kTwI~xK=Y$`QeHJYgv;u>FX$iYukP!hztR_UYqDuEwx_#4 z&^}n6f{j$_C!50T^sb;Ie$&PES8BQ0<@Gep)oiDSudOpk4WWAGxoYhs@}X}aEl+RO=kffr zF5v6qi`S0!>CO6rGtu$uGStazafiEC;NHq!YPU(SqTA9;RYsk&BfPo6bHOjo8kG&r zbx1{KjYxi12Zf}_1<>Nj6jebz0|Y+h5u zOt;fH|AOb*%rx(^$?F^nTND0g*je8&eVZDnK6a1E!i;5t$%75ty~d(3GE$edoJyk& z*gz9aIrt#L_SBPo-}~n}_%GP)#Z6Y5%1$=`(fc^Mh|Eo(A zbI$nc`f_2n&m%*h$QkEb=`^tw+)}{>K{uF&s6*FaeBf*_yY1u@_b>3Lc3kgnphoZ~ z6QNqD2z$tV5SSJG!|*h{x<}1k7xczt-OyLq7Y%+JQA1u%CDY%kwq~r?*&B?lkP-io z+_#(3P3>m&n%LjS@zgeVy<>KY(=u#&*hyyw@yC4l5A7*#=Ikf$OK&51zpPvNUg+g! zf_u|#XA-OF`n67|8>ofqf4Z|iuj}Xpc;0)1OWb7U8rrFn4L6OEW0_Sobx5^VelVKR`IyW{zR<9gBjiYj4JB*pIh55F z+j_Y20=p5$F>qc)_cnU!AQVDtFIfdOtCdsdAiu0PX! zG6U5-eMR3;JRy-|Vm<;m`aX$ZoX7Ud?1LdQXGZuz{+% zmw2SQh(Cqzq#8~&W-e7+?Kbn!vu({=OFQv{O*TfefM1OERovrfpp$LHQYDe zzuPxb%egInG+}WkpZd;g9M}@SDA>kSq2jWHJ#9|fyv}F;H{r#?GW#Y&QLSxtXe6D< z;pPdh4DR>N+XbB9GuxE#%6qwxuy37)PA-+e?Dp!~(<)kDRi~k+Sg(UEpu0Iw^-a6c z8wl=)8aeg-h#76B+Go0&|D?ZwFI?62cDqx&tag{0sJUJZRCD!c-yYw1{hUJE6>-M{ zxy%UFNJqo5@;vaDDgpd6(pyPBB%P~mEj>r?R88$;v&v@Fu{xz5WuJLl-Is1_WLt6D z*t-^d9Nge-R)?H6P7xvw72Wl28PnSCH*?*81Fr*Ryzc6a)5y13zv7AHS@yR2tl#Rh zY9DpdNp%(Sl?|N6Ha3_h&@Pb8t&H_j#x!!j!DG9tuKBY213DcQNMlSA)l~netJpLW z5qB?BXA?w!rLnEi3ybtX-*{g$XO)sO(;nHmmZ_d^zB=da4Q6z6nw0tnZ1)~|p<1j{`KtTp z`1YyXUa>&e`1yg4SbA~hzPHX@>3(T8V9#~;J_owT2jWY)pRkm(E1m!|o;j%wI_Z5i z^?6g$+vL@=t@M1f?IvAJU9rW~C)G~(Q^`!dVA9}ow;Z!iuOSjTfFY}TPj9wIlrqQ)=@iCKvmUSRBc<|>+J4E zM{fh##Z^rrMo+wW+e9bUZR|a7l{d}oQ>UGWSWiEj;qFK`wQ0puof%9eth=K4Zj|K>No7o0ydAX?Yrx1h@L!S_5j5NoJ@7h9meNO^tEv=*^*ucx0M%TJL~pXR-4Rm<5Pom4yT&FV>_a^Ct+Vl^PEsp zlhIA!KJuEWr_lNjYM~j2R?@zHVa>u`Ibl@uobk%pNve{nN1YOJ9^p{+oTrWA3=uf< zf>_pW)52@vo+ozd>RZ0AvE~=6Hq>9|())Cb>TmW3e+zU7);BZM&w7SRV#g4@jndDZ zV>*TPf{O!D!Q-ZduI5zN#q2r{>1@tZ$6eL_X40B+Hie$%WOmXaA6|MTO@DmivO2B$ z&b~Iov30u=1-p)Riwsip1IIk#$)jv07rt?B`?o5lTdNZ$!nCI{>;e&vC;qRT>9&gb z-dys&Cu=f4&^hod_>e5eCTi|mAq^**)Vii`u`jRl)^;~-%r^>I3^sRr`P_2u6w^hQ z^c{6}siZaw|EQu)3jd$}Jx-kM?G14!d1Y-+qD(8zI73WmjUYMCf|tcM9m)_P+U+7|{jf z=Y8|Z?dPWVK6pD#W%I4~qkGIfXOcmA*@+|O#0t3S(T7e~aPsPTc7WMpHt}Q^=iYnc zf`Hes7Z*c@u4KC64FIrvNYz5S-Q zTBN$z@!nu}8NT3n`tu~Qojgli`UnzXre3CZVd*SXKRNAvDSf}`8fGf>S$Tp!Vp;9% zGV{V)VD?f~5$S*Hi_v|tXRe!>>a~8R2Veue^|qUG>W*5AuW>L~B)HAx408Q~s4w_r)ci&5Ya(@wtBT$Jy9s9q%bxm$_&2!E7dZW|FnWpNSir!_au&=1B z`XANLelR>`uWIS~s-RiOjv5AYn`Oj9epQuCK`gQoYLXN0EYz*=(T;h>v{2)ao7KI2 z!P7)N)2jVBTy;jw zK{VzIUuyjaPb1Xk`M~7FI_J3`0_lUbqt#P)qBjM&=A)N1sevG;~tEn1~!tpqaVmvpuW_IT!UQd(s>4 z?J*Z@9iD?JYGT~g?m_Za)pcu~P<6o;PiZ$3>#pycsc&!|&?cKzmvvt1EUFdxi(Ou< z`NZ#*&X}y^;|Ka8~@yYe-EYXI1-cQ~Dtn|D1ac#{HoMJFgjn-Ard}+)sY@T7* zfH~FQ_D6HbYh!lV3TUZK*e9{nJI0&(x}-Br|4*GY3(zU0kt#h@j2YGM^Fs_hNw674;saYS zeNoj$s;#r%nl4^;lUp$-e#LkkJXXte>MO-Bk>cn z*hyXyH<4Sz`va(DcDm?1YBSGJJy4k(tNW`o_J2sX>ewhl=muTE9CWLABTYK>hB#Vg zBJ-1MKD^_hj-y|gpnKiTi+wm(HBzh0B6QXslZ{gI$adFeIAp4O@{@y^`noA0poTBsGe zh~`94uQWDabMwYFR#VZg8;BwNZ9l10x-?$H3cRULMo#~%Yj1nu_>P5bJ9SwVBg34? ze865Ps=9!)0(gkq(P#g|#%qlfUF%iw4tlpu23wHIzvbBM2kZqsz!?U&J~#J>g`8A- zS?O{t&ID+*i$FA&{hkc;2KTVZkB^eo*{J+>f~jXGs8(1_x$Guynb*nY*X@bj&9r^c zQ~|dF@@t>TX-ZMOu|^GXt`RRPX^&vnoW(oLpg8j$h`xpjPpCF}A9|{$S&J7o&N=7% z**8efu+@l5KP7hB+l*i>p8FOnAenEXa|4OE)H85QAF9I#+U;gMv6Y%gkNqkG5YA&} z+Nt`z^USHM=~ISfl_B(eTTAl;QIQoyRgK1WQ>AQb?gV<+OwEW`og(h?8#?+5Hehvh z_HA=Po!8@#%){(OBd)z|8K~GUi*Hd`XLQc$z3NYF zgtm64O74U?1CYQ!nIq;D@_rEzd*E&$Vwsg^AyeQ9zlXDS*{AA>E=P{xrZ>v#WJ(ja zxI?tQjeTJ%1KTz{lR4Q$c(bT<;M^_Z;yKj{Q{J2~2kb;OR{dkKQK9ns#6L4pWnRa0 zha)rdq@*VIU*8nw$~o$VF5rBpJ#5o5rWa9-UV0Bt23+Ggh}3GWUPUB$r#a_c^TKU! z>}0xjnqGz|HEV03pQ>G0E6>a*yG7mPSxt}q@WiazibYZpoz>jC>kYTP^#bRkPOcuB z=lBe(uuWUzYb|wK;D>Bew{=hAkI#7qkh2%n*E*f5WIQ*s*8=+}9Tht7?0B9QAkRW( zpt>qRWPE|w#k{bSvEj?&JN7Z>@Ug!_*PbL|_0HR2dh+yXUF*j_IZT9VH`$H9b!v4B zX@aFaQt9th9vf@s6S4nJ zXU1|&!R(E(_qS4^#x;0Bt)Sl-RN3;_5|OR)cu)OxG3q4~+PyX_zcxdEwXy4bOE&*esQ4ucIq*w2M>XFbDDd2NPBf{wbgX+a(XG?gqoZVl+Qf% zPEb`hjELMdEXf|GzFB7e;VI0bSiAL*n>nZyXiHY5j9u#Cx107v6f72D6xMG!TMyfE zt}SfpdG&}Eeu4e+RBf>L$h-EjC4fW|yTiNh7WDl1U`uo-T|#wY?4#H~DV*lInO*M< z@m8Cg*ow`GJ|0FkG$VsIO`in1PrR*OZ>b>QT#OuMIDTy#VpI>*I&}bUdCR(L>*M5g z_t?tF=F+TYrOj!JqjxKMb@{e4d_GQ{pr$673e%T$H*meiJoe_9mFT|5VBtPbjXw3# zsVGg<93MB{`_`1kS8YSa;CCW`5vC9nlTOh=4=z7#j@Z#^n>tRs%z7Ds;Ctlp9UE<$ zg2AuYdp7M?#Y{49mbc4PfG)nkC#!;#IiO;+)koA9c7x}Ky4n$cq;I9Y>-|g>`gE$h zd#XWj)=MCtmTX8;+tIrpEa|@WI3Y*1RK4v56Tx%EX?0`$J6P(2b$Hl5RxR}>H5PmO zZv( zDQfV3*5B(wU@ZZ2tWzV9PN~$F_*P@=N^)8W(fb9>6t4jh$J=TjanD;O9~!(MTKa!# zGW5HZs-(Kq)wRM}YGX&5hFBROTStYbcs)wySfAyr%IM`*Zvd|py*MrU5&sgJUzi><9La;A4Kte2|DaRWUK#%}IO zy~0`KF);(|xB_OQnS$(puGXm1b{~?m3>Iwzt@S>ndI9r4TOTfe!8s<2i0Y&TfBDe# zVZ^hOk~ckt4ZjIXJ%-F{N!v~RM2xSwEoGkJEymf@y0T89_L#QJo>RReLUxDkHb z3e$`n{ZJ)QD8G7x97$wLNj6?_dZ(EV7mQU?(HeKqArd3PTOeOB3W+}s=~#m2nP#iX zP;7lu$K)^4z!^j{L?;EC8ltm=u>FM z7p9cWLze9$@v6$y9(00YS8~!!KSuo*js2P|*DPH_WwX?ln?vLb#2yKF_q_w;Sk_=C zkL85MEKti?ZHpD73o$C$o0w`#|ZCYeSinJoyx8*&jd#=%L^c;f|mj_C*TrH8N|zU8cdWSp4u(td|0(hSM5A1!hj=}~}Z?^|KH zy(XGl++6Th5=)y-KISQPqE)GjJZ0_zo8!Pb0hV-9^@W~Ibh$s-xj6GXCk7-{%ZMo) z^ZxW6cxgDt;Vu?_4tyKVp}`9*rYlotGMijhel=B2+#z4_2(3JYDA7^-#>@h~18jC= zOhN3NI%GngnV0P93pLaZgnNqE8Y+!W0R5)5C-MKMV2^df0vuq{oAY=AW#Ia))|jE@ z7#{C6{PS=kYYBl{IaP}LIMWB+7BJ7bv!BY1Z2XK2*oCiklyf1Z|JMp;Kd+8ZC-T}{ z!4G-cI7fBMv>H3pwCHVZ2QCI{MiS#ap(qACAF^$!s9M7 z;uMxddSVk-@T$MV!kum2dnHW`+dyq)Pf@`75Z>c7uDs3ZS&?QbI`Tci3DCc#K zq}pqas;9mO6SYkYam%Dgpy!;6^9APtzd;%w=fC2d?Dv8k(*wOoms1~1KdAQ)=8Lx# z>=NK~-)v)@Y4NQtgNgTM2;SQ@#<&DuAF*BWzfOZfg@_p+V2ATK@8gkO zuCnXo`b+j+9q9f;RxU4IP!%Len4L_tU>vc5l)(1`ax66&+i&r^>e;c#^aId#Hx&a# zSAZ(25{E8t_n9VUgZTkV{7dQpmZ@42P34)VIh;)L3-pv5UK+s;*AtE01;5uLs(4p5 zR&omPbEMxNNdHs7=?n6OdC>al^;oq9%8Ia4p{^&ac_MNwu}Mx%(I={E8v?`bCNZ>a zIsIh?c3%Y0s?9DElj*Ez{6K0m-)-hZxV>f~5cSZ+H<8)O`1pSbHo|>yG#gFy%H$x2 zvj_QL!*k5VMk^V&_J{@N)|Ah2?kJg_IwO>FI&Vnk6 za$i19t0_yItB`Tc8F(oj6$K(wr*hUwL;838Y#U;CBr)mP#Rsg3ZumvzIIAYNItE94 zXza}2 zdt_3Xn!stP4N&ve$=(F>5;^RI#rr4KNiBhAX*{HR?0yZlOef@IIxMg}>^FrCLce`j zO+ovY=}Rm1wYE6Ac8RSFu6H20R`UecUj7?~4k=1S0cS^>8gMpG9Z+X;oEp7#NQXa} z`wiB(4+-=DUGxNBpdxyz9N$mGKYqfQF&m6KKeT>!ts2Bv?-ghkQxgpt3nmWaM9d)`Pvq&~{I?5^3=zcHBL5 zWHR!*a%NZ%dYNgvbFy7?vbeo$0J%L19z6@J(jkGLQ-`owokiEr;f$F$a}i5p8y;~{ zq)tZ8n`y>4E!f{sV&uPaUQKf5m$N#hCqP;C8~XE*odU%LZ9nKfhngLV*e?cW_2K$w zNc5kfy6wn|yx9JY(Tl&>8{ppKw6ULn?^X8x1yorJ-c3(!*>`FRda@w)#Y5mwlgMmJ z_@yV`+_O!f^!Gdyv=}dB53!vp$heNg*-n{O%wL^KfnU*)7He=3e0~_;RnDe*4ZY+= zhm_(ujA_hvg4{_0yp;3sdtu@!<;m523mqK9$FD_YNECS31s%S|&wa~I*P+czVqf^Rp$NlA$szeL`4gg%B*IT;Bb6ouN`BXbi`!*$2}!G2pH zD^jQ^cw%Wt?@AY)TU0?DL7Q*nbgOUG3gFVm>?LB2M*=S|2R7xXpbJCdY=wTd;d!jF zzo@0GbSj!C8$0ic1l$C7MUj0!3ZJwL=FRcMjg*jN zf%B%45ZAuUI-7%$j8Iw)R&a%tL`iNBnt2R{OY*F0W?});$@xqqB6|?c8;P#XO9bmL zIIJj=V;TG-eQnN?#hQXY@t$vLaf($IeBrNwb{%wIUi3j2GS0xscbI)2yp;$Dj|Fbc zkw6c@?tb*_6nJnv-xR0jQchP}Y}2Fna|0^_#ztmYg7v@FRKhCpBg210f1Wpm+11y`xEko; zvnChk!M)_vvGv$X)fg)lC?4j-<+-LM-~I`ITt{z+2JH_00l2F=lJ6UAwRz-&N0>82&n@_G4KF=M z!tdq30C4y7KWBEZuCzqKI|8#gXaV9%NVUz(_cs!>i24!xvlz3qMXK@C8!PAqt}kQf z3}anev17|HO3-#7_7JXG+Uq6=hn#1vSFv6^xK2*(I>r4viPm>Oa!zLDqoJq!)ELc1 zI!FhCZt#6#exC)0xA`H zhWd)9(D?-Hr6BWsMa9T=pu3t;GGRq`VwO>Gc?D=>7Pf`-z&irmp4i<;xp9M6<&lQ1BG^nCc+3?Mg738BHP3J_^6qqe}fS>rBphLlw!yje7yLbsUVZ{1AN_w=c}VBieuTyse`w` z(Ji3R3VVJ9xweX|(4`>XX1i0vm=lAa9{EB3Di@+Znhl4{Lr2^gHvj7AkoOgt9_U6`+|n$ft6g z#&`m1d5=|>9bM3$Z~8-9_rd*RRw3uaKY^F>Lfvv+>o#VSE_j#0S#j*q`bg~Cj4&78 z`4ddK#2Dg%N@e)44|u@)eYw6Fs^Bz0e8y8v*A{hCAo7-gi{Ry~L*OivG`lR~dsX zo*XPsLMx;~lH_0&1CYk$v4k&DdHOR@-ivhngZ&rft}W2?YW`-24*Id?^I)|FmT3;; ziuB^F!RZu-vH5<2v!(ZJ8)&9D*UPEl+xRvNyV=JYv+(W)8G)(Tk?px}891xX8m0o3 zm(bG*_BI(@KSS4EMB_<6w%Oq99&^^Wsj&)5K?~ElD=kqXvD3c?MkUaD2Z6T58XXjR zZuU0X{7WR-HSl=@E;tOVC3^n__TT{gg5R0>0H18dW*iK?yo93S!PSpo>38ZgzjEI8>`V~36SWS&<59_;Zx9gL)8PTC#3E_rOiz#bk6@Xc zV~yXDV-B+J0l;A&t9cD(#vu=vHX6C}<*_a}+Bk16;0=BMZUZCT0(V zSLIy%89@0LR-KWxe}RN}fd^O<+u}KUh-Um0NW8T0@+>&22YTuiW35BtY=#^CXv|dD zt#TsnN!A#Lb$kP=*#wNz<6|~P0!s%IIR!QYTH^wg85z3LK~{Z&Yi|N0$sN3g`h=!W z0?&Lr$z~aCJ-gq?Cvuv+oDW}_ea0h4UIUqaQ1}I)n-uvVdU!Rfia?&8VRzMl6A@Y< z{BUj&22;BW@&MKJt;SrRjf^n#FkBG_$aWTl*qf0(trWL)WvJcidb zC`Eb#k4H-1gnGV0+dM(`PGv1`+1*2+JORAy1ZqAovH)Jn1wFR{lRc3n8SpIYK|_lf zu?I3d3w+oQyiP-!|IW3Ocy}Hg{lkB?c)x|UUuLw$c+HF8)N6bzGN>vtiyRYcDhJQI z$dx*b(1NjRV^0+4z4XE?hozVR3#k>WmeYf!qqcOU6S_YSpZ0_&8^K*Gun~R-PZbz% zBj0R*S4v>nG-EHrd6y17T$b0Oa$Nm0u)jq zyR1paXuTLeGdArjByLk+G>I#VGecYUGLZK_5O+@iRA(Ydq+?VA_Rs*yv>BMy$1=@G zEb0{WeG4qhxz@YbgPb8OCv=Zy&C(}BP8AloN;ldV);b9|OWz(j#d;A^qXhEzN2qTv zqt0Ud#PojqnSDt==rruU4pb5i&D?{wr8}SW1k!lU(u-F*UL0g~?b$^l^j{U^tn_`D z%-sW#13UTY3bryM;cN5BvQVt#E1;hXi9ZEM_F$LIS>0~n8v{r558?AQ`?<+_8X)uk zKxfV1TIupBeJ-Ti#|cI)g9aN89b8~#d-;6{u9L24uUKyjbzy(w9>D_eOGOb!_}0z*7373_(Lm z&*zzNs&s+v07b84SFeESX-3@&R6fDWNBH(1u2~6JOW!=%yJ&fdxm{;%hgjDgbb&p3x&>uMx(L3H5fX`YK}u$x53pj=03{*(w9Jb|VH5$Ce=3?Vsk7`loA*3s7(#s%{*&j03cCI@J z1;nwI>u}d1^v{0qARUL#F}93x3;ahh?hUXj9iOG2g!miMo$NaAZn5v{{C)^6X=2@P z80#|s3kTldQ|V45C$vYgm!)vaDAq3>=%+xbqoI?Xe0!g3Z$nF0(a1@lQ0c@K4NRr) zt909wZp~B#hE{Wv`4+H`x$yj0c2@}dvJBRmbkJ*u?x_fbMsep2Rv=dMc2<)BjNN1< z`xz;UmA{1&uJheg=;9R6kRETfv24eMpf2afM{(^1A_zj~N4Y{evxp_|0BTvs&t<6I zkM@?%+y|h*2aJ9Tx}U;2`mj2IV=u5JUHxkD`W|_60&npVQneQAEdkH}!fNY6E9Lku zIkGk{+%6r$CbGXtKt_6x{tX|M!pihvEoa1Q55tPxi}f2%F0cYPuf%mzkV+fCoy?US zX0-76K8oQx>25B3<;TK{AYLb(s!l>J z5BTmjcS={{jllX8G(H=h)(#oaA4xF==(l8DBba{~JCc4kv%v2`MxO+INN;B81|j$V zjYOCUUo3&ER&noi*07zm-Cz#si2WCLN*^}qxiF3YPqQ|W>7qw!B5@~xg8@kRnm|eX z_+z|8A}wJDN8$RX(8nQW*~+S=gRkhf8NANG9nx3pF|?Eg>p2ZsfXvXTblQBxcah*g z`Z9~X_$VY(Wt6B_vu87Rx@&ak(~*yx$DTd#A^Y5Zh?{bN=$5jf2UgVLGz0y~sW zqtgHE0`Qp##OATCOvuq!U`YBiKf)Szkz}Ws^C_R+XGPNSL;8_R$GpB^pc&S|dS-mi z6<3&#O7D>D5cwyaTP~wlrAOgm;5PvL_6FL$pzikIuMwCT%WgI!9b3WmUHL2B<0gPz z>c<$rIq+-Ej4QzODL8c~bhR72NI%t;;9feS%!Wc1hDNQAw)p{{KNGUH06eMSpOo;W z)G~PZRRxd{ji7YtQ80ng?y%md5G^Qhmj?LVL~e;BJm5HZ?SS}h2WIS%p*NBp0G~oFQLg{A7>v&kpjp0=03C;1t*Q;H49Fl zZUQU~;w7Dfc7>#sSU(q7mqhPwLhsaY1HnmPs04DpT*ywH0z72)mB>MX!$Ib}7?Lih zS%<_$CxZ#e@U7t8AOAnLToVPXCjx!x_B{|BG(tv8Z%*lG_LkW$@)EujyI>9Uw3Izd zr@mECY;7cbc`W6+@I`mtmBR9po~UKv!usf!&*mF)iQ+4LAENqS&@Xkct$At}ySfzj zw}UPUv4=X)%qqtCojpk&tv7S6fSQ&=yIr8M($HP$P#nG>+9)m8l!nerqld~Pc_wg8 zf2g7{RD6AUd|dP+*J_!V%u039SCiWmUe(Lh43cCxtk)jdNY0#pxc~r zq>tz{D5(WdmX0mb`8g7-i;W)*<%k6%eW%Amo6=XWH!J81EDnV1mia(>IHN3MjOC0n zmb=7CmfrT#aeD-N8WYlT(tA((-b)YCwLnF*^bFwI2W&UuzJbt2ZY&G2?z6J{zHroZ zuKx=yxC*)x>ouBtr9b*Lq`yc4>5CUytT9=`?g4$V(#+>8O1eUil5! zH9@LPK!1p|T+e+|8SgAB+{)^tPw#&nv!(BrbZm+yz9AjYd_;pzu(C(Yxe-|GggS&u zPC`MmS?5fyjA2gcnia{a#UgnK4NLbb@pPmYkn}y zCV96Jh)eI>lfdjmXpZNs?=bJBYrXUq8wA!C0H>i)Y9DB^CbDG)bkiKV+swK}=f-lS zL>Z29Zxo~40Tve-^(r(dokpX;{RE&N!>H1wx&ilZWOngnwxFS6L;E`qMx}?ObnX(1 zLLz?mc_;lkqmY%S*p(uG@_>=W54{(%v#+tU$H@C#I@>HJ*{1xSCs_3TZN50IX4`&r8hb}V-P zPQD!htq){2>E0@sAIJEsxw|KOI|}7UpJ1VD=>aFb$i@;*m&X@W0-!w;1OV zye;v9ZHyShH)6N11^&{nzCIY82uDi~TIn?`QNYb$UiSN+hbmtEO+MMeYzwdgmU8bY zC}t+_H$g)Z+m>i^Kd3|O3h7<9fp7K#d+9B_fKR21nBZa?cS;vBiDHUeI>&0o%Q^$4 zN*(20s79b7cv#6eds$5+qiy8xL^w&hzx79J^hWB{Lzas<7BH1Fy8os8cLwW~?qZJc!O=7OjitqIeIx-*j;VbhsHGl7n zA$O6BPt##5ekQMwig~1qZErL|HS|SU_EMgeN~FFNyK2d&MfjvPR3!eS*m}}kZY7wJ z{8~FS>-^v&-x#Bh}dBNv2*b0r}l1tTC8hOd^eTfBHjBIpaDme0D z%@!i!R+IlLk!|HUFCzDnp_Hnh55V#RaqsVmO(Z0HAl|`S#*{3=2X-ob(%bP&3bh*T7^A%;&S5+*o`g)TQE^Ugu1QpwU1>gWNL*To{}2}gBD zQmhG4P&8NF0jAR9Si09s_U9b;-{h0)jK2~391h-AAWv4p`4STp3KqYvCse!$+MfnS zN3)N`(2+z|q;t5~wbEf%dI5?)I}gQ(uPBk3;~}biz~_&UX408(4KwZIPU!|IeNYU_Bb?q7$k#`XcY*f$Vv`I2Lgo3iJ|p+%lhH`gTFlv<>&7vTc;E9G zQO4ZBP6xp2%~+ZAN*395kTplb4J-I77TY4OUX3)H159@Cxp2#2_A6Z;rBb6lYxxly zv^+EPh4T&pm)BtVGQSfLRg?^k^hgxlzmNcLvw~9kS$FvC@`UoMreai?sTQ zQ6~b&&0tA-Qf`Hpr2}Mdt{x1={_9G)i2J8O3xBd#>D=CryP5*EM%d1ouyr#dtqUNv zB$FyDER7x31TK|cnXA~Dj69t^OK0A`;9)4EjpFwtb~Ps?mu5pR>v=C-K&98Q?6oiw zI~DS~2;S*$jMoeJO2mCUs~f|;9e{LaB!NhSwxRzTf#*KFlfJG~nWGo8N!Q9*tXwjP ze*#_c(H1h&AhdvBayZx2<-O4OM4%(yb9pGFDpI^GvRpDUDfzS<)^KN5ExC&s{4D2Z z4EXBDNK1jf(p*zr=)l15NqJ8gpe?KIZ--Bn-kP<(*$nK;^{RL#9bm!zL66hcb$V4Gi#g94#hAwl}J>Yf? z%$?yIIg@q@6feGgJ@C^3d`m2LJ#;3&#asEWqw)3-jY{ubu~p}Q^KtN;SRhNl_7-Sg z6`b zj5r<%IGB}ohKIT{{z^DXZ1#n$RwUm{*0_U}NLSmjXn;mQCoiv|c(v)E@z(H1Ddf?w zM7Je2R{|+k93Gd*O?f0)B4k-s;^*c0b^!BDgZd?YJOj9nWZnU+LL^cj#;k_aDFdHW zWsG#_jDldJ0)OkGTk11&9c0cpRy2_ni{)E_HI#rmb23*$RzDt|8p$dpJ~xUP1cvkZ z*$BVJuqVOHFmN-KeTlt05t(v`y9CbTSwnBWiwxm9hQE=}kibCX+6eBO1;qBkYtwk` z0=f@?y4V{>fV*h_Q_Q-Av3v1%0#alWcS^5w(S%c3#cE(Si@9cnq>uO%TbW(_X|eOq zLj|&$t4KbvDI_~2Jo;Z%L@e`3Y+rISqTQx5j(7%J_*^`Qi;O1zzKkjn0;zh~z|T@N z&~oP3#<$0zgyUR!lKIbY_ZjvhkXZ+$L>7y$IR-s97CAf!{rWeUkqBlJWP2ap^<}iF zjJKYhtmH?!ByWKlcko_jkY2cbfxpnyAh6RO4c-{6^kE$miJcKz_Y`o~7Fy}W_+kaE zL*GkxS<&nh8D%|d7cch&kT?d_t>F5uT+@fKdjY|IXfDzE|8>yr31%eDGJsFzd6*Wg znez$A@cafv7Gvcdq0sKET4cBM$DYn?5=Rl7jAh*tN0U<$-yw-5dv}K2O=T^jMRy=^ zj{%t@yj#m2Hn6@ep*vn7NsjZ9JbVl=+z9LiuBW*x7C6W9UNp^Nq|9ALkvQ&YKHJ16 z(zjbUQu3VpJrakIZC`q#+KZ#WNIXy zdmq{Okndu-M!Lm|uG`9MU&!x2#J7jovsjm+OJk5i(d=a31RJR%jdrQz{<@O*7>_Xq#?1EW%RG8lU72Q>-iJAg&;=6>b6T2Rhl zFg_955s56EA^oYPf3^7a;&n)7^%*?%h}Tg*6M8zr-;4aWm-U~3%cQ5h^c-IWRz-^* zftEM2zumy+CJ=tYxX*w@4A;hlFc`}_$!!a-pW&?JMe9=h8k?7SiUQ3|6P23a3bwjvXwBBjbU9#6hq0_m3BV!KaHIrG!^XU%ek7VWd*pFlw-?28y)7<2*$mS(b)>L*P z)}lz1<5*8cxlVGj zqC>{=ZXU0>p?HQsN%ZV=^yY9@*N+h-W;GsamiK{*n~lA6MvP+D*3s}WS)9T`xM zYX?K$L-}+(SZKxdV&ng3nb%^+1;`)BQ#A7IZ64%D34ZtGZv*b@0p`Z?y8+{h%xS{s z)rct-;hJB0R}K#(A@^tErz}?pA4&bP#N>He5S*=KN1}&T1H)MMCYp0Quv!Kd<-3(o z+e%;{T2lO`mW){(TctGcYRA5oaIM@e`ACsak71EFEQr@uzUcDKLKs8WQ^uuxGOLo%j{B(Ef%a)LeJ#=Kj3F3Ya7HYBbjF! z>yuLucJsamSQbj?&0S)_zK4?Ek^@lK=&8tNza_f!FI@H<{*pYk)JorhrX@QS&Dw%<5D3QV^Sl`poIv=o_C@}Jb{YcE`9REw) z{uo?7lAph!rwKs38}R7{+$2{t7I;nPC2<*%E|L{J&w6gbs}kpyDxaN|OC9!Vd)JP~%qL;JT>lyA5OF*haU$S0_Y&`-$*MPu& zu)LpDODssD@RIA3Sd{3a6Uafyg014axqNbz@lHZxlc1J~z(bZ1BwWNAmauEd2F0=#vCUSq#&vMWF(7st*ho}OY_jN(^xwhW4uyDU3u8-; zR;mzU8CfX&2y1u*HQf(EKrERnz~w5RJ^-tN0g-T`f7kQ=XlOq>_-q?{JH!l9^CUH1 z;t33e_6I}vlfkgO{^9pzb|k#n2Z>S_`?VCZBO}_Z0e`C?y(HHuoZB0T(ifRLg`Y2UgB%5fQ;~&$QiLiqC?tn9nyglj##6>doK63 zN9s2W{nW#{t;7mS;sq7urHHvZywdaD!7D3H)IBX4yfS*XHX6ShF^TS>d1o?`_k#r6`teUA4+Q|p0=)D}sU=MX#Fgp861hSYuUf%4XYNvW+9eJ{Ab$p1&d zqhz)vcDaWcMWc(SGL7F8fOmIRCK0TEfL>K(u*AE51in%$QXII*^J-bZQC0lJj9{Y} zp6DM){-5y>D&Q&9;$5APjnfRQb!Ok4fpBA_`bc2X1b7I(rHZ!~un-wIpFOOBc6&e* z{n*oHAhaR0=S}QpE4bUt@5AgtEDfp2lKM=kQ27(-6A70{zU3(Qh?g%}EwMVqGu#gD zX91lvz)}3aOF-Z<6#W2hm&(@*Q1kueiHz5zi_C3ybl7*D06KD1?qWCFSxKlXu1^*?auJSs6CDU+& z@g%n^-`-<25(yWd;usWkB?PGxAt@ww(Q?L-*wYn0I{|GR2cqkN%LY~{ znPsV@JjME@R$4Na8^GPE(Abgzj|M)QfR6ZktH8`KsB$qFki7U_Fmj#$C5|Gyky*s< z7b{)nkZhyGV5FKwDwjU8q9;H^GCPv9mpVJqZBIfNlDw^CIAvv$_q`CJ@4Z|jUYz90 zpRiJ?HTIL&2vYZ-BvkEl6Imvi2+@KPf8(@aYWKs*grp*RCRvr2%pn<9Rw$d zHkKHfSV|I~S;f1Fd@ELlXsJzTjt$`a@c&DReW7tgbM5DoEnrLHJY87V&+xE#D9w=s z!aK{c;>6mLN;%Hof4@s);98N~o#5y{m}>!&w+}G_k&D95Vzo+5*(_GM zIwae~%I%G;>5UBSj{K<0NNJI-Nw6XfCucZFm2~*Abwd_s!_aThf&bM_tYs9bn%d1+ zl9`y#jwGTuof!sl&0==m1vrmJN=qcHIaD|Vxg+&>1Nf0_gLoawLU54hhD2iQgrlb* z!-pa3#xdq!V6hGBs)v75#X%XB^4oHfGPI_(rnB^?-91Feh~~;^|2)Su7)o zj?4^6_~p=xXvF_KFsa_U&ieoJPoJ^+qfog-!lYtGDwFy$%2d`N(3d)NsTuf-ks{fv zM6)hKePWRufltrE15#}*8FR6Sr!)5ob~1}qY(&0`B@oSglEITaq6=pkYScdPTa)V( zn@8&CKf^g+6CJiZ%l#h9@B#1sdy?f9wxc|m6VG+ua)opvxzE)iMMXY7WrpJ+$t=}1 zA_WOYw$QLBDhO84dj(W@W39I8y&3 zPtlxUFJkLRhEeLp?}xB*82PiG`3`c&F8F2#>)8x05AdHzONsc2wf8WzUeOqb`FtOk zSOO=V<$v)4B|;K|JP|+fBI8NTp2)_fV00r`nhxeg(@XqMo_v|cck&F5#F01hS0ben zQP{%gQl%hu4+nU+ke^9t=dPhUdO{)p(c5xHm-yRcsJu7U7U$9Nw>6a05RPsNt^Nf+ zNFGz70Z~BxERYpE+=V(su0PIZvhVj!EdjAEwF9TP!VdS2CC+iY> zb{&7ktConJ)G{6gVyA)h668gHUh+IrMPyzTTpKhQ3%Z&Df22T2lAXJ483rfYuZ^rayEv6JC>e$0B~uXt4}KWmVi?ctSBm$NgyO3 zQ;7(f6B0(LA^{>ALYPU21PF?I+55Zqj~=eXN)Y29AMb${(zStOE8ibsE;qqNo9TB2pN*rfhr{&n9pj8` z9Gf}JnQ(6@Ry>Q|_?i#r)S^widK169!Xi3vW!|(2l?HX(?>L-xmV0R{oI=k-g zcTa!kaDS)Op(l1Hv$lseYnUzb!qjl%e%-)Zwoh=3Gf>%%Da%&@$kpf zynhN0LmRI6Pxz!G617LrE03ZTsUpV=#6l0XS}FZeD=QcF(Zh{U;%)jZR{IR2YT!^e zyE9qI8G3uPo80m0UAzKK4sfOjl8aF08rHxU9AZD;h8p!~feJ>K&AnzpfA@OtFwfg* zP2ZTf0I?vVMD?S&-q&%47%nA;xjnPtqZiRYBj|fJ{QV+S=nJol%^U!CiqaX)XtKFa z3GFKnjDfuYuVq3(SA_lu@q+H|^hoJ1RL|-KE`sO9Me?s;e7iBUkj|aW(-Cd35Lx5? zvV=Zwq^En}MfcDr=%u!z5kGTD&cZF1pkEZ-lvl(XD&xC2OmB(o1zK?0^ z414IWXv3?Hz*pL5T6B6F4nY~ct|z%_GvAqcRRQms+iO0-MC4r_y_`?)-{4acyq?19 z%SQ8=1(N|)#<0H{$1kH8zZ&|juriF*ZV0{5LQ`L8F^iat5Ryw6XEEHlp0z99egf^A zRi{mo3mx1&8}U5OaVgCDchpk3ImA?dh>p8TjP^Ume}&QUS3rFJ0*$FxK>D2FI-|SV zn9_*<(`Kx@>4*09VrFMPww@RTaT~_>^(mL~@SJx8ZB9Y6o#>l0w0te_VN>qjh-Pj= z6jqOheu1U*dn&z7fij7_CbI&PkotPDl!!$^4(b_O!VH_EWe>DA^@3Kz1=@+?mfz+# zxn>>@GvX%0TOuSn!GGP^v-QQkNo5WuaTk3IE4cSe-sxSG`^;h=&%1PHB$wV8Lojc9 z3}btol`f}h879ElL*Z?6aUMe6>j!*;nQ(`;iXMN!Jbe>*PhY8?bt72Q!oAcbRFH;8 z-r_u0(h{zqhA%x|6nI&?R(&o?&-=wAar$t#o>*aBkSou!qB4*{RzoOa%zFOy$?f6u zVQ4Dy|6OFSnGaiNUw`zU>9d%VSo%Gg*JReC>vJrWHdZo{UX?HlTA%6M>o3fPp0C$= zh=%k|dR;Taezz@N%eF*g9^*{)iW+SKzlq=IM=u`^-ce&VZ9wz=5()PZ^zVU;?92V* zSQX>AV+Jx#tWP|L7WF77VLV)0eipwB;>=!9xHG>R|IOgcxbVp2US{l@1C$kbWiB#9 zFRXr+sD(smXD*6+?<~$t0OBz?ot>=CLx@@?1?@m9(jKSAi6C|CGyInoqidl_`>>mQhvG-hBA34@YVk zgVNwtq^91*>EY07e~qiNnG1I{D`~|&O&yOJ%&7b9lgNX=v3kBhc1PG!rLq=aDKbOU zm}o$wS;`Fe*<0x0GC0t94gZbBiay6`Y>G!P2CUL4WP{#BA8H3zyjhx!+(qxJE7<+D zn%G=jaUW(-DK(lS6Yl|Q@$;D9gS-0~n7XS0-Pa5}#*MTeAv2otJ*7^`2#LX~+YJA1 zb5J48`FuBcy+4A`yNm4e=ENv(#Xi4*<2;hc9qZTVsz&bffAr!8@=7lA{k0&gnz7Tp ziCwJw*hVC}84B+HcR}Z>u&R_m#>UNKR5t6!Gb=9ghZxcPnr|Dqj{p2+G@fw;OF$dL zoULs<+>1UVIf&Dlmf!9=? zd+{oQAq8&Fqlfv7LWJs@@UtGPg~-|hdN+p=ji6_7ShcbA_XQ+LAFg?v<2B@?RRPTB ze~GrzpoeR3GOa6B#?p#dmIxaX(Jlk!=8jM zuLV8%9o%{m-Z!=&Uz~n0emn6z#!_+^Z9bGyw=1=b9JNOZ-opD> zd`)k?e$JP(3?M*h}t*K%grETcMl zQHJ!1tZxy;T87qFSV!w-8!U#ZRnS!JSx#Rop}pQI^^N&fM;WI%UA|XKXk+X3GAraP zYr^qr2e^MyqPT{fDPus&gm?Kpk`!iWiDTUmR>ms$MY~Pk`V_ce2BXV_LdrLHE(_?J zH7CXgPB#K+j!y<$pUE%k)oi46GBPodbM<++3o<@c0fpsB<5S%@I}2?xgsXd@2h@>z zA>1WTgIlLE+6kO#zG+YRb0}Bz=X^a-v8>a0uIP=<@ty@-Rm7_}TlZ8(tn{$B5}u$-HH8yu>KvT=Qh-F%D6uj=c=Zh(FZ_ zmBSs&!W|1(WtFUHJx0sv%`Ez^9v&NJes<_rA?wc!6A=c`Y+)CXbm}hd4wf7~?v~+=G7j)bZJ`(4?9@7*ttuk6 z$jG_gogMssnlld3%3j)2-oD3d=|2$X;;WhRt7xx`u`XavMFVFtx)D$)kJ+0<&y(nX zGPJaQpHvg2md@v4$S!5xiyY=YB{B!Yp}3jdR6J%@yE7ME@F=!npWB9A*00!S-^RnZ zN^4eLJ7iyfq@JtL{EU&@Q!W_~FY>=)(AlV`bX^0rRxnG>%2@6)fqQl5n9V$B8Ho6D zXIKU8*RayW7U=1arkhx0^-w_%yZHaz&`JCEG;7Knq3>BIW~oFGv(GrAo;lH{VwS)z zXl_JPAN&WjZcd`RUcD@0Y~E%ST47)x4}H=`E06I``Sua_XyE-Zem8>W{=+H&qRh>T zIzg24B9hE#=PhX9n~8lk=k@a-F?0(So?|~ny*A@eSt=$-#DbYXqMt-Hnk!QRJ*qf= zGmmO$VV-(DpN)hXtrQEY4WadLoIT1B9@;KusOif$=RzLUL#Rik2(FpRs#?HdrLfiX zZ42Kn4Kk#h`|Cw;1V$6|A~ZscX4DN*-aBtn_kHF^FORa%owA--cZBO`aXI~UM{eK6 zZLguSPiaNGaJoYH@CrfocO#ldL<6Ty{yvdW!7nYh<@D?GM0Ztb*zi{v71pD z>i|`%zr1!MwRwI_w$6{hO#B-aZLfyRo*VFU-$*{uFVKiTC-?Fv_(bl*c5QJ+e{H6;J zcWP;TpThARpC3Upi?;7W{~x1`XYsNO4%&zRjYF#I*G@pvBq9Zpu;siqgSOmv@ZZbW zJRP`yS9URf;_e-SToFsr2iX#74LzMx!(#^AwkXJdb&?nf`N0@L2ImiEMjk{XJxv?E zxHbp=&`V$*vi|T<+&P>1%Hvx10C}O^=jmTIV;IMg6>NoEBop zBCay>wC2P6a@RrDiM)P}!}#dQU=P*9KSqhn6WWEuPy?KXpO$mDf@|P9D}uNh%y5)T z#eis0DUqy>`dzT5n}9!$!S;^iBEATw$f63^SPoIZ<$`#~^nIQiWGU87{6YDu%kJrQ2^?j^nR+Oezbck%M z0@nE^`fEJ~(9A4G!OL!Q0TNEgxMmf%im3tc*%|T{EY`8hJ&hJKOEk46Y z#r`mp`n>hz8Bw#!+g92xMW0nead)U?T&ZVNTIlUD_w*Qa@YRTh-yLU9W|gmfv|(&! z8!cM4nPRkOCOoTEBQ7k$l#9twMyco2P*$>0(;1)D(&fG(M%b1ng>Uk>^e83Et_b)Wb#!==bQ3^!OikVgJiMOzCX)ocS+f7`6G}*^EfJJDE?@kpXHC ztA{5bi^QRc*U+xHmv>6QND6jDn*9NLsw*_ff^u>2yVW|zaAafW_TV07TWWcw2Rcus zeeF>xZDlsS zGSR*;@*P`y#{K9Jc!dAcf-~HGi9iugs*g?l+M+Tqt4p|d1o~tH6BP;4? zccvlv(;35bt}_qH@t1P`bR@0Xpf6{tYt%XV(|WSg`(3a-e-Dqg3p4X5GZRZoki);h(@T!*GnbJOmw8$IuTlqKr>|1H>*#+LaSS_ zz+BBnxy&|`|K8_2Yx6iGqiAOrJZ@yvwJj3eIQUl9u50)NeBJ=3Z-wLa?q0&uYUDhv zOgUdU{SH@DB3rg|tv;50Txrb2eW3XT%4E?VyO0!K8$~z8zKQPG&+q%uDh=T}C5x-b z>=QlBwxPwP1UCoODDP(IvW&6mEpVT(2HN{Cisn4(@$j`0UNJg+n!_E4dy;xywTD0D zleh-)AmX^2;EDPQA6E>R5o>@gWyMyrX5Gu0C0@r|=v%L1d`sc$3O0Wzp{_sI)eGtkU_fX?( zpWyrf`;PMBC0RBI^^zAyfLp9YzX@S*xqLR{>H~8*3cvL#fjii|ql{Xn(YT zxuQi#pDYgZ1@)*Y`NXnVHOriQ5$J1qR4}{Ni_v4eCfq^v@e}J!K}6 zTo1u04)?Iy$5ADNmGnaYt#W4}qmCr3J6-p})-~!!ALHPme)O{)R03eb)N%hg@P>YL zW7C6?dYu_nYw87C5Bax@+USHex+BDunMTG&20+)*jLDsgnqwi)E8rw`ueq7Mm=~#? z9&C$z#&7&1j;r-Q^@o>Ufv2p}lf_Ks1wEu~wgX;xi@v+F5{FvDcb_oEt*jsYVoDLM zArTNUSVrbs{*~ubtVCso7%%IW7aZ{?>p5v=XnDl6e6i zLu>P^wdSSIG^m&ZRi?se){xOZYn)JDU`3E(q912N=PJGv&1U?zmL4yJ%k@TSm8p6A zLIWd(*=V~0#;=WB$aO-#(^yqOKdD#b&M)fnY|Kk#J|;Y(BMxx<_A$w%>MI zRukwM5#gstO)ia9NX#wlj*RDMt?T=C9~XITqD*$zw<}&`O$NK6p~yjNF_~eU z0e4N|9+SEMQdXqWTV$>|pVoqjvihuBHk36p9j>v?Q6FSbN1ojii&h@M$`XxZb+|ar z%^%lO(U;%M+V27#tzhy?B>!DV{#JNStXFk2?{2_L(t?_lEkJ2s zrDE{aYuAC)Zb|Kw|E9fPp$VShN}~t8X|)Tqx4OdMApebprts-GW-J-K>)edwejWHG zKE#LGa);aT<>-NFg*NKMxA(KVXvO?KjEo(Ioov0w!JIGJOx)0`aETSSw9fP466Lvh zbyd)!8lGN{{1LrhhRu`l){wLTkJ&yM4Oq=Z)>BGFUYJwGe!>l}I z&JJG9L(q460Up&WVZNyK9+fL*aeR$rG#~UV=i8!tTru$O5v=~Cei8{~ovd>_u5iuY zkxh~R)3~wK-1OII7ixQo7dA&EIvZLwU-Tqb ze@aC7CVWF@xWAqRv+%4CXk6_&q@gzMIr96j$AhAs>xzta^J+o;V#dYAx=v&UbD4u_ ze5=<>6lHm!%KEU<_1Wr+HcMb7R1sILRjakDoh{;P5i&-tP{!TW5YE&g&U632f%zA; zC{|VuUr9T=;1GFaC+E9!(rwV4af&k z9%iH#BflcA$8y@XHmjDi6`g8=u24#ef!42MG)1e*{%oa3I~kKv>mMMMF47DCcfbgO zJ}*UoikL|Iu z+GERhVbvz1GkVZ3y&a-|wA`n1w%Fv?XxEn2iTWSPIIP0Qzq=w~7js1_z0`s;XRkN= z_%tNa>wFS9Yi)~C&a?)3K6AQ^Pa9ct`n0P9U7av~N`$@u#B0Z$~F|WH<0zDApc*@Ec@&2X-LVxoU+sy)!b}+}KBu zc)j`d0pwt>uz%`=98AYYAU0~}@~*C>T}O>3rT znfQqE!rbK=|5cj!-xX`2wwM7u^x4=Enb4lpV&`nLxDu{u`tZy4e!)%S;KPmiWH zxi-Gi+kaIw|IjR!y*!Huc6a;<=X$ojMeWAb(9$dtb2&=c&AMZHjqgMti1IVesw7f- zT1QX+kd{yhzw3uI(^yZqsOjx+o%wM)xRXd^M_3l=0O)$nWF$+s(c-wtawO9U~O0X$)L@?h|_ZA-X`DP|N&p%-v-W zMMls($9x#GDjTuN^|x5b%V^0l?kn!Ho;LP!)-ifiNe{L4?X7gsm+smxgQG-wxVxIm z7;~Y%H6GlBNEy+ak*D0xu7l6upXfgsME$v&l`q657<-oD1+3m!`1KVmte2s`@vJ25 zuz|=4|1)7YSD4%Q4lSEKF1}hz;lsdDezr=0=*enY6Qw>Jscc@06`SmDE|j0cee_2v zStHr0SKiu_@~~^cDiiMC%;Iv-a+*)><@7~;i|%et-jNaN3*2oNl-tScZfI)Ou&dE( z8I`Q8cfty)p@&8rwn05nBuauB?z4nv`JxDEp2zz_XgZgE6$ZOgJawcy^l%q4LhDUR zf2Emv$Xc6HTi>3DIXPaWj5$o|)wNusW-o)H;s{4VS2f~1S`%AT%Du&YrXtOxy|stM z^3^i(v-J294F@DI7SmcJEM-lMG-Xe}R2IuGeTfEhAkXzh`wk>Xn0otZf! z6MCtY1Nx^Iz-3}#K2&xhU4(7AAJup%&OT-#_(4_a1k%8-C)MKX!ehL$@Ax@_txaT2gBJ)~)}XkTxuR;N61~N3`yc zoc?@DLVD`6iJ3!EVqy{o-+RZSUe)Q|A5h#WhT>N-F)f^TQXgcHp zSwn`95R!r^;C7G&z6K%y55N_e2>1dPKtJ_@T28rA=9B~dsZZ6Adr5$NPHZOD5Qm8e z#9QJnv6b*9GzbCVM(iN|5`1z6`JPmgv#9%2HHA?udYm}L0PKPFz&C&Zg1`gdb1(yJ z2MJIMGJ%XC1N!-@3*tdAgwuZ=0sHA~ z6huJ;;y_FY1Bbv)up5+u3`hfF(O3TfZUWsvZO{yyM~|cnbb?kx(a<^Q7_ z785+89xuc@aT8)a{oi&%gPcU}BNIs`6-wQvGN^va2-pq`0Jp&~NFPpvgON`N$XLdR zV+ffmm{*t&n3tG)m}{A?1v?P1H&12KAL1qgDb#zzI+c zTGCN_37UdWfJp!wkkOIPrO)X@HIV1X>7OBs17y_JBahL#sChkDQ5kBM#vX```qNqx0DbNgrfG5D?U>Il%@<9`DK6nExq{qsjku(#! z2SM;bxDuw|QTQ9Y6o%j&=nM1{Du-~$6Fv+V!dA!*Y@4C4UhpJ_CP`Ibqf47c?GiM_sXt z*dR6we~u5~?!+eI0&$7{)FWQf_jem}#^fl524df_Ksr84h;%}i3@0Cut>i@NDV-l1 zfM>v4Ixn?>+Z30&PP&nUL@M!;KFeMjBO8c=#699SQA!jO--(mNB4Pra=_`qg#2Y&I zW)XR~9e$RM-*5B<`Uy=%^XdHq`WXF+4xx*%Tx=Y^9p8yh!6CdB>%x5SV*D&IiL4|8 zsdH2|WeD5@0zeh`3G#yFPJ?yXp^WnR+j(iN#~n@C00&KDLSoB|FG{l#1E}WCBLuL9hc{1NA^r z@F2VrNk$NcD`Ol3W_(24>6tf!UqL=lBX}P40|}ZrDu6Bk15h9rSOR>Z#N;fpj`&FY zBx-3q_7VSx)5LOmyGG~n9P$a7Lza;FWG$&fU8APZ*CN4M=m9(pX+sV&IL!M@eby;f z2@7Gnu$QpUu?yH1oET0YXEygGw~Nc+A-p{9T<%rQL-rZg4(4{oRipxTgRemCG?N_% z`si$(PUq=(%7xOSM^Z#{Ln4_@{w2?ld~zvq8c)XpF>N}F60vzS-mVbi>FoVUod>3a zIiM1(2BSeeun_nR!~+>LdmaO{fUA^%`bHilx06xiE_ypi-lh@YM%|=ZD3V6`XKD>) zL6J0Tqx9ZEW|E)jYf8zrG~=EE*mNwbz;VzM$P)esMH^hF=>Q9WpFkuS4n@PykZTN2<~?RL(~xzH1<+CKWUJUI>@c>NRm9@4zp-y| zPH=;GKY6vhf4tK?e_k*5KW+<$#gVaoGou&|2nG#-C}0JwqmoDm@-5*^e8rvdk5~XU zh8Cb{=s)xwx&zfilhjAmd)2XOLcIcgj{Zf{(Q4G3#?=-4G_jR5ptUoZ>ZZ(rXg~sN z29s&sYM?)#fmgwcG>_v5bLmK{b^~>Y=6W9G zOY8nLT19t*1z<1Dk};qqm`)>N8*rPxoBu#f=nZ5K--V^{EaV9?f=p&yWz;fEnai2S zX^k#HF{r3l9#vgcaaCqYBgG7Pk?e!) zudG5=B#V{plr55Nmlevk%e@u4N>Vwh8dA$q1m_SEB7qF2k#!N63<{w(=q@Zod=V3* z3cdtyf=|G8@M>ff+06LJ_{7-3z>(cZ6TAqngw{hU@Cs-QexX@I7hutjVgv9B_y;@& z+=0{77qXD%ji{^?%b(*?FT}|)*)Nj?-)O*yk)dF>bYL2Q&c|_@@ z6evlBQXx@{DVWN6%6jEDRhqgN1@M0S0ii{1q4S`RG@&(fIklZu$MkUNP1;`fW6E6c{@<<|}4PS|#z) zGqN@ErHZ4) zZE*?q5VOVJqAqBz`jmQudbj$uTB4qfK0wRRF%-ruuvJ(Nwv=Y~v$Vr8APq?l$sojrKmFH0zOc#4i(9 z@Yfg{t3^53QH;Pg3L^Q2}o`eJ8L^#qa+fN^{7Pq0t-ij5{ zdh>iV6_;$oD!i6vwjXp>l~G@4H+7J*r{0nv8Ahbwp|rk3xEkxj(y(*b z3``GWU?$jN>=D+1S>OxtBlugqnI896;s@=<{mBb7nj&coZ3Bt{CC~xf2Rwm#+HE|c zeo+;)zj#Y6pa#j;WGqRN3Dh?r6U>J0z~;yXWIK&51^f{9fm@(E&`M}L#DL1c>$KZ- zr=$6mRysdAi(VjkNHkIhYtmyL2GeOhCqw}|oW{XE;1_@BpsMEB=QBpR* zRA3h1N2@NI&L*)6eop!m1WONMz+I#kR_-Ma3}_;Nle6# zVO{8U)Br_kANma4h=t>`351L!L&*_hDe)Wk#+zx}JjFg@wU`tB9-m8a$WoG?ZQwXq z0!1N#Ob+`T=P0+9o5+pinsW8HLG-sBcOj>O)yWuu!=WRz`)&qrLw4{=*aiMg&rmwJ zAM^s7X|_5;og^0FxPtb-{}D~Ze6o(bMC}1?ft^qY@|sc0tYFseQ+ORxgbX4bNCToq7BXrWJD8))AXYdlgyq8uWhJoIvnO$Q+{@hK z+;mPTJB-Odet@x*fP{!`xB!2FwW80}-O3L6uyl>&xOlC&TbwR=D7BDz%9^F2(pt$& zNw&mI`aw!c0oiLQOS)cyiFuMl2_?;x7b%<7nb>OLGnqvd0zIH3d;?K2#1;m21t8Pi2ha~jmT|;i%f)FAOXk$rc(cC94d)9gfBfC7V0a?xr!_D zx$>>@ck*&N7N_M#@{h6^GL){>fl3&BxBbBdVGlA`oIO z-a<3~XY4e_!N#H5YE0>_>{MteFDp@{w(1i-J8$KuWv`?TQeEjHX_C}S_EDxS-!4y( zlk!yxxnfW`PtC)I@mFLMjnsX#I*-w=sSZ$rF0ejw2$@Q|?h$A$U8U(kF0}V{hBP6B zMy4rUV_pWI0`sU=;v3DT1DF?n5|6?&u zR`kc{cFYjJj;G`Q=vpxzw<65RIO+$O2j6C-GF@39>n3vsa~IQ|70lkvnZk|YR&wsK zF=iyg2w6_&=w5gPUVuzP?$F*f1h65;<7KETIsv_j=Ai4)aq8(xwwx=i5hsgNBv)l0 z6qi&b>IJkC#;b$X6V*xTeAFBNK@5}MsS&DxuCiZHest9w1|MLYWqEVLxiaoJ-VEM0 z-XESPKZn0qkSrJw6bKdw4)GW9K<;n$cNUkGO=rkv=3@p2`2rS^wb(-SJ!Os}LBXco zh>J2>u}D57eI>~jUlLPdL>el)Az!b!s8Go}WIv?}iH-D>3{_a6;e?913z|UnU^vY< zZQywrVD_^MdDDb?8UdQMnr2$Fv|_ZHwL-P?wKa7}?OocDT4IeAqCPlIM{tKY zhU{oYD5OON;Zx9w>QNO+N13VGrwEYcitmqFjeH-TGV)+_p15DamhF-m$tFq1Ny5Zz z@e%P`Nw~~cF{<=eYoPa0ds_Jl2tQyZe3SW%L+~~Wss+)4RKAd3z;om~3T_KEHD+iI zXtrqjX$EP`5Iq#G5uO#M3111b1${gn4jl_fPFCV&SSsd%AIH6LCI+i7C@f{+lB?nx zF(iF2o2@vm z6p<&W$CRA>O4#D*Xo-52dcJzCda0VPKB>x4$`uxh#qtL-k*rs`NVZdAP!oKOl8pAc@Ri&T8Hz{&&6>--#E+wc;+}R&p2d&hY|y zk=$Xniun~;1bG4<$zgf~w@43a5p|Kw!&jn>%3(Q|=7!CRJlX~F)QePQ3aQLfsurIR zmy3%fzOq93WaS!_4()TltIbdb7K0xoPXq3dA#6xHzN?@$_!jhopD_H`+qmC(UA!RP zMD8q3DZ8HS&AG%m$!TO?VSQ&Dga;t?o&sq*vg&q z0_k?iQ}HeF0?AwHeEEpNSEW`Nt3RnKmBorC`6RhMtr{-$RhyOTRgYBPR5DeDIvk59 zE&@y8vy52g1?E;p4&A+&O=n0uD1}DhT{I^}!yBOpx}UKcI09tR-ANS?3kZOv)N>Lh z&k>eHGVV`%hbRKPtpi02Ia5Hj1R?3PGMCf+2od8q?a=2jxy&t$Xk;@y51IoW z017BS>O1YBcM<<^ef%2jytB}0=q|O5YNaAbW+0g|CK&xXVm|6Jc0!yaxhZXz{*q3Y z)=AQ6g`6qtl9wnG)C;hU_$aPNyW?ZTYOqIvKS8iw7)TrdVYOEpWeS*DbXlUPaqN;0Iw zvVQqK#U@3i9Fqmh3}mUYsfx|2WHbx6qWcs{bZvTtw4-;UB$2BK->e|Q(T$=IqzBHn5{?;90(`U{n#NOjM_q+zzDUAT2sAKU88QLosz!l zj>1_!LDnYKkzJL^Wtw!Yv{fdMA+mk474oMFq3Wudi=Dwq;tAr(&1UZuJE9F4cAA z7e&8(n|!+bh+HPWp>R@uQyQrbtLju!)L+zmbTOS5-%${Ij6J{=#3O12s0DvQY?xPB zW9&KHv%EumiftR(?e>rrfA5Knbi1-$6VhZV-ovs|1@Q$W;Ie z-h@XGoKeAi#rnt=a*KHJf@o2jMxrL7CDlyRe5Uza^M~eZ&1srjG&DqEf-Ssg&Tp1G zvx7!rDqTSg)7_yd)IlQ=o|ZB~V; zo~u@<|3k|#MgOTZ9V z^B42(a(g&loW1Njtf$OdjN`OJUk^ormw^nrSAK}@cy1xO@zr=aT}9=h8)%gsj~++0 z>B_1b?9w2peKuXZRo-7=YLyOe1|S(Te$_-&GrxI~95IFu6n) zBZFjTq^?p6>0YUc43J~;QALIFi|W4m9omis67NVRumC&*#lhW(H8YZRm3@P=p3CRG zfG0xZ*awMpOLBYMdKI7 z!Nxm`91Y*=TkECi9Mc}Jb&po`b3BM`f{X|5;0M*sidfk-$%Zk;$d183eY!orJ1g67 zwz1lL+l1{aI=VV{beGT#exv?H11*Dt!^g%BN}ntKsvqJu)Fv<*zRE~uS#k6DA4OlZ zUhB>`Sa0;oc+hy3F=n{JAX{&*?k1g`+WuOnG)#p0yf-WW2?ajmxXMqyTUw4kZUK$Gp zG29)jeE1PH8Jnu)%VNb3M*V1l{?J|AHmAv;zNmJ2omqoRQ(E)&mc|zU)}S`ijvHN? zy|(?I2jHQK;Xh+TG66c5%7%xS1#E!3n^z-PulY^4-O#`^!>q%xQrd)pc*{{;Pl6aH&z& z=+czlG`{6!TXv_Ym*|fiiXN#R8;~|D=is}5Gq45Y47~*~&$93F8#SHvCmZ{lq9z8$ zPYf>U9n@W^o2)ymyI0puCroRKhKo?ZkL7$~YQe_TEE>@&`9oQZ6p}m~4H#P6SJXAD zV@`WP`}|I~?v5VMzM%e<1LcE>!$YGJC3|I`6=rHKeu|n6k1+cMdBjv z84VgK8GO*E+s*1QYx~*yvF&untFF5}6Z^jPdk+o{{T|scmL(pR1j|+_UaAeqK-iP5 z%ikhgAxaco)_AQoUhjobn^}!zgVmVjA9DlKRHFfdb^0l~DcUDA4~rayDS|4&Z2_DA zj{OdXNS?Yz{!P{)6UsBB?qd%Iyt|TG?lvxIJki|RX4$o)C%HGi&ugG~Xy@2|nNXF0 zUZCsaE5LaA5odrfPpeGVL4S+>Q~h*<@y5r^9$2ol*0rv-SZkVZ*ssUYZP3opnxPpc znlJdxtEW}Dl*3`)K`v9j)stkJW0!`f3@sR}=-=GCuIpvHVOx0Hln$$I{l0SpYlbvO zyvHo1pA^2RIiW$V0ouVcNH%+^&`U?tkT7mCi7}aDveR_A#bKK@4#%DH9Nq0=Ep8hv z&^@au68Q?edAHe{8BS0*WljWOw(5>0WWq$)3aA2Rq-ky>Fh`XwWdb z;eBIy^WnD3oz}gfgCnE=$yTZyv0mD3BvD79M5YnHQd6m?HtIADvPiP}U|a5R$hp8} zqstAa@eZ@?cGxbq?X{`4nrLRNpDCQE=Xjm>G*CO#zN6h`~^6fGg-4jf4a#O^I2BU z>_S~MJyN~jdtV=~b~1eU9AJZM@SeiLuxCY4$pehgx?vENDL1 zsW;EcdE6c!U4N^IG2`YtoVU4ZV{DgiYiAi~@J!H$`~jT71o$B$1|n56hL3jox2$cE zw@>S??V8`pt=U?{%#mhw<-IE1RdcBEcFSN}d#Aj2-O%UJj4|n`!RU{%lZqukAFo2! z-*~mLN`JqOoz4S;M2p>yv&MP5-*Bn7n`o765n#35#?EGvd4lc;XE)`gc2bq1O!^7n za_qTmV5ql;*HzHj)_JL&(Rj6NV^-szp?Ja1tUtP$(Z$l5qb=H9Q~PcX&KzJPdLk6f?SR}n3{apE1!XFyyUelp!zuAd(`F6c+3vE1Jv$4vwM$j1E-qo{j zz;$Tl@TJk~QXO?W`35dy4zRs=H9}n-Eu$IcQC8vB&en<6+wGP)%iJ@(?s?^o`(Upy zJ8tk#*Fi5@U!hl^#pB%vh3W&+iDEsme9U@u%HW=!n;kms?2eJn&0YK3V;Wk^ymODG z<)&QENH17kJ-MyF|Bl#58H*mp&HlfN^aC52dZIJ~nPs*E$EDJFp`(wT(%RZO(z?p} zpEYX5vbtwEYI)Pj&nnvdsNpdU1p~mZ$X<@I$1aPnN`K0h%JxdOjXoT-=|wtbG)=4d zQ}(o|x?s2vE-S40-O3xNlnszf7Q&AZoZt!B8yIzrT&_g>Kl6)D$2~^9eZ41oEFE{) zZM^#@&!CAbeEoclCKh=j?nTbK>|k?e-Orq@Ux^}wEyMa zJ@HmRZlLkx1%a&r4Svdra?e4xD93p=J{Df4M~#y7erWWv^a(AAUVlyJkB;44X*~hG zXS)Vk0_sL93d*GAU#cu>{#AY|iq0td-5KAK5S)=%S=7_67yxb}Jfsp*z=>=hjj0BS zW~EjaY;gzDEp_7bDViaFL%vLL^)j(Jtrso2rlD_OX=Uj2%;ky09CJTyoau}2lb#rw z(A&`|>>TUL=zBCYG<V;Do0>&nGN(pKD)iw!_HHWVUs%%My=Mxy2$33?N4$m$04db;3HM6wO>U$dfHSp0agOem1+dV3$6?EmKR(z_|r))!6 zR@tv|RYgZ-Vx>lfsB~-L_(dr`S zvOiS?wXt=ax*fHxwe#yQHYn=P*X%9}%=gPYn>IJ&Lf*WJ4K2BY`RZa;tIj2J7rW2) zb8Wb0!3OX3{uvkBANTohR&7{z#NW{N=_lQrjJjD~~6 zrl^Pdch{dRc$vC4$?ET%v{kv=i*3tZm3+I)$i@NH=vS4<3235 zR-;jZdAWJ0iKRiFUZ+u+O^Ao+#C^r(UyQF{h2iV$0Z7+?m3i+Pa3{ zOstG2YL$R1hbGndXEpzs`kVZx%uT6CYwT)Y-mU7n+#A!2^oI15b+7EX-S=!rA%3SU z#&y9qM8<9sL~8Fh_-t%#;%OACPw1^Rnrmg`x_RQpNe2Svd70QhHMq!kWW0tVUwLB8j0w+1~zpO`FTS@&a?d7ObtzY1-5o)(v$Iv}|fJX({j09qm^eK#%CB zk?VkGxELFzX0r9QUz%>U)p1O5D6*Yu#j{vpvCH~{L!Vo>*Ad?&e~I6l3D4b3tO=b0 z##yDu(4=mn<4D)fUjG5!fJcvI>-HMw5}&+_+3M_*c|Qx6l_Zqrl&VS?C2_^MCG3iE zb${BQjqu22!nvk-4#&qG7;ozS*Y%vsPglyl*o!gId;;5JiOXC2b9SyydE+1Y4F(;b zlI*v@wZQlpr$ntBHtgZFuWw%2U{bfUmQkP32JOCpfj+9=6-;deL{z z%(g`uVNLTxf(Bf68Rl{lsaa9Ldhc`_wf?FeUqogt zPY+11&fJn$P!dtQrepPJEdG{737!gLxNng*Ulx_H5&eTZ6(5JL_3rNd+5dFFy7y4ahKd6@b5dk~ z1*v-3DFv5{wTo>FI|^J%z3S6?uPa|7O}xqcVAd3XOZT4;;2ziC;Dw!$7dz2<9=70Y77Df2Xe%^N3O+DaprRq2rrkxWQtu?Z ziu)E<{$c>J=Q+0-OCps+v!fc0e`zW z?j13EYPiX2yH%^g|1D0K_ci3nlnWD-cK39WA&$IwSZ_FQ?6=&7e%f`O?)%2b5+$=n z6}?Lu)%gVpI&Zrkhd%oFBJPu8;`P+)ne(z9WUbF#U$UkCZ;zvFJn)9QUgw8 z!vgVHJ}YMN)=>8Rm|$hVxe2`Lb)AZ8i(qlt9O+579GotO$+`#<=cXHQLm&g2i`P%-tqHjl2gk5w3rfhTq9gPU0~-XXUFj#0iAPBt$eYC+IDMg)vSL` zhd2v{GHVYNFDN=%URs~okus1xmLk0=(Ha_PbuEoa-v24!`Qe9GAHq*_UL?LMedQ7B z@NV0W=NYMWe?~!gmuRz2kM?2BYZ?~XwuTm#a~xCL`zG23y_h|5nPUC59cOpH+}5~a zRijLrZ9VOY&m*&T-Z@;+da(m>?kGq%e zsUJ>#HTcaseS3v-+tR+}gBgR3L+Y^=DnF27O9iJi=4k^)E9|5FmxNy4dg1WBXuktm z8#gcX@PB1ruRDkwlPSCJH)%FS_nbyCEpLZU9u2NsBNsfcQ7diEm!GkbFLpf-e>U}L z?6bpfMDZ`PZ`B3$JRC6To>xDw*gyAfL4VDXzL)Ac+Dm@cd2PJO{V5_dwWkO;ZQWz*&po z4IB1)M^h)FqE);9TRnZ|7dJx#1;bJei&qWJ9vB)}FnUTA#=NLkVPojB#^JPq7WK6~ zF!#+b)wi-=s?5>qqrD&HAfuR!|H%VXbq zyqm?9j|v@g{BlZ)2bzenxr{A3fYBVC6Nt@7di9CaIbTa-yIy#_jrjSfP8(Nd&&39V3j2~BrC+LF zv@RPgS3Cgt?5To>8dJ0&t;M2bP7rua(J*kf^?Nl@{3vfv_WG=_Y^%IOMfK%%wOd>4 zd%VYf6GG8A>vIzyhIobk3M*Q;Z03wf%n8PxcRbH|MSA?_WNuQ#J}BMR8dMsYWs|xp zwK8jKX<3UvV#=~ImO2rhDlcm{rA3S=9h=w-SGuGPf6I#vcsliIb!^!;ht!zT@|KFh z35tW{L52fAKzr1PWnJmCZ2UQI+EsY@xT~BJ?S^dV!1mB;Q3Ez?*=ERkM zYeYLddjtEc1{#O+rSl0t?lVKBW2A3c@XmS03)*J8PklMj-ObuT*Y2|2Ub{I~@%mSo zOT=579uytPUXYhx`L#<^d6*Te{m^K!iML^v<`d>eRb+ocLq^H8T+Qs(+?ge|H3rR4 zdvu#aV{3UzPIvN9BK#M~d)=}bSC}mHHx5mTEZWqt{=|w^3)f9Q>%(&Gv~<%q5#+&Q zbVQ8wVNLPnv-46ir8)O21NwCt|5?fXzs^0j==rSp34SKu$(tR~g}46(#~=KG{EYc6 zO`er@He*v(e@=FRXBkvWwD|Y=O3wj-8mBB5c%YNjGwVVM={wSN`ePEP39_Sr&+6?` z^O2%~^#kU^lO;=41aS@uW!~nV(zs&KV7c1q%D9km-yK3t`g!lvk9+6W7UnmkS|(}# zxtV+^v$-(4BB6S1<>sP;nWvLa{#9n^S6=S7fs74y+IP8bbsr{jU}!3d#;YAs)u2&AVue$vAcDP%MM>wh;ZrWHT=lbWx3Oe?U%FH4g4x+rj`6O z`kMA(*IR?v_hQ@MO#j4)&-m+ z?wmyh4@!nBs2cyqziq>P3Yi1rmqD59_^JOw?IZ2Ct=v_#XT{z%doJwM-8ye=Pw0~A z#U47AdYT6i2YiC+qcTo~sXP^rMozU~D}9%A`%T2{`{zbZrp35i4ttRGt|4Jc=JZ1C zlCeVboUbX;L@XZp>HRxC)imF((z$s>w4*iei^6zHs73y`E3fQva@5C=CogXs-`scS+LOoc zR>q(FXOhLs@z0D)UitIR=g03j?=F4H`oa6I6$%Wa`sqmzqumfly!i!uFr zpY;De89X|FP4i;jg@(&EH=1wr?|r*>{$AWY?+5XZm%JQ$?-M8bw<~{3t-PmNd4l6@ zZ0&N=KQ@FFI&-<}>cZ8JS6&atLPHnk%x_z;EYu~OwJKrl^>sJa8AR?_J$?E8(18%K z&noN9oQmO@Rddozep27u;?5=BN&cSlKDjdC;n%-!zCB%Zf9%$)8@F#*-Ohi|`SQqD z-;}duZ@Vhhh9XDn>0VJmeZj!=YyM8--dc%__8Cab4!KMS{IRfQZC})e{auGb4%;7m zuxI_YjceONF9!!scxd-eZ!$=`bZ;`z$D$lhJjZJ0ax3i~Gu9X#4Ds9#vAQQlW^zW#l8l5(Ety;JE7qqV;K z+>TQx`lHMDOSgSkD_pvN_LNC!ZksIj>QH1tkN52YwXUP*z9}B$J6t^(;|z1dfPQ3L_s<;%_cjC`gBhXo;5Uk zX~@y35q>Y-|JgE(0O4N9UnLl`9SG=o-T9*ZQ|q(lr;WAsfpyzzK2Q>{0~UvK^@_!F0O@J~UaFtIYR>d%qnqp9;Ub@QDo7PM-Q{eUj$$gO6% ztaX3kzJ8qMxUlgrC(H|kW-eRcxpZu~$ErUoXGG*Lem4sXyyUgqh3#PBkmCH$eSz12 zC+xP%%129&`ZKV;rZ{8A_xk68TXQcYp3XW`e`(1b^|QG5LEj(#%KQ_Oax6<)6jHmb z!*=+yqK))sE)m?Mo#zM36Snj1V4D->TZ}&INpy7$W#;D{CwuJ=ET3^}?xO`u79Lqp zKCfp^?W{>NCr!%^Xz>u*KGc3d{Too#D6{|m-uvz12kK33tktXh7k$r%o`=3X_WJIJ z>)##!$TGr;uhdqw#q>=dv6DQO%~BLAqt(kW3*rr_q8@{r5P!}ujb5V#_DpYNTG&FH zm5(>3Y~Qfs(ALdszARt{%(DNhGQm||GGZ?t5HKiEBV zMm8Uc)Pe2$y;CO5nUwBr>ttq@r_(O_B3h<>!w|4M>d@{6d%pF|9f#Vb8J^|LSK0Ml zZ}2J`%+tskO4CU_of4foH$y8ow)j%DS<90izmXuBk?NHCo=PG=C)qlBY53Ui=20_g zgsO!+%9IHI>7(Yi>`pm_I6FCd*tS}{Gd*RJWXiIvv;F97??L%In=GGxb!K0%+2mE8 zBAdh7B)mka9uDs9ZvWaefnHu5QleK}QBqa$v^J`7xan`>);hPUMP-sl0*$`>Kbg9bUq(&l$$_*MLSG5_APD? zJhi>2dQNuRZEcG4jd$rS7ZgJM>bdlqFBSF%*(J!+w$cmH2@s{S|3H7} zwR=CQQ%Eom*LrF+*Ybp&k<$j3de2upYqYXkQ^ z|F>{hV}*8$?gRr@qo>AECMQgijJ1q@>Rai0Yj&!OXYTjfEILEkFy3BOUw_a?z zPA^+n!NAeOBlp@**E}d)Rnk;(r15(9_K`?g1ig~c4P8$hf&$sed^6!wUJK(5$xxk? z#)$WbCrfOl=Je{2_3H5ygOwxV8l;>3w7zN|;aoYc#7oz=+;6VWd-v%!H+Am9)1*4x z^7=y+yi#u2vFg7qo&y)<<4A9a0-~`Lsol`79${xt`=i#urh&R$Rig53WxFdm4S}6M zhTT*o*uwp$Wo!7+^nrP-S%C3tz1v#PG@fXN>;5pZwb*YXvbT1a;?QL;v=6rJu)JmR zN!LSol9d1j&~LsrQ|0vgp+dqMBh`Lr1!hT(11#wG+UAl?=tadfvG&mF9)tD;&4EpM zEvl}xVOwPrX#*dJdx5)z0lt&;VH^@Q8$GjG;Pjd9XstG#r#-|OhU};qaspruTQGO9 zlQ@p_%6lX3M0(vM!THD*FsDJS#834x#SPha=|t&HiF!Cf(jf@fLgobB*Y?Uaqmf*)Fr=rUNE?vmKTP?QS?{yIplV z<#fUNobh!XbB!~?9$|(kPGllH%roNLWh=QeH8$#RGYhdkYIDr8(WqC`hiySEQ;vnQNOSTt6EZRT`Q>%ZZ2zk(ACigj#$VvvFXqt+d&wjS+Ds;l*7O{SU80>0op?ql5+rSWE!_mbGzXYvlg>u zhKq$GRH*oI_nVe|O<~P>EpJ-vnkLkdRc%#~^~X9sj_k%(GE)So##)W<{2xq;^intv zJ9K|;#hM?s$$Bo0?o}BA^$b4ur=UbLNsnWaW5KmKWBbB3z!tQvw<)yVYmsj#7hOOM z(ecs%afP%&b%NMQ^%7!rru?K-SN2YUs~)TLWvzqG9Y8~U_3@g24M5wC&gxEnyGHY_ z2A9V6)|0)hqi+-wP+QbYxkh3+9Mm_lE23SiZLDoq*VBP6i80=X1oJyJ$LXmJdrdJ~ z7Z%%}w;QzzF;(bo(cCU*NaXjY^Npzx5#N|gzVFZ{@{y&&LNjE7m0kVX|SS` z*$7pAF1uX*soJnHtu?4)ONV)TN2^ZTo{svSD??8t8&w$u3PrI^gfZIJ^x1|S{R5hI zoLp*yvPL|8tU*$(`Uf=q+YPQv(vl#ZGWKH5IfG;FFd6)Pd`ilmHs`0b;euG2dz4+S(ew0e6*f$b(zTw zl-(vkv@U7qYc3TWWt{}A@vF+W(tBh0NYdz5X|{SlxQ=~^U(0{US&j%PE$p&tg`!SU zHWbk%ZvZMkmM}}-Rw^3wJ97t0#CfVcKpWdh^P7H~k(rT`UYEvx{&enI?or-nUKD31 zl0__6iloJ24{4*^MfJax`SVm9wJv@E+{3mN4QoHs?bTi+Qn4K&V?0Y)AV01sQwLKL z#%sP%OIw$ro2L1lmkc{$2c?c9kB453&QZoe<{Fz#&p4R6S9scZK^~Lb>>RIK*&3-u zZO9(@PqSxt=Z3bJ=FCQ@=}b#mM@R3Ap~Yjp zl3H1z!bRPKZ6gW#{nC%*OuR*XgkBq*j8y<7EL60^z{CQu^K+c;sAuP4amiq&##y!{ zbc>MCt02##_1H-Qq2F7(OTQB}4D1EhL)+-py9H1Y)rB_7#zwXeX!rH@TK9h+_%hft zI51c=baOOL)`$rZL%yDNvcVG5ZI-KSO&o1qYF%qw!(8}I{q~ROXVO7-*|rkvtrlC1 zKk70x40w91I#>_ob1-m=d;#`L?c&}ce}N=Ypt&SA4gXi9!1iG$7Ovt8zsTr?Fe?bySuv++}+(d;Ba?4 z{J6ti4tEJ5zP>vY*W$Aa(QyzBY??E5Y6*T3KO;m^-E(}(0#_g{)+@ymru z{G&*z&>?>n?~XhyCozl9+>lesm&?U!hww7=5PCdW2_JEPt zXMNOHd@mAF{}#J&Yl0*FfB5OpHO?XSm39fA!>Qh(Swy<=rSq3&U%!7Fk$&b!08Y@l zXBW*)%`21_$eEZO$~v3V%X=a+LoG}uTIM)vxIQ|YIZ`2x?J*#*OQl=Ki}IVc${k@* z2it_)0}P2I^aeJRSVevz>yS6_?I>=PP)kY`c-Ywrtqf%Odj_yjFj7%Gp%o+qQwt{_ zE63l68{&FxUPdJ8_a#DHAa;}ItDPX*--*(=LcU=|>uTqZn7`d)+<(UST_;>SU1eMs z9A2x&P9_o5t=*8W@y8+)g8h9iPnrD8e2=$ppj2o@dq+o6GUUKGRx&ZHAZD62vOeJjggYAmhd?{0&{$GQqLQ zjU*6Bt|V8&40np-v$-KNi1cA;$O-MTtU|uZGl;1^%Z0d<=-_C5zN$D^2^hJ=4z_`H zkUic0&9=`nojpSyML%nAWSjI*%$0OyivAq=4_ii*r0&zHEM?loZl-_WVSR}-mg^kc z?Ca!R=WXsE8M+kxQ?6jN!|b>j-Kjq1m0*AGp1iZUR9+|#^HlQQ^kG3g+=U+?%~ys% zKJ!pzzuZmUDpygyDC5+Kx3p}G53WR#DQ{6IPE{B-^WTbQ>=N8Smz>Z9+j#05?+SbfaFgI z-RIUzHH@j`8}mHp+*msPTkHat+4hMoNKdDJ(oanNY!96y-5+B!W9Pe{IFoD&TYz%o zRnfM{L*uT#P(321MRxkii<`$yNGd$M}uuJv>a?2Mq|cV!G@^0w7hNJF?> ze}#N^jy0Rj*_g-pegwaBNzzYkHlD#Aap-YqGE(Sfp>+j|B-c(H5%(;nva_OXnW-a% zV{P;oieKKVtky~+UTi!$iT=VIVBfRb*f@3$lR+1yGsrV|O=P{&k{=Or`b+x!el^@x ze6Eea)|1I}BE66N8(XXolv25Y!Cryd;abu(ysW)k((a;jO5HA9ywv_;WTB&pt77(A zijY^deu62|FElKCDB40EY1Ac3Fo#SX%-`5X)MsRZ9Lv4*xA&aLo0_NP?axo~Ebzp7 zt-gN#TY*3@9rDh0anprWN*m+>K{1=z+w5wH{I6|!Y|n@(7r!y#Y5c<2kgKsXOQYn{zF@FRLBz&^R5?KLuy3g~;}5K;%djO~INm(kSWmQqZ279>Z0v@X zBTNWC^23a)AYM#k$C{)#ZMYPR8rlm9?%fIwzfxc$qS^u zaxZm*&Kp0CfqJ5vC3O=A3p4qzT*>f0e=E-ru*T}XP2rc~79H?N$bM8ADxNHYTaYWt zL_Q^?dRuvJdE14Y;w9rJwc9e$(bF~0<#1NAnM@4%!RV^g7ejmlVWsEh&}i@JdG$py2xv|32mX}iG4UJz<59Ae#~B+ zT_ZQ>Nev|kLsT4Tg{B}rb&hx_;tJIE^vGSGEoJS`UYI-DLk9A~SNJCKR_z3`|J0U4w# zGlxw!KeZIHy|iW7*4tdRdDfGbn&umfiyVmzRkHa1B7cV;N4D^-@9Otm4Z z<3sSW#4aL{*pGb%q>g`#D6#~rixNO&o{6$%I?_&{`^a7&(I)F)ok6WC^KeaOZO(gRr5{KWj$RFc_1 z^hCEBXN>A-WqcnX+RP!2;)U=8yc&KH3nMqRZPJ)%YItF=R`70ceCSEY7itu46v3lO z!X2@tG)er6SGm?)!Duz%kklXWh)&8SrKVzzSXr5@Z$Wq9m5KL&1~QyDN_}EKSjO0V z)*a><%v*9j0U3JaDDo*j6hTx(JRdz3T`rgbh2kyPe-mK~CqWL{%t$wGFIR`V7-2X! zUrau4>?F6C4q9i~rdV5=_c5=@7g!&ozfwXN8nK45f(;^XgjZTQ;w$@`&Es&pC|616 zYkN!EK})(Ro7qnlBvzv>j9ZWyt!W#KIcOnV#&hvFJQLl6bcXxul+9v7^ee>T#|Cdg zq;xM0#V65KO@qwK%@xfXOl#RYbUKlNR8!{)(;|n1tpYOw^+T7ri1Y+G%#3l|iW``; zD`|B+<7#FeO4#&aVzbCj|JnSaxrbmJ-RJuec8Uent@=MkcNjZ1V~vSPWJhu+{>_-E zBnjst7eY-#-NK)^=VF51f!J+&Z~x#@T>~At<|h<5i28JS3m*!7^{&i4oV_#mqkouK z7C&zBx+=uVF}EFz^#XgFnuWWJhSI+9GVg-ii#fCM+Xmyr!$<=A!*M*`l`^AnQjyLD zsf4$-L}I1%AefuqEO%9IIRAh@D_mP>sXW%lqd_bM51^Zk-s%DIE@uke^i}bk%A1jQ zCBKI+F0?*6O70JKi?D62_Z;tCNQ~^@Ej#IaG_0A+oZ~xto7jQ20St&`hN9J0Sn+j~;pa*S(w z#oiZL-O|f{<-gqe9+Oqk+llk2%gKMNs(WM7o0Pr#A z>H7qNuu>?vAa8YM<(~tx$o!RoecWBKm5Ly%2roO@?v0rmcRDW5o#kw8>ukC}B1lQ; zU1*6XnzJBxwl50$7|Ql{#JSJABd&Ay(dMdDj>L6)bZtRo-LdwvTwn-~ueRh|r zBN5Wu${fEgx(~)*1bIu%wLWlV#;%Jq+-IE0)&sB?kT2Kf7=KJ&eDiq`L^cY%bl6a*p?!Aay9oG zbA6(q-c1@1eHJ?5KkLo+)(T$Y(~ONw)cz%AMa(e!MTSS(Nae#zJY}=meZT(czz6zM z>F=d;>IETg6x!2oFvcsegJkR;J_x@1SsEO^;Q2RaOHM&gyU;&keWV_J#JVx&Rl<|x zbp_j`+)SA6yvQKPF>y?!f3R|3ULZN-<|c`_9#0%%*IONS*?P?sPd!DND;uNR0#oy@ z{~Y~&-`8^A<1#mUYIAecO89GflPS;qyJfw3KD(40X)F``{$e@Df9(9;<;R3<+@HxK z$QT>-v!)T&GPYut3h*2)hJ$6i1M(MpJpKof zB@%Cxp%d&a;!hR)Tx@QMjG}FmnV2ZlbFrjDPO}#MD3p+&gPd=8eP)}bw4688qI)R&Q!MX{?J5!6>2AAgie z-}s|R_9fp#s3!;7{ns(wndWF^8)^PPkHsCjCgg^Gc#(WOpYV+ir$}FoGh~bjv7WYd zw5QvWVZ_=>0K$(jBRI;lEH{!fAg{T%T<`&B1A7}o-L|+~HDfEqj&xd02=ajYE#H-K z{cGme(HT?nH*>{|?M$Y9se5bO`Zzi^&868cn+`xf=_${MRtpynjSfGEt_3-h$cd)E zY=|q?eaJm1ro5w=`4X{0>%`v*Ow5pXQn$$9_$&TWD-@kysJF7spTV zPsqEM{a+T6T_mSlp3fT>{?(6D>6+F=XA9R+$41Ll3PCzZQzKNMhj*jrtao>yCf8W* zhQ4CH*_X%a2{#gN#*d9zV0{M9ZIE6>h`@S}kar=U@!yTqP@3SEOfwx{Vs^S;x}Moo zCKIh9UkZ=$*3A7UyLfK0HxhaS-^-$0)>zm0*j;fwW8XU8S=P}F@Jld`js~QrSMax; zVM5yD?O4gtBEgs3C`C-D!S5{W?m&2*Ss)1pFA;DLnOOXluY|z^R>a}Hp(-BkEIn)vhYc1PI z>tu6nW-{*5CPkO}wq|>OOiiDYaWIDtEEf=D0`0PPaRePv+h@~!Xvdk#KVpnnPHL*m z{Qvkh4zM_nV2jAPY?k$u^J&cbn0F4^5+ElTU8OjV2oCVg^*;3-3R%S=$N;voQ;wYw z*VNU{)Dr2ym-Bzlos!)r`+UyJym{X5fwEj5$!07fWX5l9VQp*eZ@FY@#(X9QB2|=9 z{M2yWkTr5%sIJ!|_nMD8n!9hu^-UlXTO@3WJ?}7^o?vs-1>!=Xx-=L1(P^v(VIeJ) zhkjrZtgRd~92cw{^vkM(E#S|+mh~;GQC{CbJ>i7Wg3hp9x8Jq@Y3*uq(kqGY*fyAd zwrB%nR@lM0I5}Dw^ge~UVrlLY<8CC4PFYZ}N6O&D?XD9rkM$7Rg?{?023K$= z55J`q6Nf`)WzT3(#I^m%HoOP5nZ010=z1F0J25litNXZh1yN2Z9r?reDgRXd1n;y! zt;i7JCY0|eZlR`A6A8sQCT$9vytQ+lX5P%4pItUD=o#(56l@XM&kImbk}S?1H+Qp) zHm_ud(JRT{Vg2zKmx!tK81r67=eS;wtLQ1XJQ+=x<0@ob!~8=OMdP$iVvWct{}WF; zPjjCm^hfl$L}@H!h9gKRqmfo##`x}`IA6p3b$Jy%*ZoT)Z^df*2)rMY4tq~WU13)b zXLs8Z(434wh60 z;aq0B$hL%j`&oOSeS-P!9MTefh?S)dm|MD9C7dcyy+9zYyyHFd2UbwmWxMzrhXv5Q z=|A6nPxwCZN6+jx-gl8{AnzI8f+RuCE9pd-@z%`A%S5t1WM9qe;>!(DFzyUypSc<) zg;KB-I^mIH0}C}m2SGW#`c>5?w-7IjUz7wiK%Fv|v!&W5Ti&t;l?!%rUPif3-i+** zKaXZs$m0T0zK_}x^tr{TZcKu|=?1Rr(g1FkKP|sZUSZF%!1!oU(1V@$K_^(gIu6D5 zPJEVBH}NmG)AkpPJGxv_oF&{AZz*k&Y7}P8j5(E9Af;N$j>H?TzoB3F_}PJ?`Cqfg zWi8Ii$sV2e!z%}yM1Pm2s7Lh2@V!|^U;UE0O6G++(HG&?fVU9}&*Qf%SI`>FXxrVG zhw;Ud_9rz+tPuCwdCRhazK>nemnqey+pxds5wfJ(+5vPSC7Rr}MEh~;CpL*Fply$K z@P~4e;e?=S`mu~9Ir08s(J^Wl>;mi~qUQeQ{ie0d3!=HvNZcPhlXolgYsQdFGVezq z9mcuEG-uUZZR1wOr^WePy{wa|rp5;m<7$U?1krHqs9mmwG^ZEZeC}C^oeMaUe#S1e zXTf-~M$|*wy<2mChdhW0IYNGBU~ZHJy_F&6(IVTyESQ=y?=Y{tFLW!nS4MpLjf_ZM zR}S=QcDtJ;O-gx`a-hK2`2Egrrmy%AEmmyLrG&eM?uYtDs`8`39#&BethN{~u4zI> zT&bAb*5UMVBq|+@RPi^--=6z4&+gZ_z1mUgqV1UbzxdpESL{XmXqab)3I_w{^O|Ju z&RUr>!Lu>gkSEnWsGquO-e)faJBVIKwgrRozY~{)MW5I6!TT~e2lSA_j&vS~#|sQE z&>?YOY$N9@%POWb*y9#um;md&aCRsy(wM&?wbNE3UGd#yGa6@x(q{4%Vpc|SnZD_{ zxtT|Pe$QN;yVSoJ{EsBlwit7P;)N`Qt|e)%ZtNzbmvBE+J1{b^DfEt;F1plph6}$- zZ8O!fQO>%qO3n?|KcHNB!t&sk{5d(Bv%`=#+Aa8!AFNNJTsDilcKr7^-c`?HCjL_n za|wZXPrKYj*`KrCW}6_Z70@1$q0C+z=9=tU;V5d=n90OaBS{{`RSazMyvRT1Be+*; zFY2aki@SRKi?}lGN{*f8cT^O`wG!f$NTXmq*!i6t7#E(w7l(E{LEHp{rJh#Qy55uk zcF&0~Li0V}v-f3I&pMm?(EpG>ZZu$~+ZVdgcq8te8+A(NvhbWNUE@M9d;c5g6CTQo zat8y&3sNSwq-C~!xvRDNXiSD3gMPA_-|Ro1cO`eJr&?$-^zU?Po@KS;sY`REJ9=35 zk;Ans+%xaB?CL*9XB5nQkn_g-Fx(u*^NrLL3*uZIvoj{fSuC@si^#*u_k-N@QekXW)IHQE+RpMW}UTkx)h7NtLmG zh!qoV#xHekFfBzh#LXeZTQ4^a5F_0AmSA`BCc4#>>OA0n?LOw3X76LU&-SIef?afz zOLJL%hxf4eW^gaq&swIbV_w|5q@T%^lG`MdbatSV)n1{@Tyy5{KaH&Vo@b$(;%+0A z>TTKXtQy-TZoGSyy$6)*nQ$*S*~8@&&;FTJFK1x>m_QA_sy>4ftfynL;u^&N?cQsj z0?#!SCI&9$iP?K{hQhjLo;bl6Njgk3tseV!=OkAtS98Y-OBD1!MQjsF^Zc1pGkZ`@ ze*UOnH-R#qQiu(8*GfoADxGMJeQf=SPZcZsjGz0ymHM_TL&={C{$@H8Z=dQK6SK`V z-BHzgl`-M-)y!y@;7@M_Z;Ia=n!sr+b;J+g~}IuGaRQY(BbMY7#Et z)AIwK^?~wHzhcEdu@~(ocgMKoxSBD~tfMGIKOf!Y-VNt++9fxsmZ)3|~SVB%f5{jMu1{To1kTsdbBWmT3_&OMMn8>dnm- zGRJ4X$gdvS3jJvhbKr%HC0T%VVU9EE;2ojk~vxBP2KHrJvDqRnM7yw_VN zw@voVY&Ey6cXJ>R9?$QFb~s0swAGHCpL9LBe}SuU8DQh{(T3_bv4n7gUnFdkI%;pQ zXKZ8V{rHRmyOZB1wsJSORsnrjmC}MgQa+RvXyxx0I2|^Lm-W*mZ_aR(aG#5v9orLp zmRZmbR`4r*d$SXMuK9T}XM_JPPZ|TL_Li-V*qBo>ja}KcFk66Vp|_XrMc+ocM&!u4 zs6|`?^=wG~V>)La64O6+ZEOj5n)46qdZrQfP`(i%K4_x+nyk~q>gT1*IppOO=oR>N-#0-nw9Xr^4#ktk$W>%u- zgD9GERBnw}{X@OC`1m8q|fzU>-znJ>V zZ^w($4igS&KB?xS%r?B5enz|&`4*V!yXd{@TNNx2Ev^*88nLVFTFl$nhwc>TVe@LR zUypK8YABiHG$pEz2mi~1b|vieZnn4if%&#+KVzo$;b*`P)HGVj;oadgzQUe0o(_SB z(Rtb>0x_MiWLO56Ri+`;3ePnJ<+>1#Yz@s18Ii-{OuYv&njLRFZ6mi>Jl407m@=jg9jsK&~IP*owb(hN{rq0 zhqWSnueroe66z|O^#*7OyajQexCfZTAMg~s5!L|eIaqI_o|n$^yCYn% zbD&kAdWekH2fIuqy^!CEncmXh2^Q8Jj3Me{z#m(I6hjlRz4$5e4Ly>*!>(Zv>J9wf zS&s*NUsyLF3C1`zAfAa{1(eM3(GlW7r6b(G0LaXf!T)S;RMJ5HFY=%Er_xp)Dmzp|ABFWJ9drdcOcF#v>>i-mDj+upu>2{bjowRhs6FMr;zs^R zv=M(+Jgp8#Z<9BfuWSu=Bn_xW_)ow=Mv(e&=H`WRspuGD6}6d;qbrlkp`QJ9UAqGJ zE1(zA&Y-`qnp^oQEan!5#s_lzO@d=1cZ4=zpQV9&;2`|oLr>Ahsnyi3YPxEI_x{38 zl0}$mYzJl@IUcRAx#e?W70IRy(b^g{;Jev+M{T!4$%n)ZfG21Z;>Bb+7vw#}E)p}z z!XyvKx(vvjY779JV6ZDd4W0wNFO4>>Fxw=Jr2;X^+H~^&>em6=OBjC9|;LZ-X z9%NJk{9OTX2uBh7Ni(&R+(@*R74KO#0>wl@Gl}xFN^hj!@tO5HA0p@T)sP_Xk zRyq((h-8NG^>0uq}ELkhO|)_4$yvQ@O8IGCsrm`G9-;75TJ&e0> z2iD_A8$iy6@zR)Stbpqb#D>+!3ln{a8+ctP-yrR+{8Ic2Fl!%+Pvo8&iS)xL;x4h0 zn1R0seYMp|t*u&GouS&b%fLLaPya_h1WX&9^~u1Af*`%&H2xys+0Vqj8mVeUaT}K! zULM*Lt`t2gjsyK$@R6hj*mWK0_hcKe$2sa~X^?P*9|1r!zbUn}Tlx_w6@vUQo*M7q ze|2DoX9Qf+fL~Wfc`B~s(dYuMCSXFIksg4&B>X#RW}33)*x7Vb80S_hxdIop@L}Pb z{1)`CBRnz|-j74BAqUan*b%HL_RHSJ>HF1ZWXFg+!Vg4nWqh`IVa#}pa4~iC#*5%KLrJ?+z@f+k3sy1Nt6~WV> zTo3f^S{K03xh`b@M%?exJh_%y4D@;$MxEhsQc{VUOgX8aL?%`TsiKvZ*9mt3yK;WC zfN)T{1@b&t0$HDGMfD&rgYrABH|=nkVh%FR=*dJTGEcJtbwoe;jgk)UJwtJPK3*Ga^bGPz zPtrarBNaGf(Iy)E(FOQP!cVj#P<$BbH)sQbaglFm4PqRXPNU3b>R+(4MD?}!gzv%+ z7tTq4gPv^o3bG=wNwlNSlO^EyTPiOV6L$)e#0hc<_})~kAn^b&=Xc^8uq>1Ujs_dq znvP|6vMrei*jZP(Xmo98Nbp-|OY{hg^LvR;^iZ}3i!irH1^Q7D?VN0uri%robh$6c ztALHi58)=f5Vin4ieAH}5Y4EGbWb?xAAs5P;ZoD+&~Tejl`tM1D`n|_6J^1}d+$J; zA8ofybHEOpD44i`|HMxPB-AM&uPxSvuuy~ON=#?wJ`JaOfDU&L+B>REQD%u9qN~E0 zK{Ql3(p)&Ij>S&XXU#U-McY&BMsr7|JCy&9Qd+zg{l@(hts*`LKczhJn4W49&2=q3 z%`4dQ^f_V)7KC=04>*F|^<-_P@>p6c))Z0ch1?FlKLZ;_Tqc`R6qQ4Kf%+a*hf85V z-2a2m7F&aUYvUux_LPP46W#IMP_ELcTggIW_MAT}57@Nmj4ol>lDzca8g37BWlWr%q)TZ4Lv9#IzxzeOem z_XTbS=SI2-=is|{@by$2Q-d+l0b&%iAFfYQS=igIBV6T1hKq%+gtl|L!5`X06|vm3 zTbxB4)2#d0Wb${UnKF~F6Q%>bd<%Vx18iiH0B6@o2-{9zWE#;7kD$*$AF~V|cvZ%z zj9gXN&6SRjx--`0ot55Iu51pWPP6b00N1h}xO0-qLw z>_h_Sef&6SrRUSHsW|XeW*d#w($Y+RDYq_iB@*Qfeu9*%l+p_$LG%>t9xj1+ksVZZ zvKHPP|UvsoUDp^Ma)8~)D^lb zU6wjZRKi237oKAcnDVp+s;YEQ%WEC=;lM@I1n%#Ga>T;y?8Z+6YVH@}C|1H?l@`Kn zPKzj93!#+!QT?@#ng-rw3wVBkaZvB6JyyEPqotuz7dfij)9OO`9LOzT9)SLhCgP>Y zm()(cgfB+*0vkGp)G{pK9oW>`%0IwJ^+wqZ?YIQnP23}olGTVkXfHiqelIMIR^mQH z26F47WduZ$WK6Rf2>KVqWbPvl17Ar!ybJaRFnqiOM1A;<(OLf!7@K}!(qY(U0(M$~ zqh>q%k(o|!AzR`Xky!nvk|a+D=Ayw$FRh!Ahhzax`$0^>*5J*EOu*2;O}qn?{5bT4 z(OiF{)>mNPKs+t96T8UEwN7Xju@d65G8m0MPPHQ~fMhfq6uw5E2mEVaw2nqG^fC4U z;!(o*R(vx47ha9nPaGkfMBx9=9n!~WbyY(7F2w@_3M13%X?-}l8GlSfi4MT>5k%74j<~Jk>Ibf{RbJZd+Go(rV<>_h@BLV9}TqNDp0dhZh z!-br8@ zI{?>{k(w?|xrRUOp!EUdkc@Vu`Ts|oN10zZu zVXDB2gfvXHsaLclkkcROjg$vD-vEc+Vo2I#U=tWDU4dt+L!Wr1jWISOeb6Yn5}0CE z;fwJnz{XJ1_@J(lcZtP;y=QzhIa)pXCh8JCg9jOfc((P11af_VZVzq;vd_4rH`lf) ziGU9Oo8*$$DNVK4pr>0%6?77?;4Md!(GtieeW}`B?k(Dd<9r37pO`LnQbt1bQv)utdX9o>feh?^uy{X=FF6Y)`~A7+^s z5-rvO#-43rdHJTIsSmZrAm>*<|A?#tJnzS7e~9ku2lDnBcB6`35b)a1C~kF>Hpw`J zuEY-#--waKZu}vJW2tBbT5Egj^If%B-EMh`t(E1>`KGu*&) zI2JpO9md)NHwmntv>}RFN)v1XBNmn)tAmZf=qfA&+ku5(21Af1`W&s1+6x$LhA9)& zd~KHT2-%5#M-QNh=q&i&Tf+{VVhhpM*k3TK_d+)sW3{157#K{>!@OTqJ*34O6Orp^ zCUyz`fhXfV&~&|nI!68=y@re$uy<@MFm%i(mg2{;Uf6kT8enukBHjQqL>}H7&jdf= z14=+SP@@YlBEHiJWC1E;2|h^{_vI zQ!EVUYB-ELckqdXkEl*ICGF%aVmcm+tww4X6ZM7AZW{6!>r3MsPooFqrEWQUHi*Lt(Todu=y@1Dtfz$fB=s0XT{u?omC`S~;`=b>N74-B#UZ4zz zdhG^(VhP!WY)U-Dn!$URQA^*d6$d`dx!N*lhb8C^&~h8B82mN|zOxwJgij-90q*@Z zA`QkdO}hl#MTE+zlBcc)KYw;8we8Tuc3L!$%i zfN;PTx(&IfA5ss?MIhqN4N?4Kr3)}dT!52n1bl}rz`OKoKc$uNT94ILb(>OAPKJ@Y z6tIn?E0eTK`f_-#Gw}IU0(R0oV4rh9`5x%|wTr5*3U-2-$`tvFbV*8*8KoDnbGZKB&R=1T-4$I0Ja$fyH&~yq-jz2?m81>Q zV)=^VRnh^Npbu|6gIMxF^ zf&}1LxQ0qPU$80=#d!rQf&KDBo9n-;)#cUVpTa_+ zoY-F4DZd348AGdOoP@RcHejX;|;Mzt;QIX%#4Lis8q*@oMAtqq0V(N-=FEPyAK zh*}@~&+ae=Y(k%bj_pvcKVcv51yNXvKst`|M-?c7Q1>~L5TWPYABiE75a$mVHFmWyb zwi1X7K&k@@KlD5Oo7w{Wq%*L>%Z3^0DeMpK0zYXo7Jvv(0=OCKVz<#=D2v|z|Gd$~ zn5nM^4zjAiz1kC4nab%CjZ4VCXdA3LHUhp^AEE~fDP&Sc-{CZ7M$CE{b*0=LSn+O()1~|JRJA#jYbv?{YYKis4ZIn47#X9#Q(8)O z!A~g0_ZRj{JJqPs4WCIBVV&kUixu$hUo$3(hjyB)9adJzh2_pNra0BU+FiX6(jDyw zZT=Ro3k(>0@nmd^QCS@&qR~6y4&n4jSz#Wmdvfr90Uxp^JDP3J3QP;eL02PhgS^g= zNqkG{Bz=||{NKI-?wdT$JTy_3dX}-~b8HQ|Jn=7*sLhs|@V^K2k+#eReG-&IQ zb1ZYWj~g7D=)$bGp#0;ceQUH zZ`-59gB^)GkX~$0+9)yKbHW#ehs*o6E13u8sDw=3jAt*QIczTVS#Gji;usX|NM_O|)v2(E4N_c7*k_ z8t*nIBkmB(_^;8_H|44H&8B-uMv2a zS2w3=PLI6j-qN9x{2rw>dV)gCYatF&Huq+pL(Kdhc{BexJSA|_Timn5v&oNe<(2Q) z3l_1XE{`kLb<>`29!eE6Hu5ih^RwG$^vSrHJ=VVr+NmM+C)?aqh>fG)5WmiMA}Wk) z(H0@hpXsUNDeryckBJ1K{EO*9w(c=qV&BK4*quL zd8kkzuraT64wL)XvoEw(++;i<;{%dLUXpFd(1FU7h>J(=}8{9BNpo(PoqVl%th{vQ;y6 zfO7Hj2R@$r8m1#PAydAnI8N48!&nX(6K%|WY+vkU?4;!q*bgb85jOA?B3*VzW`n&Z zQkkZg){D05)^27iGn{w`yX4E{1hFpfjQ-8_j7Ipe(rOs@rr?LDKI~-kLrXd92uoYj zeab*bskiu?&_(}K--tk`h*#`rEGN&IO4`=hb8S;CSD7AQ7j4udQhiYswu+}EO&+eA z^^!;gn@aUFv9^O?{~30NWjd7WinN{U1-z(7LPQh;JFH3_Gf%OO0Y zbOk2YO=4Sl25^Zp>M(UL_#3_P67)V(VQV=X;KZB1(OvMC`e129v@r144T~O?RJ|3k zlNn;(V3}drYOZHm#5AWyz=C(4xQoRzxQT2fH6(|7a~_ih**KhttP-@@J(Z@ZL_;nyXWl81Xy+NkR%XB1X5DL9Z5mAH;xE8M_zn1X zQFRc^uNPpitBl+a^uC#B!X{Yj+8fxbS=X~}ay~Lvtte&iYootK{m~b~7x}GT2k%8s zFh$H~%{goZx<2tQ_y;KP(PDacZM-@k_{nQ4JJk}#CTtAFnr>ML+hl9hbe(F04pDNV zXF{U`X@T>hYtbj5_XWgJx-gs1bf-f^C+I(IVOQXmIzkO7m6hi5YH7MONj?ST67XC) z#f(|&S}&S6GNp;IzE(QRxkG&d8G)T)o6uKXjP4*A<~W=RWRid41JLP4Si7UXRT80o zmBBMxF0X-8(z3{X{3CtEywP^VzRXtJ+>n}r+>yP}G2!vSXuu0uM34Deps#+!8X9L0 zGw-R6#22XFC%vRT9XK~zD#`LR>AGYE#$2EA0y|5lF+WYaEn!Ph^L4N*O56}!?rD~9 z_vMDYVDH=TI>ZL3mjvAFt-udjf&MxPf%856hWbot1wP*``ICH4VfAO|ZfdysnynjP zFTbHG8hwR?&<g(V;7Sy9Ve(4Fj z0PK%oUwV6Om^w=tE;j%kxeS2-cB_}-C9$NqQMfHkmK2pod(k(|U#*6v5xWF;Xr+bg z;cCHEf!V=};oRs$$i%*gE+?Ll4ag+uFR#?zGA^AJKZy^4NwhToCUP#cGuSMoMK+1! z^b5pf(@R^DGt&`klbHrsP;SWe3l#Rf@Fj%I{1_N7-uyvnxju+U03_<;_NCS+)NhzjGMMjK?z!V1&RqdL6~O<-`+=N; z8m}ZM*W{k^Dyf>doDXpKA~hooA~zxs8_4$)|51KJdXmje>DJ=*71mm;8E>O@=BtF$ zf^=wMq@vJPsbJIqtoTC2Ni5fxpca;H@gJk3cwRUlZIzqIC&gZT6|P9+Rb(=M9lkq? zENU8Oy=(hoy=nRa{kf_9K6(gHQnL9P%1dJdRwC%UH zGfgBbA%~Q+;xoY_#jDMbOT=i199abZ2n zV6KrjsJmeuu?OUxg>&a$<8f_b3%kTN#A$Q(v=#w7_)A&~C**CSZNv@`G4h<4$#gOY zfqAW(IYMWFeU{XoD*pk;u0y-8E>UhuoyDT!KT=iAiS48r>jCF?cQfF0D(LEFIY*3C zOGnQ@7A3(IQBL9m%^e-{ohkP2rsq(<$7*v0g>hhqat;37gPEp?S`*?u`l^k!g~llS z5;MtGIc8dHJNG_&Cz$7#h@&ElLQlgR_^V1u)K5-j(;%Dku&E2vlk5ZScu8NUSBLg% z1}7gjy@YyQJ}Qp_Jpo&wDcOG1)ye%Trl2#;(wbUfw2|I$l_Q_IMbLhSshj3vwnf&( zrqR?^kQ33}!1j(HSBK@%h`>L4ebA#JRAJ7yOD~~9YU(y zP2H^h)Mmn-AOic>h-%Ru>loS#Z%4MHzK~bIj(?qMUJDbUoKVZ?2pD&c6AkD}^iHA$ z(n)#6Z;nJm86iFVfun_*QaRF#r)2B7d^4TpWJORPj0O& zl;=rRq>9k15#%M_gg(a3HKnmbs6}WiWiNNnU)b|8Kg~ZhT3f$O-L;$tmR1+lM%VaH zp@RWO&>0@fRp*^T4IxFSE?gE)im|d)eXI|`{-h3?npqoK->{YNEiwaHZ-w)|<+ljt zg5U3?%UeR=m)*DaG_3-?Z5+{QB-&VLFO8OG%d{K-4|IuE2klED>;SXV5;4Uw z`|vDnt}r{)(KpHaD$qulhUS>_V$$Lt$9}gzhH+=K{GV`LP{n5QW7uo?ODZ81mUc-+Tl3PmqgfaXgzLR(!>~b)imTxruVP?%gn4(aALQdx920HmV`6q^d<(+kh z@w}*|vUxf48(A9qaY+BD_lEx47Wn|E1QA6RYw?FT$S4#5ZySHgF1PJ)Y_MN96V!g= zi8MJ{Gtxe?GkR9~sFx=GWV)DNnai3mdMG{$`Jn$gZ=C?~I4`09R@Ms0YlX`EAik!! z3d&_A1@^P`gT1L;GcWoz9!Ob$F|!!5c5{rc#A2qOX|ZW6dzjuuZo-ei`|tHJ`Zti* z9(7|E;Cd9CIKGn@<*7cCIA+>qf9Krla9Vnjinc14sA5~m1~ISFg5Mh1$5nuF zvmQ-YMf*wnQA;sq1;|O#pJ@j`&M-KA>5D%CFU$4+_>}s9?(i!f(?M9+|Izf-VNrh1 z+uObD!qVL(T_OgkGzKatAOb2VpkQ}=Ol&cb5GfTA6%|2HQ9%?$Lb|)zh3#N_-{bdp zy%&FQ5&JwR=FH5Qx#yljVlp`dRBZpVGw~t_e}HQ(@RJNfw_;WkUywWi>Yar)hU+P+ zL4Pp3d>Vp6>_NGsm!K|!=YNwni@DZ*j>$PBuxTi$Q`lf_IB{VmmOOMQA4M zFIab^d>nKW1#v=zJ@yo_mvo%8l<*GhUyA4kPr;7nr1LS7VyGp?oOpv&qa?wZBdg>a zL}r2xzLIbf?7J=Kce)Mn0u}=z$KkR(nWusW^+DdoVDU%-A6J6D0^1=y4f=o;v(|Dd z1Q6Lf_*(1(0)&`@!=r*A7sciL7n}-qALj|*Ujzl?_yPRv2r#a%a#Ijj2mT2_9&JRK zqVJ-PAP~?d={=zr|0DM-FK_XtR2w#fO2sb4{s!~gB09|NV+J#SvqJ>8!MKWH&Tu%8 ze>eGYnJ#!v2FxDdO39e7Xj{}3upcrBLRim(aMy81`Efw5P9Se%x8Tp=qR^c%w(OH= z5cD_}^KUNpi6W%8!RL=bc%Vc27l7{xlYfDz0&M>?Y!x;bT?t!|x-SlJUa&@4KHL?H z2x%XX(J}NKrWNoS0X&U(s~{h|#fRS{{0{V7Khy%Y0nCpMy5s)Cnf?bggr|Wl#x0Q9 zdH_w4)q@HjPxc{}BL@)wfO*uyYmp-4A4Dx|7ep*82cF#~;g3as;e^QQe>lt?kW_`f zd`kLC+A7loy{_4aYS7d663j<2D?y7OfO+Q!KUqLqv=R%XRK-5nB%B8NPlJ4`EJsWi zx&Xf_Q%DucL3h_)AzAbh^p{;Bm4JQlhgBl@sAIrxy+Qqit(S!_uI8*}5}0XhlE4>? zYY*H7S%_SUj6ke`|GQ@u%XB0Y!t}+A#b#kXzyS7xyg-@QT{JE{Ay${;;W$hlkkbWR zDGCR5lGq9IITfr>RxtY-cU)i(p7$7`gt>rC23$_1ihfZcPmTS8>C1}e`0{myv0}Pp zSo%PA9;}xKOpf@C9>gBT$uZGj-~WiH+?xRX&8EL(jqqNGY~_Q{-H38z4{{hW2IhHM z`bMbAcja7QIf1w3a6br?#lB#E=CDq<7xE=a6%&MQ29f6Vs4vh@5^w%m#0jPU)OTyU zX@yhPg1YAv!AfQ;Es(mG_MJY$aTd)h1jrzqJE01nh1EcxLtX-$D)X2j>_gy{Maazo zyMp1uBbv!V66>cRTposMCw`*H)tlA(RbJrF!Ip?Fv3nMZ<}>K8*`-xkV>+7 zaZC#RH=V>*5`2?bK#Kr#n+%5`Y#^WroA7IJ;}|Ej9kK}~SJX*;MSXlU_Z)kOeTA0* z^t3-(O6*eUP(4NAD-nSo?IkqkWHVG4TUiyn)nI=dk#X1)gyTx*NRLUsm81k;+%!Pw zgZQ{?iF847S$b7=OCAaAUm4^lG#;Lhv;bH$TS6}32C$#wU|pxh8w6s`6?On8fDdr6 z5D^lC->H;IDpN8f+{C;=m_cLZkEMSl3`x7x8_3^#kd00QOa+%H6s#Gf#@LgF_Z{l15W_w(NF;_IC#zmCq+@`9D0q8uT~FpEIDf(09;EXchDdbPh5A^5zTD2Y@GLzj#7S zmAJ@4x@MW4f6A@~#X2wAxE=f{uLmPVnl}*)A+tTUEXoNzmNp}z8-!A8H3t` z!lJ$-KOnz@bDDwp4%VYY>L-d890WIly*vv+t}sYaBnt%l)(!YFL^2e>^+W;;Nr+5S zJ}AEee(wu3kPBG%9z}~{3jEAh9+g8Och? zdl6>wHTN2;hGEZyb20?wV4beQAn-9TUS~O5>MDIL5r_q%S%9P6DD)O}i!VtBWTkR1 z@cdoSYj9oE5lj>IIhF~?v7bWM$&)3HqHKVT{~{OyccCKL8PMwsjy{f#M#Uh4VSNy5 zFuuP4zetmvmWE5l#R$o25Oe%4>RF@+GWp9Eq2T?Q2z|gnLWF;mtrFVvHgZmLCb@ zJRArISWkttQWOO^70N}W@<~J{&R@w0^v1r1wMKeFugcA3>M}z)4w4Gp z+Nj~QWZ@8Rjp&d#OPI@V2KV$8K#y%f`x0-EYsm>p;W%)wfV>v-`JG^PQ+zjB40@T0 zj?S1ti9vwQH`S9$ez-~u3i}ou|G-?T9iBuZJoF^F0cUK|10nfNQb>RN^ILo+EV-?|@%C z1IHq25Y4D_xYeY)lhsC(47(TrgB%AiS|Ll{8#K>p$P5+PsYZ^d`{N7*JU zGfu8xUh*8o#FH@t#A|B*=?|FQw=gtZsWiu3(S!N=_O)j5o$@b@oN3_qQ49^zjQsT6 z)FKG8!2bM}y^wtX^O^#h_chqxbD&P65Io-yRK)zNEGdWDAhmF@q@$E}&~v+!cn@t1 z`-hv0ftRCuah8A$o43rfZLle`Vp&KmF-zWB zIT`v8nwSM`XDa%NZWZy%0vp&POpTokF#rjzckEe7o&b^OaeQn zyiCoM2;zo(XZB-IUsyy)(v%s!vGR6kcEv8UbSF7ovRJ0mPDbHFKxRUR1S6#rKH(k^ z5t^H=qF0y)u>3|{3{-Mw*VU-Mh|G<78T{R$&y5N05x9%WT(vBXF-jfUmh-sV?bAeo zTh^a^T*cB361Yh1#lSFU!FixTs!+E`cQwBnRGOZ!)VC?Of9sfT|I}iKb}T*{>bTQ*rn$TkkB|LmyTUE%w+^S5r_U$B2Ta+kV;GSH`UmR(MbQ-7OB zQ+faDq1@(Y8eq@0Z=Al^np*QNs?P*+-7sgVn_tpqwe3e^7NY~UUGgP5e$(AV>cB&k z+qK>roYk8J&l#e_CkDr?=gs6lRnx63-CwNy?S96poovTW{k!??+H~}-^LKfIa{r;~a^4K)(UzKrsxOOYqP-XTq@xvCXg#a$4gW z=62rcg2fXx@VWWyk;{W$Cw$m`Falc1(%{c7}`W1HC!kn5bA2t)Ch1Qqw~f zi5T2C!BK@Ro~$;hHKtii(IM8Lp8-FN1N6`rS)X*LVl`?NS)`?5psqVjx+}dtW77Gd ze#_6A259&B>6XQDY>k$I*(EEUsj-F&>@SVgLue+oxc3JDfc#n|3gt047uy(C!=Y{a z8{anD^)3Q=GS&)muv~5Ke{}6D=l41wPd$CB&G6Uw51mHppd0_O%0>HczOT02-SXP^ zjKd#r-n`f_dMnr;b!CQefOCP@?SS3u&ig5OV9jpfO*t!uy<6}8ep(Cp`tbeB{P25_ zls(rb(-%r>+R+SE$Sz>7SiH^D=~27B_pN(>IaS_%qy0MNQ(zD9D{RVw`(kVM2qP{9 z)~$N7a*dyGD=n@(x%oiBz6*Qq?LNQDJ-TJf)8#JemUEoXLurr;kPCV@p5@tpS=aRk z$ngnsv(9^i1+8K1F*d2&_{+{hhpaV^VlyZ?lh1YEnSTpxOPD(eN9&(;IqLo2dPeY6 zu-(R+>&pGfYws;Lb-uaejCqY|zL~DYZ}SD?NS*u2Xp9^5F3_je2n_Zc!4|Mp39<2D z{3aky=EpFkT1*#CTpYI>A0HbY?Hbk`%cDGs+EWr_~0OlL@n^ zdtl$y0RKq|(6>_xO@$p`S$)gxnopXv8Qwe8Kk3VRg10cEdc5_61z32cItq>9THDov z)EOElL9dvn=zlo$YnU|6v(|guVq7`OkwW}xO ziBE>Rz5TR>kHI(Po3hf$j^D#?lXFti$=P%5}PZD_-vX)Z6M$1$ioRi`GM(XBub7oftY;*9FBoC=7hJ5%XDzGhc=S2cC`O0(mIa zJmCoS6>k2uwQp0nZ}8G57Ajgg1dvDI+Rt7bjO?6jU)K-#5g-K`EbBa1tlJseV1pnM zz};}pP0_lC>h^z}dXf5Mf5x5k2N^n<`!hl_K4;IqjQZg;)j>GyvL_@de&#@2l4jhW zh=;+SeV?olI+j~x7=RFgDM9hqGYe%i{r=a}}^)hP61h?we=Eu=#yQd;^0&m(f zF`FmwHD*N@vceu+NM~lQ%T~)zdGn<1;lgL#p`cqye@?jXE%$mssvXy@ZTJ{m#cp<< z8kP>IP1rL6ePb^i&pP99P#lVOiBg3?mPzDr4~mQ7DN}#_Lu7zcXLnES=|?qpR$YEA z$ba$_ULMgID^lp_IIUhvv$4=x4fkfD#{YJRei)SyGn`V8mv*FfmF(#)fn}J_ujKjX zth==_ee1?uCCRf#Q<8(jDxCMCR`tDlpOaR8_17i08z<6xU*4`J47e}0spi`|c@J(Z z*mNt z9bU0h&DX#b+y_Q3{8W2?;8|F9#iP;thWFQ{?|eT0_2)=6^4b!wfVA*c5hd%6x^`-} z!%El+z``$^PvQK_Yl@IJ@sZ>sls$oF70Q|-d zX_3?k=nGruO;F_lux8$W@L=*wqHfgO0Hljv@=F(d*R))_GAtf~_RZ(>voN3@RBE>?=(X1+X8I9*69jx9C( z`sxq7|M*-jzfz6|=POQ3=aQLFdKmo{!<5NnTx1q=k@8T|8MAN8x2$`<*?E2Js#i;H z>x59E)CvsGJ9@5tuxV%bLRj*~juq8L|K{gZY5myudhO-uTj4q9%QPDwj)b$oeTR5X z%fN6<|0ShgJ~EloU{!Yd zjh5GEl0>P*UfqIbPH z{?8lap7@&hC^9q{WH()-$7g`fce|!cskq&3D&r?&n=H zY$GinSz6kkTUM|p)#s*XsAIi87wF&Tpt5rw+>6KvF1i!H9poquDaVtVz@2uF&NKV4 zz(=w7k138B?q2lNRDCe_;8*nP(@zQ>xj)Lzj(MeBD;u?v9UvR4fV|Ntuifw-_$r*FZw%9zJ{XOc^g z_8nQZS7-fEO9;eu(6oB)P3+UECzFrA=FYvA{>q=*PmXfh<$cs^l?8~Wb%&I>=k0vl zn;-q=U?sU4`LpO(MBS^plscz6&-%=!qkY*7CPCHy_6B;ae)8vp@rXaZ*-i~c;c90H z?}6RD3s{+70=sO2)l#c8mO3Cjp9jr`Ai@}%9(mb;bFV!x^tq_};WD2Y%^#*1Or8 z(}r_9=^5M(ps&hNy-NOCH_gwvnfc!dAJ|)Tpe(5(dWDavVKJ|);p5Zg*C7{NE_mI` zDMHjd?YuB@dNj5#x#jOq+ZxNdlO2MotBW}Z2+(s<`dqjAw@)uKpCmu#6kICh)h``O zlU&r;?zBA6bjSS!?13A}n-d-5;&$dlpNXPHEZbDP>W49imkeI6q7_=^CO*IS(Wm+K zga&UJs0##mM_G~NBRG#Ua6i;WurBKb19Js^@9K5RU7vn=FmP@1a`E-C^fx6%jfu2_ zScz%Ya;x?0BVOh{kO64Gh1qnsf zDE~0p<>K$_v}JDV!3_q>Y_)cB$69W^4a+*2x<6GTeOZ3_Yw{<}`p%w-ws+TV_s!p&xS0DxCKW^338`Io67QF#X*`0aLDLW9=U3qS!2Vhk_>6|$ z4xRVuw~oT@nqkz*UOmcS-@bHf^g&a>=?cl;XH*{~+K93IT1Z>``{VUzm!DBRTAet( ziy74yH649+$HZovm6f^&`9m#+C2g6T9)@REzcH@;+k>8q0eW#AHcz=!V^Q<33Lk!( zZr<7V`SJ_*Cw|%I3bw!Q`1rKC_D^2VHQ?v>YwvSG_%YVI`CW3!)dq1@TIT5ND0cp) z+(o0sF(il^!#yx`jCwFT#@;sr$R&U1Xtn>Vx?J89+B5SHzv}J`iMSD1OYME8CKiW` z|I={9d=&&tH*`<^;n&iC==^54caFIMec^%oLqvf*!Y0m*sU4uJufw4KY@aZKFsLWA z`ePSk^UicqJ5Xgkv7C@gBCF15eAM#OI;?S0&0CdEp;G)QRmzj3rKHWuQuPxCiI%VI z4%(+#5%uqqe4z*Uz6(lI>I0jbo4%O6q-Rp^Tcv*}-14omHxS&YL(ky5^SKD(g)~7=3R;j+uu|r^BR^k+YL?sf)M!ij~!? zkFVY6rMxu5qFiNKgc`N_b?)_wj0-n-mk(Wg_t3CNz1phpFSsubVNrxu5Vu$@T*kX8 z0DdQ_%y6~C%(BqsB`%vSVcLnrIRtQHL0$C{P~-RyFY1Jk)~GN`ck=ew;nC(aV5X!2 zaK(^Vkt**s{nb=-KdmL9E~mDpp5K-?vU?$N5h_mtpMM~KFS7vtL@#U&iimSpdZE&( z{!Ht(7FLT4NI8Zv3Bje zUJSpwJ^u5KcQYT()|~Aen6;IBB)r#+U2?(Qa&1x|Z~cl5vFlO-w0-MV|5{$;EVYU- ztkFzV9a4F!tWAW#NK9cjrE2ln$w%V*wV8KbKq>?4ojTSIxQ=2bc1@m}yg6Yq9yj)Q z{Q5L^{vP)qAH*xbPrQc^spe^%=J>*^W?gcKVOU={Cekg&bkE;?k;#Ylt=Lty#b%Ar z{+s?Bi037+)h27ZGaFCWCVor)g8c;hh$-!R(_D-%9(r~1?a2?B9}_E6KZSiEefRrS z-0a`uGq#!b1K7_2yg(gk@XDxGFIsgR=qtS38Tm}L+Vb&=jv!)4T^{PO{b6lCr4STU?5-ZL-{ zHJC(kQJlfuP2WddJ6BG1qI+>NfZs>Na7j7pGVNI1&)Ny<*5q=`OW+fpWqU8Iq{S@= zINCB9W{0Yu-fv?wvwOg9o-xZY`(gUj=#oyg$`BZ*mH4=@1w4NvijTif@*-a&dZKe> z@ob%`pFX@G+OhzS*d-1Tb{eO>uGBj4O6Md(DJWm3Y_jOxw-xB{j8xP&5F+2 z;J?NF<jJmWbk;h`G2dmGQ>4{Co!j_h zGBoc1i^2(Ctdt)^e^qAceO{8dOw;?6zn=FIHx(lj^ty$N&av<8x7yD;a%c0bUcRa{ zZuv7kFQX}MG>9;xnzk8;G`f|%kbROP+%5EZ+8X*+-fy`UE`buLc}~Msc@*_ROl5RV ztB#kA_sphq#ELbQl0hcKZ%uyTB)@_nYE4jW_-iISa)LY%ov6`lPDr?$yZg_qh%b4FJG6Bq10qC>DJ!&8IgDqb<1iKw_ zH(YAzoN4QBvEJ~#W*|8QeL?Y7d&)3On<)n z^RR1roXSuY$>d|861zxo91;RQgAT(VAvzKb@Oc<_bPL)Gmr6WHUQ|Y_tW&Nd8z~2? z9H9hI8UX_E2O<%4lk4H`ML~PwR6=* zh*L0IzHHi}x3JN@mQ-ExWBkuh&+1t((Fa_F)=Cqi#dXsx-DS!G#8$B@cNgmtdn?~r zd>eF2_zl|x-;20~rV*(cBc@pAY0ol$m!LJib{=mn6IIth(iwloOnbH4zqfccn>KH2 zp|*B)w)RDg%uT9Nzta_LDBvs9kfkVI1OAs6a=a8L!U{}5pAt3tE9%;r)X5VQw8_VF zL##aM8GM!Qc6;a={^k`iJ$sGf{bO>2OFayYEYZrW`y<=ChT2#31%n_&2&oITPQ0NBHH1p$( z=jh!5?f&`!;;7wp#6mSUTkHsMc+&_X+(#}K*f5-?bB4S7KK5=L=pG%I`AApe?iFB! z+9Ha?PQgXONOoG|#t*D*?L8gnw!baYjoh^ElJ8*5VPVMzBVC_v#VQi~P#EBhRkd~R8$K~4>t<0NU_MA6vyaXF8s9zqci?2dUjJaf+0gMZ z>}(G6uxJn5hk&OjYnW>8RbQ%tBkV(nBqz9?^SBxL#EQwz8Bh8({s}n~Wvo;|snpn~ zt*^_}{iz$Nb4k-u^@S;m2vV_8beRg1K4=@%J2Gb+{_Q@en_0O1Lu0Dj(v&SAu}`?-FCAW^>T z8Jv$xP}!!bsgtgiM?vA@Iol(73%Pp7%YumabS6)%Ok5xI!_2F#S`9RMkw8x)yIDzZG!DWw_=rWF&GkZ6J&$f zhx=*%$W-#k!-3=dR)g-Nf2Y{A53J36U!j9&V9}q~#aKI6H{Lu{HE?|>ZsO^Dqd)`= z#wRMjrfg90Q{rIW!qXH!vT5lG`2=Ji;soYB;T5@uavAir5-VRM0G1xPXz?EJ6z}3< zx%4Y631dcNl0#HyH9B>&49tz~jZ5{nY7`PW6$zZF>3xH>oiVL<{wB3Edh^G#=%>Yn zs5G*!##1d7&2K8!IE=!EQ#OU}t7x(K?N&GOd(Gdbp0-i!+&tq0PgUHi@I);oYO7jn zPv{#No0)Dfn>3p>GcsFm5^8uzS4*>ie_rL48+f&rvGb z*2+spEhdc!+UJy`uvM^V`FH7*Y!`G3>J!*rTNoGITZ0%=p<%BDUK4TepX{b zeWxmdtb#L!L!_tpmMs5;k5tTD@6715(R9z$y6KNIB&y*;2GgATMqn>)ljT6qBek*f z_A#YWsX&UM0n$%hj*=)40vWMCESvQ%^ z=yBB|h_?}Pi8@c2E}khH*BM^cAKSCM`$W&ffy?8UX>L5a)CfL^oJO=j3dNteq4a#v z*J*MVLuc{_py8z5y3OXlY$9yGSehATsQKW{;V~6EPPUM#5TFU{7!)6=7Gw{kV(XdU?6Dl!f5F1G*bp4GJjx(HezYA zi7TQ1@v^C#Ci+L8PXy3D@VCR?D!UsrSRZgsc6VMD^G=2)Nz-eHN;lHIm0%P_M z29l-950gDZpHNw^+hoGGSY!Fx^o^dY>K*J7#ZkcxmH{Jz*~KjoAApr(r<8t?-zxQD zc0jZjS2D|J`7{Xgo#3kigP9=iBYy_9Z6u5}{I2|(*l{tRC*VYJW;uzx6hXbP2h?_@ z%BLU#_*?WfJm?ET7L%%pt8mdM1?&yvF5ogTgTzB!;3-IFOg-*0;XYv_E(a-OR6Re(&r)b=_L}k9Z*?ZMmuq|^ufqJNSSQ5sR&b;E zwjy1Cb$jb4mMV(n zmxJyVuJoFPlXO?+Pxd5ly|7Sn3EXrAka|VAbYk%rTVuX?s&kY&6gOl&@^;*DMo4?e zWO26%?PS$3DE1QRs@hWRdEK>o%XKs~e3ZR$&tO=I6OYY+F7(ZxXI$bci?RcG$w%+&PD86MS+)h*0`RpGy=w5eNazScOazE|y% zsxu{pT#au+9FVH>cF=E7O{k;uzgVdP4e4D-I(!N85b_q{CTyR=K_cTDvn*&Ir>>3p zj2s?WKi)KRi!r`Pg%;s0Ro`e=>#s9>Vz5lFO$)B>uhOGbj0f5tc^OurxGqbOzLD;e zpNHN6_>FocPvF76*EZ5ys((|jQ(H^@C+R+BAJkIv4}Cbxdc;g&#M3<%Jm=}O>UmWL znR9ayDgTD}g)<<*l;tEn0u>n|KPuSFd`F#`UYyLGJUwMKvvW>B8>AbunY@djFG>+o zk2t65tP3+vHk&cqVq&ImsS!*%jShh5h_(6AYy{JZL1BJns|#8sp|By$K_wNHIhAEd}&o+Lvi z18h=j6&vJ^vZvA*nNV>7k%+rbKB$(Vp|0LTF2ukUUqG!nnWjVaod3zJ;fF{+!2&SX z35Fy(Ntr|^sN<{vs=6N_vJ9cW04CcI@(1V|BSkJ?4c#g|Ax;LInaczri$}$`6q=}2 z#7WAy=5ifx?JsH_#HDb$z>yX@a<6Noh1Mu;nrSoc|1tTPxnH7=>QGvz_Fn6fZmfQ* z!Mx!`BW&*`3%IWuH;oF--7CR~bDp#x%ukE95Op*jUygr`MWL#o8)eNx9o`+4!@*L_LrA z3GqfYEu;v(@G5vog8SlY&$y8QmaOE3+*#wv3eYv``?PT0jbf=nEy{L}Wz^X@5 z{7L(97m*a`R=^SKwV24OVDD#6(*x-H=$&*lqnG}KZpJvsvfz74>k-Dp)0Fj~mx3h; ziAodeGN|Jrz5T7iW@hWMe$%-E@dM&zy@wV70L-#ZSB*RhKP=kEPMXi2Svr9n**PHT zJ=<&7KRu*9VK-OESj8WeB*GqJn8ZHi0ZIdY(2eT$TM*Th!*&gsNebXyx^!2x2SV1iy&qDxFr{qMKwkXS?6|k8`0d!e|fa zpk(D-THl7hYK;$@{nZc#+B&-`V#CwzaR%|w;1bMR#XjSGbfr@S49*T?iLOFz19oB@4h3AdS&%H)dsH~#fr_zahfcn3x0WTP z3-eWSjBYXJ-1Dxzt21rr5=~dK1AkmI$?&YPnnAY46tNs0B97%u%O0vT z)!EU}(W%s<+y7-qJ{CMDD<3 zeKV-ZH3C=~HM9lbmY7$q)=e>uvS!-Y*c`V)S@f8vTiM!CoWh)jZJdn0k#*$83#!BZ z9s8RVO}XvAhpk!jsBrxh$HL{Gmi<|xt)?Q0nk023G)RB_{MG#H!cW5=E2|5uxBNsk zUg$NUn(K1Zp%Qg#tMhB`4j|@-x0?30tCV zb;hJpr;M^l@WGTIp~y&72(CamQTM8qi(A1etmhKf5TkSypUP?*tsejMq^iH}VV7`f zk>>#)Rw8L&bv9}1sGTLa!mNcan2U3VCt)Lu-h}qoO|cCN4cA+oy5S>gGZ70)>^=N? z@n7g)ES)S{P!gKUeZ4 z@{|IA4W7_huUo6dQnRKQPy}kLw0ra{4NUdd>vrob*I#0?$!64-a-}Kd$JEzfpQ#^fOUK?hmAQPA zH#d%+B>9kwZ0{V)xl6jZk2K`M+(>-8;cUb*IZqvsQ_G%+~1zk8IkrdE*x3R(XWYPST#8dt9T{HkP?fY4(e#!}}X3 zRWlz4DsNVoG!X|47OqJ#Sc=jF=DE;$w6DhW*{WM04E(>ZYuBUie|g+mSXcJ#*ZJNl zYO9Ed8dSK6I+?b>eI~Po8$+!HrzVq{_k7&&boZmLcfZ}XzO(%< zlS5QI+}ayd1HQ+!$I2x^i}F{bxDp*1a0# zH|mq_nc;TLHo|_${l=(-Jj^k?VGEz6^m{*u_P27wXpl_hJMvihBv4c3JvR~(=iNf5% zSggv0vDlwxcYz+D^U}MRY9)@68(}s6BB?=h$T-mAjv&ok%rk)8a^F1!PH9II-V@&OgwwSE3COh}LKXrRy)vMAdkPU8a zAXQ^)el%G11kMx+J&8i&csI8-La)E}UgS-4$u+C;|J+KrMEIY?9k`Hs_4fn%%MZUE zjaG1s#T^2Wyq;U2xd|^2NoERnwvX~U*A4GCdTzE4U9wes3nR#Bt@6s%D;K>5o7P5a z?sblf49i%9w~QpZ3-W=un|3y|zw1=*{o9|@&*^&67Qx`*z1i zv%{d92|-C~u^vn8@P-ZK7N{wYH=8kJ*bb|^^NIPU{@L-|f}H7mMA6!})bbafhpVcq z{J#)B4}5m~UR$rz`D^?uYrXs%_8H}Yo|jeTQmp^ounl{jC5sN&BnCvyu21pUW_FAe zCH9^h9W3ir8QwFm0$HMwY9n7!>?`xfc@Hm*Gi72$)Q;cDFGKFnr3~JT%l=y?ZP`Cn z&0Q(+mJ&r5x!)H;r|~1PeRq3q50Ix{@&=LXRS)WF=m|9>_1uI|JHyNmryP!Atf%C^FEk7Bz{@OVw@}cGJ<0(d$ zAQyIDbh{RH|7fo9n~utp)dsc1nv^d|WgTzcyv%voT=MY~=1=bM2c8EZ-dO4y;Ty80 zJu-b)a4dJHV`No`V*QE0ilE9(^$~Wl5&M|QUIzw}V)py(GftrI3J*QJYFJ+^8t6Ju zK9*g7>n(T}VcN1sZJE}2DlZ{r zi{HOC?&|IxF`C11{h>E0BbGg@LO0jNkP`&^GWMNKOi7H|fA+wS!-mIX$0)~;hZ+0! z?KIj{?BQv^fcDSG+Q;fzYlZb8trLAYIt2m_-4K|PFgWs(_Z6BcnYGwH4)5U#GEmpNZ zDnzf|<^`lLxuovf_f;HR6PXFa!mrpWWjo_I8U|Lg;Y?83JzS~l{I>+`&; zc19jjK6)^Txoar=N$Akl`R%NoyZ2z?9rxPA?c9BKm)A;+5%8sJ{~5 zDWHvewlsdufBE6@y?YyOUc44_O>#Z{7X6O;y@vZM9Il0mDj7C(YbHNa5nV`+KP$g?ki1NzE3B9YVXPY;-gdN6#uaF7xH)`~Uycv952+)-1XD`Q5jq&jIgtJbRv%kye!UBQrAZOp)Zh+1G}KyzcE| z4`$>uCnh9=<-I%m$0qJ^yD@tVid}B_d4}!XJ8)G0fAZRO7_;Nv>Jrl$+@{3`3;)fV z@G6nX`ncuQTR-jFaa6kBBGld?458VN{%bo}3FtVC5^D^tRcIPG)TYjtPzbO?MW~_!q(Dv@wXS}B||@YJAThvN@`V$M|Dc$>mc=D(Sz%~*{LP~m&-~vRMg13Z{A~hdJ7xXa)iFzt*zyg}la*y*w6nvI!L+IN#VjR= zk=SC$;5wE?wQQihN=aATO1aT->uaj#!>^AHXLM!aAH%b|vTbt`^9_r_KX85=>v}Tl zE!vJ*tCnaKWmmnTEXXdLx2tvUj(EYIw|j;XdJo<>_3zzlPwbDS2DB{^D1BZuqTZSM zNSzeuV-M?Y(IA9GO)aNMPnn#UJ+Me*J_nitZ+V&vAEMjO| zZs=UdY^e1%*w(C#j%zdkzN}jsHLKAFYdiF}w7sp@X7u>Xv4u+JYlhO?v%$Q^f=cL% zwoJzTFZW|Io)%ntmtAwN%aCTKV2~bZ?ocUt+R7kRRKj;`8%Ns`x1vE zU)3nk{h*B{`HIcPsC7%;uPo@vT>40ymYMnJsb|I6?soYHn>Sm~8+3)&o-7(?A}d$QzQ;Ysa1KE8drLS}q@L*cpmk8fVS zS5m;OiXCc@-_yJ6bk}{YZMsS!L#=Up2`{Jk$@{{qAKrgmUlaK)vNEG=qGIr;@j#;h zWXqgP0+xqw*p(e;97o=5v{M${8nt3OYV#7W4W`WsrQU}3p=qVpe%`rO7}Pky3((uT zg1fGFUHJ;S!L($t$D*=6KjBg8!<89FvQ?g(%h-@QeCBjFhd z(Fb73lku`$zERJ&yKQfY^o%(d=fA)G$oA9T=PsOjxW71bznd!M3Olpc^-piTPfJP9 z_aWHe!M3N>7fMv~4rSz}Q?vHvh;pt!K9rVucjlJ(PJWta?$pcBvK!ydH0(KAdJ%Sbd+e|zaZ0Db{Vn@Rb^7tF^gN?y+4+^3yws%I@&_h`_iIj!y;7Xk z-eZ4c)u^9&5Zv#UN3l7SbY66Gp={xg_ypyqgImziDBt*t2}ff+x3_H&u1Z|OQiaPy zX!nNa`?rj~org)=h+Kn1PKiDp+dl89NJ@&o6SCSl4!d+j>9aUD>OS=DoqONY{4)!( zV+u0fFurf^)#CJFOSPu;o3suP4diLeJCj%YGg_U0l~gj`e0*8)ZsRZMlt1BslYX%7 z&aZJ%yZm;PY>$c7*i)2vb#G$$U#|)yHne3Xrqi-P`15AnlV(~!j`0V*#H4LmpZ9go zB&$V~(wNV;j_3KAq3P;bPESH}qI0g~Z7*u8AheXsSS!w|B)(#MCj^2M+}*W6p+XCFccqlN+qdp6)Lm#PrL?6j(gMZZ9fBtiCGI58`Q6Wd@gj=_ zIcLt9J$q*M?0tP$88xxxFB*Og%~SXz_Tmqt^}41>&yJ)2zI@OAXn33ae(4wVpWn?- z#+ecv|2@!GB6ZzshQU4M9jki^M*AfekYr*cz0Kr5 z;swMd177<=Gp2iCfPmf^9~<%vi@+-oY^;*-8J7XQAWs{;*6LI>zuc*=y7$eLH|mwm ze!r>6!!b3XF!#swPU8yR@!lWJF-^vf1EX>o3^7P!Se>$bONj#a^U>Yz)k{lWl)~%G zdwX~n^v<{!X3Om?om5UM9Aa&J7!A}Ul9Av|d_)W+5(!uELSm9>yn|QZjD$xEJD1#? zM+&1@XHH87+)H+Tkl!o2+EygL55)lwViHHr`5VXg4zvoGh z=ib3VHc@HuR}*aFBEvwQ9hNPa$Ev(BM$6^m=x^Rx{7>+&?!Ur{8Vd`5{LUPCGV?C! zuKBaQUqv++#R5F5kD%PM`ELI#4pMkZ zbd1+I;?wn^c31J+U$DH!-GNe01;kJ0Eg9d~Z4l?I}h&yZ7*3`7kY=P;r^<4o~3%>@@mCL+1J|KryuJd8~$&-YI$Mp zou+5|-z<4G>yi1bJ68j4mA@GI&FXSdRnfvd>!Qu(JzUzm#%42Q$D-X%`{@UY_pI5z zbyeBi%MqTwTRmL7nIXzJ_xXnw^5^}EwszIwnna_mh52po@-qBy{CUu1jzIuWLrHtoLBAX>U?r%-|iit9a?O4%=^D?eZ^x6161$YNGk9-h{4r za-3ys$Bg(HLs6oTAg_bg?=b`Nh9PRxqkoP?TMK6wpQ@BJ$$L#X-=!xE%aLC(-iS{1 zpRqM{)8BT!+VI5w4eOVz!%<~s_S3&6k+betmivtD-Sx zW@~NCY+Wof=!+?4RL1ZFEgh!_v*an=T}5o5KEwGf zA%C=Ik+I4COSEx*^n$io=R}VG{?MR{d;$LAj;Aa`6uP-LX=|AXqm@nq{-b z`I-AMFMD6LAK&+``(bN4>`M95K~|GP?ULqi!$fT%{jKlWgcl2KmrTrO#9s2Xx4lSu z3wt`9t6K`_ds1=P)R~L`3qP}$#8R+$>g9mGZcec{Kex!Ya&{Yk@V^N&p2dWIIIw@P z=SII^)KSP$?E?p*?-HX;(#;QB+uEjEA7qqLnt(H`e(Zg6AY+bglw+U^+wHpd$zZG4 z{-of=bC$r9kH$j$x@-n;XVj%5RW)I`AB)Fb;iFfp~F*D61v?JBfMNlPd2@;83kNyuegvf@i23P88rIDO{ z-Hx^Iif_hDuahC`^y5GngraWsb|XFSMCUjSCL%_Yd&wbslv% z>$u(x<=+{(dDfnkJByzzp0OZx?#j4*A%|SkOwQxiwS)~x8&<4sTy}O|L3E<`IV&q%mA*mTHu-t{)EIqyK6^a}Ik9Hq^@Mg}<7EEi z^yD#aDQ}0cQgT9dWjYP=8MYL<%5Yn`OZam9HS1n)bgw_FiESaf1*nKYCB-f* zo&PEJp*O?K$JjA(uOqnnYN=i6`ikIbTt93HwWK{wi*(!8fuQ& z19KA#ON&YKMdq6rrDl1I=T^3k7rbADEsiTlpvIjEnRJk< zC*d#o6p@3-R8Mezb`UF71(QF--|Z_b=K{)vj%oT!qpx*)orgNKtN*4oYN4Xv39GRdU( z(~Z>>)fu0QNw{4}>|E+~L5;_*JxE$2fQMnk3z5}3=-4B;iV0Mz8dq`C{ww~b7daP9;(Es+%JMx0kC8#M z0M(;u<4o{dXcn>rf6?TPmCpXKQ?0$u;xOqV^p0w^C~GpFvw+h(VZ*BxluZ$3&y_yf zZPWcmU$8s)gYk*pUVT{_C6KW93_lx~)z4t1jym!GD$QZFWLN7Z=NivJ@3US{-0U1# z)`4b=$(u0cP%{HV>ksf@mTFdNqqKSIRAsGvtHMQ7ZOlPlC%8~%kRiAss6=Za+aj#u zrcW$q_l*jN=s@LlM&DF_{Lr`Y4T59JPDmzco%Mh#$aj@rfoFsR$ZR`y4=7xXlYJ5w zi0QI?)qcZcgqSdFf-o;LKVf#7wu^KDZ-Kjm{Q+=BH^7)ip@yIgmHWttq{^8EKj_N}Fy7fqJ1VaeMI- z7$+E9yHa#~>_UHUcYRMb3(Y18m9n+!R~oTug*;u{B+L~a5`R%7>t{n1=tR;x`Ua~6 zhxu-7&y$|BTz*)mQX1fXI&XQRgeEOf9M-*t$?Cf+3#T@M8(!Giz_%W3p$)LP^K zTn=a>5e%EOp{l2f<;qt8NwOQ3goz<+Cs~roWC5TiK_EAhSj13*4-QbsKusW>kR$N* zP+Ox~?J8M1xo_xa_tm!Lt-hVR2A2sa(}VaT3v-9dj(VG~v|sRa1%-3J_fG4Y=AL$k zp*XP+*35`@j|^HGN)60*_pqI1at_xCw*<*GFQsRB(6KSr>VZpxGsk{TDg^za1d)O7 z$?fDEV&mCM*;btNi38kd!SSgysgoQo$4b$F72M{Z5iXW4(kuZlM3<7jnclX3@A%te z(hm|W37!*p!F$x1XER}Tkh+f;gY!cR;ddanj46O9p@VKFpl-KGxkGVgO@V(O>Pb3;M_y!5O+}dR2X>ymJDmu^Oaj=pQKOa z&FUP(Gq?`Nq%Abhx4vTQY%|x~fwC3}(#4B5kM8KPX@xazYgpgh)wy{nns2S10~-Qz z*bvlo{7UE_rICAlXsRc?dr{x{ky5@$1&3oOT&s=FcAhHFFju-wDQOAlir8#CgyqQU zAF~qwFmi}UYp!#gN39#q!G*CCOV@`BlmmB~KUWg}%`6u+M6;?S^$2^jIOYudc#BoI zLQO8Gwew63tz=HY(67GVSN_;k&TgF=S~K;1`UKhy$S^o+xxx0mLz3%P&nllBUzCrN z$CT48J5wuvb2KA`am1W&5n|LxH_T@&D!j_d53w1*<4yK;Ua1d#8;oF{3y=kS5I7GJFxd~1}4ArSBUP5&r46s z>y>F5x<1ul4KPg8A!JweYMOU7TMiOh~Oo|)XlpM)G# zs`*=hEE@)QP}*v&!Pl54IWF^<=iTC2=lt0!o5Y5oRDY+sMIYq@#zy>Vi&n=I?i218 zoFi=zv>^mRvtAG}<~?l1UL|!$^jSXj?TOwNzbLB7>$cgjp?W;G;XwZTPv2ixyq=l0 zrtoKT4d;`NiVdgQ(c+1nhzWh0)Q=lEoYWK07S&wXvZI?awp)4?x|}x0{-yVV(CV0N z2}|efN;;l&V9w`+zcW@wsDhsRa@`Xhqpg~#XV80%YULWqv8nx%kIKqvBWxFD8?G09 z9MY$BorLt5)(8tKa~@~9z1L^Pe$_rxYU-Xp7A&8C$87sMGr6VhQKb$b*s8 zi7M$y$Zgsor&oc5n5KBw__XLXAz41FT??59EvfW4A`R67YSehiI06CZzmbKkUHvTs zqa#kdaQPg=8^mm!HSs>tfl!4RfZf$)h$;t{HSH`#=0kt)_?uCy=yBurt2>bg>E9i; z`OXb1k8lnias6dVg^tt zzadRk@a2`#7)hcuN2!?pjPN9`p@S^%+crBGTnaoCUgx}Cxoxy>U|b+BLsS{7^d-|- z5PxhQ-N8ZS>lt+~A$GRa%%HF#*G=?;kQ?GPqYrw-ZKqopEm?PcIYL(r-CN+9Ph zgzy(YcW%a{;SW;!O!r&tx1gF%5FW!8X{RKqfA(AGD|PYZnPQ93Azfg5jTfw%2;W6%Cem?-^8A@7W)*b zhs}XHLBheaK{m$Y)6?2ORjnkO7d<+=x3uMQt#d_wsiyc&@xqeT|4b^yb$;#nEFqV# z{0Oxto-mVHOYA6ii!Bpqc9_q`N##RHsd$$>UuOm%Cww!#ZY^fMbU0;y-^QDPAumTg z26^cw)PFQB`cBY8*dF8&bRza3o=VzE!BF>+ZlV_%2B!RmOIm+cWtDCzl~fkAyd5G- zIFM}0lvNdTv+YjPLG)d1x*%gj-sjOn?n&q!?)x-=8G16BHK7&O$Zd5iK@9GHpr>uoIQ87OPb1K#H$+rC|W{X^rERWglXJ$e=;gwbeKYJ;-DS$;OfQvHeV zaQ4_$a?+TOk?tb=PApfr@iZY@0H;;-glJ*4x(Auo^@$h&WT;bjik^<}+XHIPR$Q#MZ@W8cp){g{Ei2tmgq)1co_%y4c;1ynO@x-)PeOR1&^l_X3mIZcz+YzZ&GPk+_n~!rq$QS2%pgaQJkTWlc3yBdu1Zr-o%iS0aPiannf+%4A%-=? zzt+QUv3{TYZ+fk=KSnhH{}Qziu5Z8Gc(t*zP0+t`;(!cdT!BuboHvIuQLeGx8vg}B zp8~nQKinf6z*cijTc~F#Un%pbJE@l`S)^z}4fa2@A94k34`_p~Q{gHhO>uzO=ot_g zdz{Lr+fkE|p7Pp$hkrGhJMP`Nt(n?(CL=CzY2ymByMZFPxuQZPZ*cTDq1P;3?QIPwj~1Wj{waa`gG52@K{xyn-Wy%r9q4v*?D8DOT~~T7@_z5O z)<%MttL_Zps?X-~o`>F9b6tG<&dd1U#LigxQeui7#u;OKjgY5Y#j2{R{(0>4-Au{H z;2f`C{E~{gm@fP%RAi-HiFii2WPQ@L%?}-hi}sE+#&P0@<5S~GqgRJ6_Q`N8GQUZF zfGt2L;S#Cmt;;-D#Qa`t+~$6$;aJ2z`O1|+O{lV-gfHj+FXLm+#pGAuGAm9UBHj9d z3)((|5ImJsum2QvC-!{v*@-K2A1rz8pB?y1TWZ@x9K1H=2YQLWN?&2oW@T%eXaB(E zgy%b-QJ?i*FWr7PyEzG|5zbsw{W^_H+`r_jk^1rc8cP?Lb*0|B|UH)<$*>>(9 z;`f(4Tw!X(XMplT??ZPbHloXIid6ZN#lwwD*!!W29a9qbZ6Tic|IeH)cLgsijk@Bz zlxl!#pfcJ#8?sB>fHJ|1tyLoU43T@h!dn z?_keF9LG42)cUEWw92pHb=TC070)Qp@ZNBCjG@LCaw*aZofLWit;baZ6--F`t^U;s z=#|`5(*vx-utPz6Jh$7dd%C21u3fBU=q=wK_l3?5%p3+#eW@B{iOUOfqThPIE6?q& z2=2?~8G&(FJ0@+vTb}VV=yTY+wwK$Uqn-!7-1P2X_LpCVe=i%;dq>#EWj2s^B&^jU z$0IJj&b{{c?et8L>s3GNNQ(qe5;d8!z+~~_73x30 zJocg@J`4V2`J>ezw51s%fKD;(kHsJr;INLT)D-1pKQ+7a;g&L^;=N_zt=5MC&&!1h zXUg5L`@Hmi=}NY3GI>vkC;X;+IsXign?JaGdYN@nW#~6&D@!Ny=e9*|=lyQ@PkZFs zeWOQ`%8C7yO*B4Xo$&#ORO|8O%p>lN{_A`1E_nLid-HF(CAZrwfa_0^H!=c%`=1=Q ztEv3%opm9XQn7v*;6Ydihjb+^U%h;r_pXvXe|A}Kzq)31%I4Uuf#2NvnQc}k7I1T_ zd7XK#g}r5*1=%bM$nWykZr~O6YHIKNN%$Y$b;jmpjgOQ~PHQO^>$-x9eUJYMoZk*t+t3y8YqX2M?q^Ut$^M;~Yi{ zLS_NH03z%XmTB_Zroff#w>`KcL?0O7z1;B_>mgoA;S?vgMig0o9*zObL5Ar8@w>xKB z&ozhuxZ6Jy5f*-7ty7tijTxsr5=dTq{q)$76U1(ln7|0m=MqZBc7d}D$0 z@7eRY3)qXAm(W&3u0652Ak{zZ$kz2MFC{wrEHnjpD}r^br$9aJ;?~fhmX3UQTIX>m?4rwYL}xbWECBWwW_(^ToA~ z7D8fMJi~$O-R5a}_qN_{KQKbl>KLQpw^wXV|FZu{TFNs2@N1TU-q&PaS61!K68^6P zUxTuoKh4g`E7Ud`*lUcA^fISHFF#LHdltZHiWN-^6Z_`$y&7imLIGZiA7T`_0^qdR zYL6<*0q#a8QR9e>mMj_A{Cn%u<>O)7$gQJZzkMy^4W zU2cHxO8Z3i-ut@i_aFTJ1TK&1w-Zfhd;reM55wH)WPk&70Z~S7u$vPYGCOre*@n_} z9~PCzob$-Dd}8v%Om2VI|6r_B((`$zX7>1ZG9GF+c2(vjJ;}Jz_7z>l&vWyVJtgolDR=06HoI^(E<9jn5{r2bufMX-T3F$`5 zFdJ8!G@EBk6E|95L(GoZ&*n;J9uJ-68o)@!ZGqJR<31eFm$v~vw}4#0djRfIv+lh7 zInQHgOM6t!s^XcyzUO|(nfYx|K}prRo&bOc2c~SXo8g5H?V2%=xPEp@d`NH<^Cj{k z-@big`J0l~8h_S+`V0N8@A;W=Df<_M%()VA(ybfdPOafpbrn?8OJ|l@H2e}QwDSrK6GnYx`o6_u@YnjP19x;*G-K+c}rydsAtM>fOogI?zWjw(;gN?aMS z%;^;B>qK4ctUP{}Jcm@$)%{w2f+TW!7oZNF1&}b&WLMB(`CRcM8AgvpSQ7jwzLeL* zFMuXUH)bDp6ZSj$7@`T_(|t6+3`~8Ca#Ap7NZS%xHC1L-E9njtECj=-FRgFeOPKeq z!DbrbCB!4$*{S(sXL|0p9BVkxkkInHd-dpbF&i9V66A6@xFxzMO6Z?tcMy{)J3egJ z?$_W|AKskX?J$m#nMH##u$oiq#v_Mwm)Y_ zv*@Fg;SS;&r~`Ije^6}2Y)oQI=tkxg{L{p*x})FVFS72!@6LTe%qO$h$XcJr$xdsL zD_v)f0QONZ;9KJMDffy0y;XN3AC0~H@Vl<5Vgk^oFsX3K4ZSk^#PT^?cBM0Ro!tUm zygtIy{uq9%@qfH?E1bxp$Gs-xVT@1g-bjc)$w5y>8cez6?RSe0WSKtZUcY`>d}HPF zuDtCnNWf3e#RU-qFaS4AmdEMsTi9~4n*7hHxTGYaO5U=0uuM>@1))$TcbVUPqN93d zy`6V#4k+5uv&pO+u|Nj_IAczPT$@B6V#K)^am0B)wS5om0qO)`Z^s~C;H|*;UM0Id za{P9pvYc!h`7j{tfp@2LJNMNLEuCzY zp&>UZHyjFs!V>>2>RGXK+3~q$K{&wQ%$|(zyWTs=Q5y4XnxZ{c#_i}xAKGlTz$MU< zW+mCxoccZT`Nq5ayU-V-x!bFqSa(Dn8XDji6b2Na9}n>6ASOcjd!lA0W{%q&IQ7@qVxA7i~T)-bdykGh> zdp0>Xo9#dyRITO)j&uyiPj)I^Am*ED9I`y``@p>L&R$k>axNlL&z7e1;zm1qCtG;c zl9JnnJAe$){OX>r{oJJKapI))d6&2DZZ2pWf65BjZZ#M9@ zw)*vgLkx5qhZ#kG2unUn6+djH3Z^@Q9 zNl_NQvzXarK<8aV8-{e|H)3joD#fKye{SY;f4?eXmKW6@YX!9~tgZO7HPCD!_s*&H-(UTW@Et}8Pp@Dk)(}Q-xPt%VGX4W9L`93w?*ImLaFX7i12V}MUOPuo2 zSwnyN3ww5UxAZu(I>s9KGvthE9wLpBX|u=kcqn7W(%8-jh~Hj^Li#RDo-s%9fS*4y z*(>Qv>93hMuU$e^+E%-*b~CqaBJ9>yaQ1aI)J0Sbl(knmw}cNaoH_?uOeipMF|h~i z6eC$MR?<`2`n=Jj{%-B{nw2$)wTtR)n`XC8bVz!cqYI}_Kt{|z1}>eiUiWb8`Stwy z7Xwc*fOW0_Ga56*7;EBF)#*qTpBT9A?$Gh-_1NmOIxi?Ce*)9x+I#pd2r3LXLU=LVI7 z@cYQ=&Ze5zMYxq0#thmmQ0-fD^INdgb> zN4e8MhPQw}IA`$HX9D)H5!-8p3>;6g-tcVK(gQR0FHKM0oV)zP?CQX!mdg$N!B1r& zIse}EeAxJXTluBVd2FcUgE|Iyo)_Ri$ZNpQK@AQ14Z!bt(8Gv+;QFcZdE<#~m;Z_K zmgOAEG5dMvAFO@tB)~&7z3N^Waw!5E1aV#r>>Dmg_VM2H@~2{z`*iaFzhnVZPbNL-Qxs}rEsDF9;pSD;N%r}EAmt1xxh51-IOsM zjNQ;&@t5^A{6plcf@klals~q7+Vg79m(^v8L0}zj8xVGWc2iRJ%=X|V&LGnV$OtuS zBD+gl)$nKIpOhNra0p1v%!@s~N}M)yKzn%eA=U0noAk-g0`uvsRg{6$n%=+9i}saN zRDEc6?*)(52{IM`(+7S5x^8R&+U*Wqf%u<}d!y86V$e|(q!bQuJC;ZW;7HPANdvh(^zO*#cTgH(kT23Jf}M{2p>YO zBM;#p!#`@_Vq0!Az$XFh&!on)duH6qpzWX1f23Bg{xxfxj|Z^s?i1`D+}%NMs;%2t zXW8h|o-pueVjS3S1Y_e&2Ce)Yjyf%1W&-Q4^s!GBgsk$%)ek?uJN8#Iuwq)oAoz%* zx)XwDEsVPw;@~=9nh(q;??5cfCyQ5}RbfA(OhWW7O(ut+j};Gvc{~zdBserxB!#L( zz>i^vL){cuG&%e9Ie5T5K*7j-&iC~h=h>yW@gswd|t1lKu{=NEd zc}-CB_D;}%Df^mK2*X*#`E5_^S?0a*@YdEXy&G_=&ny|4e|1()?2^dYAxQsmkCl#l zEESY=%stpANGq%gZ9}9{wvk-X)A}-D2P?FhR{pr)wvSD-u^l=tSJL~V zrLgri>$*gPEH;mD)H`aduhULr&5;6RA2ytLn2?UN(r%c}-k@-AD`=-<`ZzkaZIFlx|r&~a#Z>wSq*1A~*)Pw|vN`jIDj{Cn3 zJQ4OJhLo^%w$mKmtoDTFcvM_hj7y|f@Tixyqn|}E6i%&?g-mmB zCv0(nYZ8Af-oGki)yJjVk|$<8ow+Ar&m4Mk?1BUHYiG_2x#+TlQG;6zMd}6eg97I0 z-!68&U-|LB8HIlf5(;Pi$t&%tIo?U*oK>Ge|1`-nZ#1P5+Kq)%cEdm0F4W8}yIC7d>qY4V9v^qe;NMI)5Gc#Zl1wJK2tPL;2>m8^~-*$JEr|0 zJ7IxP<#f0rlecU5YL8vV>h{`Ba6f(QqUil}BXN+q*S|K>KiVPqwsQz=E?6cxGO=ys z$Y2BO{ZKO7UA!N(lIG+xBX}h8zpy=C+pJKynFbr>VwqfaNYSdWmOl~u38WLR#@~&) zk1iiu%8BH&CG{F61dXX7X-zIMPFS!l^36r2nbbvuOw?ZJV&g--gYKIatBcWR8NNae zquvn{OmWuFnR@#&J9kSy)rdZ7T&g%MIKyrjP8s~bavAg=N*f*>fsW6ckntZ&7N{n4 z>kJzV>-6cGa%GIdLZMeIRZXg)07oSS)`r_=x`65Ee$gA@^~DKqQHB2l4${Oco+^6P zHq*tRJjfVi0{k6>2Ze(Efj$7bh%VZ{QcLc&!CM`d8rD=_t&9eaS{%B@#&Q&kFsU|Y zeAyA1QJI1E_RlanaZ3Na#;V$i=BKRL;#06-Qv05^5F7$q=J>BU;9Dp5#w_KXAE&(_ zT|s+*)+juA{2^iwszcr}tN$ivfg%OYrardLvwz6!w;+&XV0%;*X$?Jv7wRccS}C_Z-$1?rt57e8Nc*ye;-k zd}8$8fM%y|vq0Qe@HUN)tWPvU5Xw*IuM?aQE*AY16^h=8euy-pbyEdX;wh>)N&Hv5 zRx%|qmwuBB1G>*Q#SZ|zW`^Diat(>bB5>z16yz01{r|W)$_DvN#R7Gr!43HzWtL5l z$4D@B#$-aztTzcUF%yAwm*1w&2)ZJYL+D@JVbb=jy|g=mg&Dg&`9e5DdPBJy&>+k* z@TY%i!xf<-9w%owf8cL_59{sdVeS`6wRQtck1wYmx2mwcVe^q;N>anb+U4?-(kMle zt`Bk;^Mw4?EZJ7zlIL3<@+JIO$Rn>98)y7=ovDyA7Qvd=*WM%P&g{P3Gqb;GXa~EC zHzX2@LnRNylBqsGRk21a5&se&5m$;wBu8amwy{1ovp+}`+ZdMU^Tz7nm(1fv^aGmReFWaW98TwE%A&PB5+ zBYy{v4h|0Wjs%YRjt!46hr@>whReowad(Sp)sg;*!hHXk|kv~^)l_DNgdtW z>>Z=g+}~oOIoiyGRz%o~nglC!hgHssbcGL~qDF@NMchPxM~?vH!nc~s;!e(P*3M2s z%ljs`w${F!iDG3U{4uGEe#|tLdJIbQ{+7HX%Rw*5t0CM+Bfw(gBCg)rNf$# z9+{L`K6kKoANHiWFJcbU{NM!T4eq01abIQkkuLM@Rei*f!~8mR0CF2`ne~AEsDlM_ zujLh5F2=?9PMXC@WqI|U=|4ICRyq&*jGAn>(EXinO@Kw+N0VV+6hg+${gVJeV=?XdYTp1x4m~!fB(=z&NU%K9z zZWq=My&O&hIcvYl+r%5EGQ@bfRK3u!9{LQ~g5lx%@W=2kv4NTl}*v=!4& zz&7w~qygoLK85l{u7?{T)5gvE6Y5HNwKgowiMlW(}OCZJloheYy8#7xW%bKjd>%Ch|V4+*qNxDLW_f7s!N5=SlNPb!;Lh00H|ty3dBIVWg*WRx{39+R@+-21{{ z`PykY%9a?vUvN;wQ{w)MZpM+JJV(45QDX*P;)jAE39QYfxO| z4CFjyI4T$2j5&7lMr5Fb7&86=Aq^NoWyBrC1BA=CGw3()kKhM}rPG@YH^4ghSqy^ELmVdFB;3Yb z!wjR|BTEo4#B%t1SRvF6GRL@9E0mgYn^;9{>{_eJU6oaJ#`fT$G@ed|)*Ur|1BXEl z0@+{d^a1LNvRPB#1#H1}@k`Yr@NC=w{iw~T!&;{b`!iNoX$hERphfCkG7sr2d82x- zK?lu5#o%-Vcd`hmrIb^bQGAIEEEVYuLqpF)8)1Lo%i(i@9Z|aerberJqf)DusgJ3D zsrxmx`blsLDvor4E@Z4^oS;=<-9WukI@_=3PUGvUj0$4S?-nmsD1WJX1B8h>h&Drp zK|uPsieiy`vS=JPRzJR(w^x>Jh{VjJowwL&v)0zvrra`{;YwdXSw<+t_@GuHccMg? z7W_|QI&nGf0fKGVri>G%Ph1=w8@f6aIaOsCK|Z5R5#1872FD2j^bjE66-08OycP*T0Q9; zW(M>(Fy7Mu9eBQe56A_A2U|`Dt186Se7}igb|m`>r+D%%zh87fN>?t{Wa?h%v3eWr zCe>T{cj-*&KbgDAL9YYXB46Tu6JL;45I5j%qDEnJAv?iGAwh5|dNDqh97mIzSkgbx zZjy&_zY&Q>u`)qK;lvD;_igUY=uaBqbLYy(3`mqeVKK>ozlc;$4@u1@w+vbIP4v9) z-!)P{i4|unzNrV)pOp5pn^Sn<3c+jP1My?|v^sbi4&DXJK~k`N1Pf}u=|;;BwtD+X zM~qXD!%15oi)vaEz8YZ$LTel4Z^c7Gu^?W=l%}h%fL5V*l4DJa7%R-SQOB{N&~7bD z)-UShZ{kgJy?GV9KYSk{b1Gd@A?s6IQEAjqweaao(0zCcc95i{rPEVsWdsl+TF09@ z&JJebd(L#__RJsr%4riH)g{0|_&;PSHJChrBOs-q8@dOoG=;t5x$-ff+&Ki4o7$PYCld*_*i-12=r7oV#7(q^41?7T+jQGND-}I~fQBv6_)i_>lngEG zPv||?o7ul|2r@oBnIn88*(di`qEsO&lB!wxQu$EXq}-@FrjF4L>JI?=^iMFuq;%8E zR&mT{4u9-pYz~^P!#{uwsLLe@qEDjhQftjd@LG&1^^SRo&0D+EHXe*%;y9#Vk;lC{ zRNWol_Npnf;a-EPX?q8Sl|GrEz(D5X-RNs9oo!`yIOZkWeO5#Uh~`B+fb~IRQKrbb za7&0_8mvXClQeikJdn?er-JCmOrj}Sco13wyA6I}0OWDBshXGCC)4}EIj~+p1NItyhC8pdR(CER=>_5uVc0fO06B?} zjQNN-1$c!R9Zs=aB$#~0**wV;zEzx=u7bWpCZjXZ3(*49YSad4zNgUj)R3W~-V- z6BF5EKSxK$w{T%o*W|vMZXmvN4oXLf@Hc7w=6h^lcIDRR%{mDK5D(>f-u;o^1GxkH zM`*k$Anvmj-9wzDz^IPoHH4kmtEi*!cJPMjCF&#@WonUN8DGn96mAp0k!7iR^qZlU z*cfWIxrZ&ES;Ta*^|2I~n$g%K9livUjd%r?>Q*SfN-{-ng&#%xB@Y!(fw*r3{3p5z zXTTe9JoH>3UWCwvsLU0=NJ^pQE^T-syqrJiXARLp# zDA~Y$U&3!-=aB>HnT#bCJ1icV4>4lQlPt#pR{kyL_bw4m(>9msfQ~6>oASjJUa%dA zr8iGS$x4-YEmw~Q;+u<%PxU1#nq&*Nb>v}xO*gtbt#{9$5BsH{OSS;Geh7RwMoN5R z(r13l`k9TR)fD|cJ`IwfG6*q~0i5HLZX%Mh!GJ~PNB0Jb$frs}e5z zF5W#A1JKWA04mp0>Tf`Q?Sxc7&0yc5V}RB(APWfcHWnE8z z5!ACUjua1vj7cZ%3a6y&RTaR_1qZZu)OgBZ&|x*1%4!8iS*kfQZ2&YRp2LffGqG0W zV8(jpeYc<9i+ut-QXJNpTax}DJ{i8LE=gPj(8)SBi1TprpTI$~5ZFcK09-a0zmlyT zrm~WNoy$PalRm~EbIh0fYN}UpNtXe#fZL#t;>HM0q>Usu(mql!g+>=z3^Px=#(OEe zb)M^8@@(2@WvEa3a>)Yjn(;$pJ2|rjmt|Lg_ZAKHf+2u$;Q>^+J{I_ngIU6!t-X1J zA18(+?}5Kx1fKu}0ndYlbz!zqoXxZhbJG}7IFe+DQSKC<6GZTCa@%;PMDOHh^+#YQ zFqOCq7$4YNU9}{MTQcf17}%c%*o(4v9c$UxA^tP@>}ea=HdHufEv5y%1nmp_K+v~P zu_zHL9ZMn2pl`HHwk6npG|wSL!q%t}Vn?B33J=)X3e*vTl)RLBh?+%7Bi+Z^LCsXU z$*27eZGBDNZIAkvvj0t;2I7`O$R}i`RfNm5SGs4K9TAr+xzux{3Q`hY7Teh0zm(Um zTnH}29HQO?xNTI66Xf5JEGdbTF&sB$CLja%*$9h+bpUbje;`-bY1AgHH&%(<1H7Nj z#%gdW+y{LL^9vn{nt@u4JxIQ1_R)%KWoI@6i1QZ6?D(4}aFh4=QQ|InhuTSBVDJO% z=I8WIjav3zR0Qk`mT^P)gF>G8jl4=@0xrXt({I}`-T6Mxd`mrkGw;(l&@Li}W5{&gHTqHUZM$u8wojS2BN|eZV z=lcqA;tkSyvU+K@#8hT|%E>gLZgoA7#D5qz+>WQYn@Rgri-(^46+`vieue1Koq8 z8KNYOA7l^uJu%3ni*doy(h_UNBDn+4rPI7t&Qg4m*UA1$Yo#rU8PizwOw(r$*F5vR zN8R|gXp^Iu#XvvoGlD@Yfp#SsCk_9z%XdKJ9!SRkali(|CgMj1*?zyveCG;lE^+U4 z4bQUwNsDvC<;IHk|5#q!VBlQF0acDG$8)ilQBb%Ka4z&M;fhQ{95Ovid?+4w zZ0*Rv%qBlAP)`~sz4#|Y2c;_IA0Tf11dg?C6mUjWOe!llvb1-S>_s!Nl5iWg0dNL~PTZHuX< zJ+@AE;<|OXb6vJGr_2uG!@)lkGX-!?`dB1TU3gNUlH{s}feM5a{1!UTn(YwnblxtF zZh;U;M}~~exs_|nlB%Azqz$ARCs#q59=oD+Nhd$U;bcv75pylB8_DI)y3N{B^Vkw>WVf6 z<`?dsp0MVWripeZR@#J(q*-$hybQG#e}MQFKMl;w{%Tj*0%@=!Q}a?^s7p~+3&rEL zgFdW;;g_6n(H%vkz85kJQ%s4pXs}!8kZrfp{0M0XI3M8xs54{(X;s z@i=GB*=yEXd!4=B^}ZA%^9*+aFO8E=#}MZs#P+coLvLNHwjsN*vu&qjDClbt@wey) zxfl84`G45cLH}my|KpnHmmS?YlbSUW*Wed3iKM&le!2ZP)Z^$)#H^(^y z;64%FK-WZDs=31$XPR%GX4z+9SYVb(*1ZltFAPzQzKz|B9gKmakD*FYF!VT}#r_^b za?b+Q(m#%JV8?3sF-#e$idEyU3C)Rm9eF8x-^xYsmzDZlEUkc0e4@0P7#!R@^KQ2#a8M>K;9wv*c<|!+)WYCcd}md=J*Nyzw)>gaGm;Io4Gox;!IUZy{M}}9p=K~ z9>?v!CHVsc-kG&eXmX)mBTew*&DOeF-xk zGZ1|j+<&iqsX+?(qn~QNS*}9Y6UrI!+#TF$tSZWT>|^Led!BxQs!?H8Pc`$r-!c6t z#f+QmR3EKyS=5hY(a^6WHl>-uI&oiR+bgK=lAdgRey`B^XA0I>?WQFxSb8nbUZse!Bv81nmmE?5ASt08Zta zbsCT;+ip4MXmalZ`Jo6XswQKWlIO5f{3ZpQ33%#Cc)0J-b1nG8=sYGa1x1D;--9OB_v$V!65O9efU^ht zviK-^BBI_{DsgwmiuMEiM;GPYR=B(n`WA;FX%{(|U~0f>|r$%_-7MwSO` z=lW2Ff_ODJU%JdN4C#muF>FrU+I}H1F~LNhFZm79?Y?1MYlzkE(27C5Je=(I<44p) z4i`4!2B>ycEqt~AW?%NvyYJpZs{f}Cx10Ldr_ygMKY?|YBn5HSX&0FGXd>hk@ut2l z;w)vSN#R|GTTd%yjbQ&~oTa?NL(n#mkKTCX-Wb`Javy4*F!TEVeA1E*?68k`hgJjPs7L+qcD@OgMghsy9Yu0;fL~yBCijM9CKz$@wERYeHnQ^**C0;-3#z$ zC5}?N0qh4VggjoUqfC>}fip>YqE2JrXkGqv!cY8u5F4Ys$Kj8c7VK0$%d^-akI4YrQh-u8gaZO-K zN*6UzIGh{EjAa~P-sk-1J3Q!hWOw|C#Ll?cq1oJ6u+HP)JWd1Rjp5EPF6t|((G?T& za^EI=OewRp;z9m+OxRBi1M6L(+Sio!!KW5m}1Jg%O96aP<%mIEi`8HlezRpbxs*ijV1B=l|RmQjzJ!xl0Ode@V z6$C>OjUC}14cGZ6U58(vGF%tG-THeDSnpE`g*^-4X$DDlRCrzu2g;T+?#_M#DJ}XN zp~?n%XezuMz8w6ntKEgpfgpb^Ltmtx=9dT4BeNnuh3bUg`D1zge6sw!!j*vog^PU# z(93ZskT>pm9G;)R|6{#F+`}DoOQvq8>{jRc`UT~qihqCnQ^5Uv`1`KPf9pzH{kp~C z3(DDgyj1|cKLb_{YX$qF!~MjC0rrYUT_P{^QG^nJsUD@3q)r^LE_Q2pT*$nT^Wpnq zwk0$USeJA)F*T+<$e+6r#QQH(e`Ry;>%T8*$Cn8{|9Z3aX>qRU$?aE&{1Js83oAeL zyqo&ADNj(m@7KA%o#IQX#o9soUQoAuCa(%OmGC}&&XDHRkofFij&D0x?^7yF3_lr% zN_sx%)ezZ;>zQ9OPmR!}SN6XZ$qTr``^LOR-$IXPBr$TRGjUc|y!=wlwS4F8!xvVb zjk-4aiL7`sPgd7l`z*?J;ryso@%#E6 zj_-?|6621NM`lE>jkHG1h4~HK61)8N;~v_p)JjQt z-^<={y$QYFdk={gf!X^LiWJGmwnbGJKJXquuf&~sapvTei}yyoE&8skThdzwIA6r5 znS`;#+qmthv+%KqZ`e`vBEPKgrSW8dhg8Bph~bpPJfplDw&-mQG9%8|}#-}Y;Lq3;vw z^}!c)=lU)$yc?c3>rad(iaam2bL^(Yzc$_9wP81N+pE+GGH7GWg5Z>Z)}RxS zR};pin9??+y9O;veV1}Pr90W%uQbF?FLZfCp|!J0W*2Y$x}k(tezjtF1-s&W)vUUi z9gh^ltcj4F@a>RlYpJ5-FSCOAVb7zZIi(kmU8da$%xkQr8^-x1z_f(LSV0Lr)B@vUH%w*uj4%F#H`bE{_}m4 z_q#}yhl>az3n-tCb;Q#BkR6 z$46`#(3tVx2-Z+l|6_sc$pxM$v%$FBP6YLpi2Q@LlVM=-!Te`x;Cta}ZY-|K?Azrm zpPEN}_&R&^g}>(&IT+711f|; z{_}hS*n{aolnul(Y$*Jd^{nJeO~6Of{aaVoUO1io{>i~_i`%LU=?DYSM6SU*;B4nA z<9bc4>Vzi1h;U@XMiQp5e)z8pWkpe<7ewfTR{34wG%@n%eT+No2|hjkEx{GhH~(nr!k8@HszNv-!kvtyY9Sc zc@p&HV||%?fs2dtqu=7h^E3UM1Cm3-qf~M05>NJDlh6@;D7eb!2yH%oEP4^D4V{at zCH7HT=*6t5++toicPbN2{0hx7|5iXm6MGMf1uC>@kn<4aH2f2y2oZ*u3f}^&gZ4pv z;kS|Xm{E8$PK?AlMT#%Y!+zfTu=?%Y52F9{<>&OTj|}5BW}HCJ|tWah?d5%e@Ak zjUUPM_iqbb3mC(%_6v(U68SFpv;S<~b7m~H2Y(B3+ObtF>*)VG@=M*T;M~p!2~RJ7 znEmUpMzbJNO^18&{pccY#{n_U964A$>1p|vOSH`pmCC7~M5FVkI_1J%K@P`md z;0xa_W;tm;ZW?YEiN(6?&kuLT>H4Q9Jx-h%0|e?gQ}Kt~hjlB MFZPWPHL0v}a zOoQIVK*bXhNVWL4$l>lqrc%`d>1^>XF-ICM|EtJTURPdKyp#d;>E6p7oh?lb$Lm_^ zqML@cCyA<6HKy5M7p;YH5GjcN;P+wOP#jDSr(#Z#?y@v~AA>K2tHP&;&JR@jZ}Lm_ z4;2OnKm}X4e@U}oan=XA65T87F?cj}rLQ$OA)+_pX!xSguR%S6CA?q?6}CdZuKQ7~ zq_pts>Y{x`CyN`u9{Qg0b58~7PgldT)|qXq|6XX$ZOEzrw~_s~uIrJADRoM|i)+RE zu%qZ18=Y&d%u1! z|NN(@A+7mVb4c^%=91R^oq^)-s=KBau5HM7Nxod>k@Z0i%0Nvju-FzeLH{H zebr^c>A}a}9=~#W?4@Nl=I2g*=PGJ0fB45$ce{RjJ*@Hc-_hcB(;T#c+Yrv}KRh)t z?eZXAs(4`fz{+HFa_xYe{%QSB4=5O@Nn1XoW{7WE|9*Rfr3ANST;Gz0-YQ|`+-iRF z>)wspZs%20ASr};i4=v>*dpbzf0zFmS9PFzeRDzY2-Ou`kNUKzwyC|WsPNqz(+kLJ zYQgN%pMTez=u~{PWZ0|8Bc?@<+d1f2IES+u=Y=eFoHfnUtdW^S`I7D07LN~28PwSC zQ*u%N=*SA6x44_Y=MRdWb|36TDKac@KXnRF&q2F`#^uOH@`9Z_xg$h70p${|76vBTK?%iCtqpwI@dz;VGALHU9W6e zR<3=ItH6tae)GO_HQN{4f4FS$^TbTH%WqrYr=ZTjsR6(r1OdWoLAZad4~6}K+Co^1 z*R7$UMg;<9%&_X0*CUX_u`M^LhiiiQRX4Y<(UuOMFXGARaH?E1@eU>Xf!r_;OMf z_ekL3$gH@F@o6!eLZ0wC@ME1*761Iz|9SwF!Ko#}8d+b8dn9LI+?RzxQPt8hL2(j z`C|q7!kvObemmmlgBniJ()YP(D%L*R0h@+CPeolp?Ld7;`XJ^*|GL*XfR>a^=(-6D!>2Hk`C6eW=ydScpxi)O zU_#)oz_Nf!{{!3yWGifdxkMJ&J*cChJ4rFc8j8G3;j*`|JE%?QYtEHgoOnY=QOhl$ z{1@8NukCnOujq?nv5syYZmY02+goh$R*v~UL%lX${aBf;TCJBj9%EWKE5mvc)+CoF zR)?qYB-s6~lcr)rkmZAG1)>fAnYN94Ku{QpjM*O_A7=~e$M3}RoH6PwQ3*hGiDl2# zMBQJFP@(QE{Tp3B=64;abONi2{%mU6+bNek)Kr+NY!4k}4zE4kzTLjrS?Ya_vXN_e zb-{_T@%^t4$V|wHS`(<_tz?X$v=iUp_hC$MKQG4>?S2nWAjkXGhx^8li>nNo!Wsv= zquJAWtp3Mu$gkXAyQ(EM?s{i)W4oazS+Yv@P+HgbqXXM~u(qaJ@u#(+vVEq+pN#v+Y2$KqVAE?%dSc+32c2QO|4`*!a2WZVSAvyF=E! zx35QZOVrxCznj>_>3$}vQ)OA}Vcmon#!${x?lA5;P80hX`z8A*o54QDN?;9R?Pm>V z{bAe&F*yZ24aW2)d(L=cU?v0t9fPrBhT}>ICn;@=wVWRAM9vv{2!ROiXOGv;6-(Mu z>H~f~Dtl78wPN8Pd6TenUSFfwCXNs%ig!rosUnRl9S0%15ED@%(BDeH4kh?gUeN2A zIM#oR&0recMP5T0L%l=&K-ovS3y^6s7#T)_!xG7)Kw=UOih2so@Jw`lccNXJTqj)v zT<4s39I^Hy%TGgqdV%CqS2NH`4QkupT_`nb_L{a^_152({$_#6WqfDqF~?bt+2{_2 z>%QkGbOd4=Y7TlH`U6UXTnBn7|02&JBawp;D%e%%ZOCZIT}T%60_-9@88Hd*1->6v z1gY{c+$dLv^N{P5d#oqGQ|O9!V6A53Nj*w8R4dUu)Ev?DYDVh;vK+%0%i!N*JjC{~o&%^9)TzN1`eb1o#+emsjsu;jy_Fxeo&!olTC7 z_F=XxORn*T_Jg8BoYpJv-q8D4@>O-(*kS+UX@KU!f}o?^W3AoV@v^zSqORe+r2!a%HP|w|j-;Z!VB7hF11^Mo4f_~s4*118 zO2MN>xer+h=19ORhqG1LYHWaV(x$O(wP)KQ_71DwGRV5ozS$iKlOnCCHHcH5j~1&& zAkUO!NvyK#sx3OFk!`KB6I{#Pv)qTAdTXpPOw%Y=NZyELZP9X*p3a5nJ6rt zL3%*mNBK%!%&6p?_sa>|9S(^)8#y}65O9$n0A`RD;y0qVAy&aMp$O<^=uj95z7-w= z_l1pzXxzh{Pi(s_c=KtK*3@p!v;4N)G1H94w3W(IS&X!wbcbx9;<<9HimSS!Jfgg* z8mj%rc*Yj#c>;fqK@;;SU+K{-6z3oAd>+^5n$H^_rq2rQM%Gx`dg58kV#HGDV{f15 zspp>Ot;giq?+t*Agdn_!otw=w)z?Ll9W^bNTb8y5_Jt_h4Rh^>J>OtQP!-rtd_I07 z#tCqlpLA0caM?OVp-yirhKxluVwy3J(KV=b=t}H5;&N&!&_1eRA7L+G9c4gh1*A{7 zNhk0ZSi&0=GREy~jmRt$&1?UQ#!RkWE#U5K{eiuq~M!D5|3bGsTVBq<& zg5$zA!4&@}zf8Yw|J4E2V8HWG&}+e89+`23*bj3WaS;ZAU4;!p@X$U0OLmW8WM^?E zFz1o(Bdqq-x|i~!lFO2tGP?4MDp37e^^Yn;-K3Qm*W2i>o9^8%sjUO_1lKA$rC8}! z=|d{RzQpVMwKSj|BWt7irRhRY^jWn^)hd5gfLy@_?5_JG)iK_d%b z0WbtSA2A0N0D1tc5d^5*wcnOxTB4h(-lbfje4##T_+a;i@o`;b4fPrMENG(lS`FH% ziV4y^5`pxV?1SQh`hfnU1?#SdN8tj@vzkoNrX$8~{YT9Tg-6`hi|wuLyCof>+N7Ig zd}*?ox=m)&60^ZP5-1N6Eb}b&)?W^^_ci<(8iAXQvtVAMb|B2KDX_7yeXyUfm2ez# z5@sh6%jn{56RZz}2WI+{x!0)vxLff1o=whhN0;N08wERyW)g={yJ;!Z)r6_Y1#XyS zrmj|%p+qS+C_T#4s^RLjnpE8j!y)rD+k3|hSD-t^EpwfBU3A5`cY9_)cEENbtbn`k z7V0nZ6=F4fENm-O0l5P?1UU-10=WvA2YKgR?pY4%XO8uQX@YUS(ciq+hIZ3n9jHQF zI&lhVAZaaeH6aUs2{#DagzAJ%cDGqtbPiR4>Zk67?Jvwlc)}n8Wxo>gR;1kaLK)C? zsKN2;Va1ZF{dF@t)71GcAKX7ck-M0kz>J^`q)5p*6g1UBmJt?U9>DuUINlYIRd5BW z2it)UBrYIDQ0~&wnf~lU>|;gMUR#MNnX)Jdf>(W{P2` zev2{R_Alfc7E1rZyXjvbDDXA177;hXnyrJi5QS7GP$d{6++Q(g8A<-9LLWylV_GBk zhUolevucPws0&_S$4>JKy;Xfo`A|`*WNYJ0BOKEqy8uEV9N&OHj6aVH$Id{Lk&9qi z-Vd(L4ut)tZHT?nVQ|g!-h-}zZ$rF7#32&l8t7I?u4kNUq`lI7)UaRoS@**@*xu(Y zMWV1-m;s2b?odmd?z!roGEtSR?olUdteQ-{!_0I4LS7?@n7epid>(R-Fggee;28TP zElV~)Y?98^A>6a@i#TtD@gZYF4g_BD|L!xAbCOX=-i(cgg*vOv93#|t#k9$?%{tp! zWx-ne+aI|f!k=P|#F6BGh-B<%xYTpXsj%O%o9&s7u@1UJZf80kIo3E`PL0#)5ZE7B z;HE$N{kkzaf&Q)Gmg$xy&lcre?Rf~xML!^Hpmb12Qd;r3$kE=xjuqAymj2c*tIm4B znq^J47rPUY*(4}uu>ZLLfiS_RgL)I?bd1%#k_Ps!>~8N(lut9PckMt{;$M++NF4+s zJ`HyqhsV#ujY02&7TaIwhJMBsSCk;m+`N`t|sR@lG+KNrN%j z@P8rso-6Jxu6D;O8^^NQ$kC72hH0*=_p2YPwdx_7T+Lu@jP8PdiE*#_igk(ov*WRI zj*IPvc|4vc-UD8t=a^GyD={rFyw;mc8C$f`>R#moC0f;`TBjMI z*O?EvMj>O zjcl>REV?1OFYYfBssIAeJl`JdQoA``B%~S=4JyefaP=Gq)c(cpbj^kR!e~j?=t9;p zRseH8Z9Qorb{PDKiwG2CVA@$anQ5jT?vkja&x_aJ(|!QE!%&ik9?2#0vQv zJ;ON~aT0%@JdHA*RErA(IN2~*FN6R&?|tsAhs=TpqqFhv$Qx*Z^v~1}BoWRBZH0e> z9)JiSryviYNpLA547C&$iktw1cICE>#z@V-@^bO?z6-rweQTszRgN*%vC=yq)&)BT z-Q+EG4|BWRyS!BBeArI77O@bGz|SXV&{LR+3?q3A_BS-c*=NClDIW#sYJW7x10TZZ zKkAO?IYzUo$THcMZ#O!+oZ+s+POBYn0~0~kF^+C87rmH7WN2AGS)&*wl_WTdW-0 zYWrYknER@S<2~(3bf0$yIR@K%ZJjo{{h2+)De$Oa!?83<3A36rfwPRS3H$_(5k_$?3G?exrs zkHv5aO~l*8rG$C-mH3|o6nP_c7JWUVgmHu6%Lt{^e*XU-wEqLU~vYm6H_bRWRL6z)F17HqzeD-eYsysE)NRKZqT12{(;$ zn7NL7)aS8J1h0wxnwibeFlMnT*&Xam<~;IvRJv2C9Vu~ku{(RZd!!!yJ+}su`nUDEyU5-AXGYHGkiJx72E;m zBWMUEybyj8z7oC=z7JjkCnJc6k?>K_ZubQHSd&B>sJ^dSswvh#HgoJNoLgKcTp_O4 z&RNcIXNJ@4Y;rG$R-$$hhca@x>-k~)P24=@c^a2GmpX;EkcOt*!R5k^+fn))g+|gL z&XW|#Rx5KgOASjbPJ6m*vP)t=U|y+9Q!P>KRc2~`nOYn>AnC|d?0Z5J`8aI{V+J#j zIgk-cKS^6h+f8#(2UEO+>lg%L7G#TOpGWC^49$U|VJdHrtInQdy=^JBEU*?@`&+M> z8OAi-H}yc3MQK)vG~@IIMvWO`+vVtX|AuYFZlHeUB7`l$<3oo9ZxbZ*AdFTL0&hT{ zK~#D}?Ku5Q+4r87j=vordv?gObPQXcTL66wt@5&6QPwy5nJR^Ju$V0VBK1`-HjQ?z zgTKa{#}6l5#P7saVzt;7TnynJF_0{y45p{Cc5ux;Yxo7+MRYOttH*16tmH_aN}kKV zYUY_T?JX{u=en2YUG84(h_TT1GG(Q-R{UHtTj8U7XQ_5AgOU(-L^)zOf`xd32tXag zJi)IfFQB)xWSoVZk&FR^7m#=(M}lvk(LA{IW>1Mm;{Jdi$Xw0IV>oeR-IF!>-KUz& zHH@0X`heD}-C432-8$ojqE{<;d)fm}0s%3Wvr%xxnrQECu)Xy|cv9-7+K`F>bV0vXXHUwLOS&xau zATTnt6b;9OV91!2XdUtcJRTbE-R{ZpZiXeGPGC8BHTDm3y!Wv6r*@cJC0f~+Bz~!A zHbz6%kdE;wVQn$_v5JU60n0diN;JL^TZa3N-;FzjY;o1+&&-LZPQXAReh#QD1mH0TVM3`~Zn@ud;UvKWpSyG_c9h$$H_7hzUG1GHUaElT z4w{f)>LJGZ31GcLZHMin9UA)s+h%K>mE|meT5%GF8nCC1^D{7;FslLMCbsKc%f=Q& zXPRuNSq!t1?yz%xUh)PqFBA5{KG<&P+7uh4_2N0=1L6S5Hffvun);q0$JXtsL0GUH zQWUixoy_=`K9t&@_y9c_y3k3pZa34d70yN&3h$&XD}v5@PCH@Glb6W8c}Iv_xh%(;w@aXnH!?^@FA zYIxN0rl&#!&Xb7=KAVCbg!TsR=f>c^*w)C%?eiLUHUtAqeZ442VOJm1H5%H@$qt*h z098%g$#8J*`PTdKeCKdn6eLFHnPa_T7zOl3MOLr-C1N4IlKPf?hhN~|;~&l+&Cp_s zoMO!`QB%j5wy^eoUGaVYNOP1l9oc-?;qm@LEX6#-5eb<<512=_(^BbcD7jdkH`Xvt zoY*#{iP4hX{YDcHnMoTHa3*?YKXL4XkXQ~CJ>K$22I-cyAL`WgLSznQgodnz>Mj`? ztj*rb*n#vC-d#T*zXMz?Rf7TeI@?);RkK^&sqWMcG`_TYT%9l{#y}d)*b3eSTX-1G zSegsqJD!*itGr@PA76A?woyC7it*+lh4}O2t#mp&j%V`m_1VlC1MnDrx{z)}{luyV zmGQNoJB(_AcP^=eyI$}|c+~Gc`vOUZ7~--3_WudSBbL#ivo#PdKzsvui41oisA~XE z34#aCp>Wjc)B|J!As6k2J@ceE1@?C~nBzV8{~EyX%mcWfT<2Nq1T)U!waG%R-6yXx8#{JTGRiPE_6Lrfc zntnjHlaKRW3GxMRcrx-{7~JfTukE|uTP}?Nc&b{`AD>x4yF-=>KQK2T^NbOqfq&Cl zx;o2L<(|uwZ@xO9A5R4U)Ss#w?KkQif9&-u+D}S6+KE=Ss~F&?_CvBED*-` zCBTuCK+>UbfD5_dx$Tw$Tu3YYBW5eHj-sa?rqmP8qEA3CI%inc8&-iSRFzR?Ukca7-_5VDgeynojilnLjk6_)B zlG*~muTkE7Q3-*tCL?s*N=sp(-$Uq)f;*GmJgV>)1Ku?D>By4<6EmKs>Eir- zAdpSs+*)&a%&(O#Q)Cx`{RU-I9P_$b$ z-*Jlwx_z(}wN%!pEH@f~eT`tk3#pr@K5w}0#ZvbP z5mDTjm0>JD7fpk00Cu14zV7S*pIeE2#SRNQG+<#mJFT~0Q$Q*8G-5IE{~PEB)M~CD zzmT~fIbW6a+x3EVIsZt@(Xi})^PjdsAP@QJ(Rp#TVRp`6#4+vkwlS5vi|il$in1&J zv}XdG@luQ!vjE_*NYE_YexH)qv>^p!jH4H31odMFUEq$QUm(lyw}D^yI1XFigL$0a z=tg2M*-jjd>p_=c&JcnrN#smaq3Kx1g3?WSkXM!O;NMzn@ATYK-v*-*fbS5o0lg1g zKiou>w6&V*KQwLb`3mkk9WS6Rpp{Y+X-PnzNaMdd@Kc~h@RELc|G^h>UTk_kzTm;{U}=k|o&1PBjTHzG0wET?GF9Byn=N*#m%Ely28Mmj zP)z@}uYSC#Byb zKvBEEbu*x!;S<39+f3-bu-|__EPG75|K>?+VPM@1c^IgZz`U^ZZ*_Lwfx98s)Y;{a zP8H>}=ede`X$dcfy&c2M{541(u@2y8w`xK~v-@tSZg_^Vmd8FHacsJC-ru>Wr>+=j z8Za%Cz!ehLAj&{IZ$=&@r}Ec_4UJ2Riw_B>O|+bAdiX(b>--tial;wGEpgtY+I!&l zU5zWoYA@Sb>Gr<12QKu0n z_dM#6h<~Uq0373Syqv`pdWbjAjBVKB>@--QamYVuq77v^k7>jDrj%J(ETxkHxhDf0ntpuDQV8Q{jE6 z#ke8Fp`^ojE}Ub!)#Y39GH>J4`%j1EODYsy=M7{}35bKUu0M`c5Kr~+)yPI*?;zKH z2O0d$M|hEI!1p1Y<856aFAA}_v;f{XzbXF9{p$G1K2y2NSSo4?VK+($1CIsS3AqN| zQ3EX7w2u@v`AE%m%Pq)rtcu)D@@SI~v6672IBScpyJwWJFUShr5-?LbQ5%fE|v(QRq9^He4zWh28?mu#A`2 zwux%CR&jn$XuQ#rrTJ=m=Q#_WpXNn-{}-nyPnm6x?uI0@V@}hLx}|kH8`4_#c03lX z(l{OWu;)3WgLg!Kh!sQ)3(DgSqooi#2Z%65dA)7eq1x zL0)}mZPJ@HKQ&WyeuhhiBf1C5>mqS`X48ban%Z9tNB`FLER@4_WhR$pfMtoH323r7 zS~0cHE2>Mp-_Mmiuh>{u-Z>WJ(b=@6fywb(Q?93nr%z6~m^d_Mdf1e}EPpHiw@-yn zCO?WV02^MIRrBxjx@08Ed-UWW5Mz*5QAORlOc3!_%887V;9T!DPKFW2P5OXH* zGb-#Y&Ba=xk%_l z`ceOH5hoLlB;QFsmB5J@>DNum!{wthaAt}>H^;w9xYG~88h|_Io@^MT(1}yTtEC^6 zC-ob^{&E{VmDE6MqOYJ#$1HZmXcS%5_2;VWWew%H-#+y>S|4_qMNRVW8k;fNeg^!0 zZ@hxd|pQb>27R1YBqLL>JQAMqeu}4S(HM~OVb?h$Sw`JZ5@CsZZ^HBgJ)|GTR zh10(#w1)xqEA_^1Y?}^X?id<5uty{ECw4O-gmi>dNYI0P_1(-+&g(7iSlVgpd#x%m zb6wNjlWiB(pd~p;%MLQ&A*ueB5iAkXx?p0v7469ThqtokiEoo+}daDSk&g@m9gC6_LrFpQQfE_M)3Ot8_AFuSzberrBbB;lvKVtA&>fBqA zbV3w`LS>WZk%e&meZ^&B-vbxc!gobFf($wHWr zY@`(S67s+A4TWWaDaOeJEpZsJ3Hutp%emihN^w9e6=lgy1_5(EENZ@K0=Wgt)Gz^$lW>}A3d@=}^F zGn@64xtHn$@3sBRCzN@jYdxf1f*3EqsCus%qm^q;Yr?e2`lF_BTa|<1474fqljNxG zh?YHSL}rH18DACu18+CyG`ol+zxsRg~QVB8mvsi!3AkasJAa_7xof8cZ8L@L<(}UWzHFdQw8xSok+iacCzOz!M z>OU<<9|C%;eu~wykMg~m9y7-ijC_opfw$o5utY2cyB~WTbf`w7{)3-~4uVF(hoCzM z!{{pREkSf>MD&CBNeRgKnGrYvks-!ZLqx6;SE=V4#0Yue+2lB7o~}QmVXLE5YK20! zNwQMZ(YIR+CKok(j4<0`XRDhHX@i|cq49UfTj=>r5-X3ffi{VhsYtdm3;{Im0g<+Pb*!@J+Y63|@iMf6eFQO8r=TFJJK#m(Cr$F;r~P2bZad|`YqaY z{Q>J5D2GVq@CC#ma?o}m&+oj?ZJv)$GN0}dz88*^l^ey*)2DGfUYjGQ@ zt)ip8Ct7?`QY_BvYwuax^QU*0cz~=}K1VK;+WLUPU)SmGk9{~fTL-uopvAZ_dOmlb zFWXPz!{aPvtOT8kY*v{MS11qSh0F_z5**_lX28j3af{KHkl{!nYC2{vejllwl1&Yz z93X_F>CkyjkL8+aw5h-xX1fVcNUxk?+XLfb6+zV625UT2(_0--(^x;M^;uVnBvnP! zN18^N*P4WeMcOs$b!xV5vH7TL2x0>M59Ju+CyT&&$vMr5VB;A6WIvF-R@+ZY2IG@0b`D8 zhxm3U1BljMXuj4_C`FiVcpspnaCIoD$6zumPfKP+K?TOnW zUA`5hU65(!-IAop-WPjqn z%(wi_!mfZ@0ts&gEd^8VzHOSIA*=RkrkhuJ>v0>HD|{ad0|K7-pZAGj@1nO*QpvMP zMxvHDiquY;My8P$6HBmB2)=u#d9-Gpj3!#v!|pb9Il3qG;iPJ1s=m&A(00()WZtYd zs{9lq6m9A$rUkBzh+%jk#Y;WE% zp}gRkep+CybmkY(T>Ng@VCD;YAn748&q*`!s%u%8s$WcU^)f5rCeO&uagx{^@)OtCV;I!E+`UrYl5!ab3IGWo;)r(s~|A zLe+Ak#-8NSdGC5AI!+pADM@|g_H)g%{-5Jyr$nd=cbtIl$KR#4GZ%2SaMy76b6Qx} z7)ELaSxK0MFT;i6`2-4a6S0dh2cL?iqn9JI5JM3EAT}cs5pUor@PTk0d>pbHg#>5y z9@Gy65q8%b;Hh!%^E~oCflxue_n9}-Q|+4P6xoN{mRaVTc7R>8P`5`v(b#3aZF4*J zx!M62O@GI0tIRafFisb*jnhukoiyAxFR-t0&xh6^($TS)vFJmHC#rJ3 zR+{G%;yF%2Qc>zCLFAW&4BTE!8M+_l2KF7EPMSiY(2#T{kowS4#*xd2uL%PQ%Lojj zi};1qOL7tO@M?5D%;55~jx?lecY=Pk&^X$9-Z=%}IX&>#2!8|~mg0G9Q|mPfmKaF8 z^vo6CR)zvTi=W;2=Gj9X4p$OXhxEhA z2=&BP!b;pQbS7dD>@>6=>=7J|oP^AWOT00Ttwx3Fm2`_}OyAqSUQlsPkY>xiD=uq} znby13BjZV_%sE^wm&9I9rQxQ)ra562l_A|wZ0xfr90YGJ>>p$dIvvx53BzV!A=pd| z0lfp+1%Ciy3J045+Xdz&(jX_?WXBAP)v#ALQTtk3pr37?;Yfx|Kz+ntCl97hp!5)S zplcyN?PTLHb-j!Mx&|L4nTogCObgRZhR;Qx#O7hwV)monBL0P=;RN6*qv0WNBdiz3 zfNz6;hd+S#f^}gb9uL*~98w9Fp(6>`$y=yXsFTSmd<(h~J`qyrj&eP6GFCPJSp`=r@DfJ`{;WlP3;-IccttCv zKL9p%iu{6nfWlANp(LspjOeMnyhTjpGE4`zkf1pLsZP0<>-#zF%*hhp8 zy@Jq9ZDIfCyCUFJ@UD=upbUYNbApTq`mc6lh`!Lc&)JKbL~rr+1TGH}`X6H6!+vmI zHZD}1l6j@6irt#e##Y-ySE?t?4R;(dUzpn!c-L2tGBK+EY`+oH|rOf;f`aT zIj}HfB`O9z8?_bD48?nMT@lV^$0O$)_XF=(*g)ib%t`!I5|*MMuO;roJcmLYpA9he zDS3{Jpt!C!8NS<(L-%2iQx-7wtVYIuavG)<((V{<4Y6DSSfv*0K5K`i%QDAy$2kHr z8Puak33WsYiA}mn`b1_>KLJ$t3gUP4LdZm0t&XYmNJ+8@%F%j~eH3&ZhELo|no8J! zra?bA&X|J@%XL4sk3d8kwdJ})h5&Q4t=^ID(mK6%ij`#gp+BP=t2?Hv(iwCk_45to zrtdbW=O^3`JCs;Q?g5#SOlcr-h_7*n(M9l1Z?@}>W2wX7G0un@Tx{GSZTh<%7 z`WKo?7a4=?YuyVVUZ7F>2RaL~(6h=F>KyM};NAv}Lf;|Y zp!cwsaWAvC(h6{op&r{614cK=u--lap{A7aR|iwViJ?;eP^K6&%1r=O(RS5L&1~aC zM>5=me?}k7E%C|c`LeE)rXh=+bV zL^KT3UDNK-sSKMeiH>RRQ151-xV7HJaJ;pyHIFo=gQ{Doxu(Wwc4!~!BTSb75~R+) z(Xr3b&(UU=*qa=2?h#Nt+D0TXeSNt8>je@1GM{7YdRhhvoYx}>FpT%OdyYp5)u2a^ zlUW8Hk?-KJY5Oqu+-ji6H=(z+<63)BXSgU{b<5o0nTl$_Y4A><;wytFKxfa-RIYE( zz1O#!HaTq2k+`?i9F~TCgL##bg~KC{LzhEFKu5uMBBRj)%q-AfFGL+hI3X@)gL#B@ zl3Xh)1T(%DL@T5_6|w46?FoR8BkHH=5Zck|ol1;ihdfHLK!wwJO>)N^X!ZYbbk*@q zCQUf@=6&PtY3ikl6^G(}IOT9CE{7j*$l-dxp~c;;IEPalN|Dl*x;JT(#@*!`zQ6K| z?YlcWyE{Ac%rodUMt`nAkSiF#kLFMqHCQI{ldupU#Ld88A-GA~u`Y~t+%6)i)Fg=# zI+)v$MgC#7Df$%6`KBJ~xxhp>(Y(UG&CT~_MZgKFtZ=xsJwaIbQ z{>VPa@y4;l5w@MRkWI0M$NGTbsQDvQv?hB_d24*#0?uG__z~Vg_?x(mxR)p(jYLrN z7&eG<99u;mLu?9H1kU(6duMrGyQyy2$DA{qU!B#id4ML!^{WDz!EXVDKh2|XOf}!v zQ8go*qSS|)+nb|Zoq~&rcx)GyL93!{L~oE}g#Ey_bCUpd#%Vt|v7&luZ#iBzK}6w3 z(!Y`w!~#5xIF*>1P+(+rEZe0PG1QUzh#Oxj?ypF%>@153qFnY zz8Qfxfgyo_|5RXgXefRqX)rp8a+cZ;GBcLazEk^Bj*?3We}@M65cgnbPv>2WV7uV&!QH`A!NlNK|1@t~m(OHP@$XsNo(H}6LwRUt*4Gt%s!jc$t&Q!=zIwmMvrn~pgOz`t8SJVH*OjG`}L z%LD@@*JQ0^>qR6^DK-lC(UWG6vY5>|wwBj{49`%+@mi|Fr>cIk5dV@46$|Ce7Z zxZc0R`^vq_#d5uM9q~-@7lp19u9NpsY0R4tJzv1FF`rPUB86eZ|Jpslb=#Hd;R2>) zZ{SWKDWZR%qT5f%~n^_Ec# zoM`bNS*FY^?kZTs8N-y)J5ztc&8s}L&||SaHmI6uI)*U=s%?!6=O7(v|FIglS^N?n zmotDaBcBfAeM4QCGsf#5 z0pAW^PoL7e&g=K|@{DxPbCF#yTsF_CKn#8_Qjcw;o}zjv?Jergz_{_ zY8^zd`#RTH3xV5vmvyV7(la)cNxDx-V^lJ()BZsdi331Ry$FR!J~o^3fNaMt^;)c` z;iA@{+0{%i5^S?!z6N7G*;|B=c`g|zFf*T_!|?Y5QXk7(=23dY-uK?gepx6Ne;oOO zU8Q=cOzKVyhdx8LlBx*ZaH|6<_fY#y^Bdz=<1F(v>wfzbN4F8`*+J;UAAV7_NXDzzRouk?nasVP0R6yHa`7cIYf%Y50ZX2;)j|J3?)Ie5W0D zo{5${_MyXs5p9h=op35%rC7vIf(2LQWBL}u{1*`J5x2v7iwIu!HG`GV!5wyG zxo>y`{+*#igrj5=MNa=gFQo;sB+`oD6sN|pplM|Nk=iZwhnnsf_Bwch)r8^bK&qT} zkn#ZH2)n&`_DIWo^DQge)d%*AzlnR1jpTde-^i(m2-d;Czx-64$qXde!}y|pTSa=gtXipzG+p)ni~ckCY31#M_X*r0>M8Bqu2W=|LI}^5_wI8r%%)xgA(~Tf=h$w>?vx zQ|w~<5=WKmoHr&Q44nWh_G%&lEv98~iUc_#svrQoDoK{N^$SYKh3|g8t(yq^M`M{S z#2@84IagXN7{m$EQObN|2((`i$_>qed43iS2S56bKy0V8*<}=(Z`!7NE`*Px!&ol_ zR>=_AS;brM4^jC=IXa-=?YBap3=A1oh;%>{{`(h%fTP!tJ!2 zV1?PCYFW3wZfjG%c}M_98O)E0z{M8C-;OuLAd35x=069$x(o@QDR$D=iBvww4_kkRZpUmGWMiP;*8J^=0 zGjTw#1Wz%BUH^YxDE#YOrNPMx;TRkN7lG^a>-*3;h=hN!T{QI2$W)oC?%H(ob6BSn z$wic*)UDV#;KhHkN>&suuH8BlPey=BL!D8tc zYdecVPbanTb~LtDF0P5HaW>4<4RB-!wWR+jZ2Am(4DAVa7THOd4DB`JKO+mMTqcdR zk*H*{*t7N^!d z%5%`2?Fu_7t`n|*+{M5d{EH`uPdtKX$Un*Nk&C1)fZE+lkuk4uG2uQ@XVGQBcVNF% zadY@jMEzuK6}-qL@;E^)w%fBv{j;QPe&olw`HxGCszl($+CjdGegXNj{6^PPdn;=< z^Dt8v^F7-=Pb#h->Y!=pu~ZJM)6T(&&~3tH3XW3(tk@yRMqo!#G4jwb?xL4vqv+2x z{b=l|RH^-j_6{)RaT$bF__$Dpm+h!CpEZ6m4zyf$#DN}fMP$@LjQ^NznJU_7Yz}0! z7KLBn9K;H21CzwB5*Nvpk!8{BnC(zyek;WD`r*+76>onaqFrHu;(O)~PL9s*+fdu`F}otNu!D`?CK^#1)t74r(@-ce`3b zfA->+;ty`-4sB3HWyME!Jz7yY~t>`hF-ok1rPLZd$EWZVn zx4TfP3I5P8e%*e=8Dt!`3~eMy@Z{hKZ-Q%-{jkLczNXpwhlWD)4||-umuCd<_geI$ zHL9i?px>`Szg@^1tO_ec_nPfZt8xnNC z@CmokQPhR7zbafU4XLWcx(+q>Y97?BYTVUyTl-Ky!W3nxv0SpqEi*0u*|gApfI{Yb zWsIo#kt?P5xEa(oL?rwxj@g&EklY`5T91+$q!X~t*M;AO6aT*sA+yd;@xyd=s3L zPWLWbj@e|QSh)6yV2`Y)9N_&aUmUYMZb?kr2%Hel)F6hS!c}A{(~fVN+|;2t2BJz( z_ZHcRoEP}+l?)te?s~O;X`>=M4tFM`vA!Or2oC|KhT%1 z#O;u6C_`JLEzux(Iyn>R2e|wf$X&`R)?Pub#36|iyo2Yv9q){b3|*{;eESeBQ_bV@ zR?+{!3msyq}Bqg0_ZW>{Ed*N-Nw+9q{;N7Y?S8)J@x}{to>x}G z`K7p!uFcq$k=f1=HwOSEpDocEg(^jpX6oQN`)j@6UWB_rKOW)f#4~Wd_6y{AcftBg zr1a)}l`Fbq%|{jQX$z{SWD&L!#+4k_+1 zk9xk;u|JES_IObKg#CF;LwQgr9p7efkFPxq?e<124R>p}_xS=;eY*d7YUNkmPgiWP z9kkbh@P&MYF6SWJmCVENynlTm&j`@dAn6U}rOu(pG4t43-az4gVKjdYi$EO&x1#r; z=fbc%cpv(ePL{A2$@;~OOL(qGX3Tf2sL;OYbN_XY=w8%I%eTDxefC?V`&_y_Ig&49 z!a>%=FLinc>g%Zh?LbA$MW? z&C3_bB|+hP&K>GG;-yd@cy1)Qfgu&tfS*Dr-onX6TL+ zSjyn-V&G{7v`AJQ@3EkRpbMv(`U_WgB0P#%M^SLtqVLjb`D;ZWTAx5p6}KFmlqZE~ zLE|598ZLJ~l6Yj#<%_Rl8i3DGG%j{pEKafjU2eNmgZsMTRo|!ePjPRKe`~7Q3wBlm z{a^lC;Y?lxqZK&`_K!`(7+_5q64}4Sos?0jY0064BXKPtw>yw1NG(juO1qsrAi*5f zPS!>6jd>5`GtzU^`ouuj@tQYlpQsl!4yZ~ja^w-;PJBgu+xU5VNnPUw^EWs@j^gix zh@Ki#t?Eq`tE8}Sd{MX3L)GI{m4=YB7j6L^lQpE=&WIeAIpO5g0h5M|_$wp2#S{+U zG@L|xN7(;9QI`p76h9JHrrmD4v;EL^g0yFfdz7)3e@gefetqx6%?Y>mJWS4g@k#Mx zUD=1)XX;_*CGPbwKQluEy`ya>^(4)+CLXZuKXg*U^RN}{uY#$f@BGh98TejD!SA9e z-$nl=UFkY>aMP&Uqq_~+n(;b`2RN5&7QXhWdbK{qu_p8uTgrVQD~xMMeb9#1+8H;N zrLc}D_;{Cm3Oj5#8GXz5R#`SeyTX1C*3TcL3}CDWyTksYkzDliL)Du;pO*iuYue%- zMK-fni*ltiWxFLp!3GX!SD(nwd;a-u&l_8><=ou=;O;9~!SD5RK%W~ZV;LV{ zAMS7ap$wE|6-o-TO9OQ*J>Esd&8Pg}9+kIGXx;LD+tqC^rMcrLDSk>uNSh;@6SuSt zbiS0)Gvh_)x$RO~#+_Pork|p z{0j28@2b*FDS7gi@~|$a^Bvanb>By;8$y@)uj0C(SRp#5ZtBNju?Fa~{ zv(-#(1lV7_Xv+k6Md!G6El#!Y#_f(eA{!>!!vC8`;$IQeih4@d$OkBW%R?&!1LY(ML`$n$zpUm-qR%`i=yg?r`AIyC;`b|;MYb~4;{OGv6 z&)s!U-!PZejxsMRv+&aQ*-66_Ylc*3&mLP0S~Z&NLcUPEx>r+#@tS5cJwyXYFq-JOi@bfBtlT!%>tBRU`6gX2f#XbB8dOaZxlT>Uu(K^10+eiCbc8R~yM)eSDOIU{_0-UzKbZd9b@3qg%<>i!`C+d#C1O1e$;-m{!3S#+hxD&Yp zd94JKVQ=?K43c(|PU115N5Y}PN`Zr)%1dT{0ShA?7*?}fFC3j*-+YC51$7L!lcYZ4 zTV$caBF*48QHO-v+iFyh_E9kQOW7B8;q8iJ%DD!U^{%a#X+aaevikdxkEh>t{?xh9 zShpW=?5AmNj+i%uy@|Gud>ZC?ZRk^w4))w}(h7PH(VwxGTK4X+wG*d(beb;)l{naK zXcF`U%=hlVLSILi{|nd#UPDAf+|-2U(ajao?6s)tptuNqL% zx8!U=+s_N%IbSJ&-E#d`e%aN=V&h}jPalxWX+2q6*#lVnXei823#f^33FI{tlX5@G z^s(oX_NUgTl2Wo;w2fV=$d~LElyI_{hiPZAumKx{)6eHS%xK zaG~R-zw7Cx+kP|`tSTN*c~?2sToD*TLnI$!rzIUrd=Py`I3CtRo_D(E*S+c-@{>A~ zb%FOv)J^tU{-^Xi_fO=eJ+nT&p!uEsHT{#QWUsb+D8NmM)3oBYPfqWhER;o|w=KDq z=ksR2Z1ep3+uKFUU|(Pel(D~XZY?81jN!rFrg&vU&EV>&`ZCQ`+qdvY<_CF)q(9m$ zNgvp9VT=*Zz4n&vP5T>$sQMV<+*=7t=?{5%qBG(N!Y-T(lukI8Q({ml$JSb^PF8=Y zyP%q`n`TLI{R#cOL>xfYqfanB_8Oalm4ID)f;@*zK`XFo+CG+xTgxBC53(oF_K~oV z(evGTz;WHN)miR(@5%Nr56em4v4iyU%=gSij4iYpN+p(vxydtNUZQ*->|ISKbhDc` z>;5#pu*SO21{uf?`U&8GNfGtobSIa)Uuui1W)>g&F{8M!a$OT7ON3%*(*>*Le?{UH z2gN(tFTh^d=X>W)cAMOzyd-}-%=0DUWmG{UGtSXVD8KlvJNTcu>zo^%8LsQ@rQSjQ z>cHdR!r*(q(X-pR$MzK}9k#J;a;RJloEVNACL&@xj{t;5iDNZ`JCzR}J zY5C1qZ2V$v@+=~BWLy#HA}7W*#g03enb-QxqZgg)|m-$KU8K0M3-2o~{8goE%b1{FmkCr*sIdfvj`8??eyT_z(5#p8j*#hfIO#oTiNmr zIQ;@+l0601-7Ufpe22h&$6p3k)1JEJfa1xosc5*O*=E}Aya@B!k+2eCY>@X8x*8I} zdC?M|L-;~6qJ8PdIfF#2>v=;3+Z3eH*jEo4Ekz)@k?qCVAltYMilTjMkg zx2@%Sd-pQdB5sg3iLc>VxaT=OmYVT9Z3Z?1Sql56H^>SU zc&lBV>}6jg%$uW`3EVd}p1<7%3#6Kng{=Qi#DUpLNZvG#Iz z_$5EWc)|qe&+p+B+!un9e1P_lmBIa&dx5QBFerPGMMM+f0WnDO0N!#HvX<09>`nTB z(6Q0fLK==_9sZ&jw~Y z9_y1-ry^fTNs<6jcM^s26hf_U^Efbrcs_K3KVT*IlOAh)o`_|%re;2&Wzek z(Eb!On%SARSg@bhKtF;{ws%*db_QiWGlIQ~mBaW-{R_^mmhKmZ znT^vcACyii7uKz5KIGhhx6r!r6Qz41c#7V#$NU4-JHgxL8;!5Zj}&7+!xbA~Gwob7l4{4@mjQs0lD4c0|r(C%I7+z8BGg+_<*oT;06m-&r( znnh>b=4f<}_KQOI!%3jO*6?1O8~=gW6}gW%NYf!&E5x6|Q;CyE!$>ZC|6q6bMe7Vx zl_}HS;JZ)ErH$wO$1`$^SQ}^o@~?Y=JhU{(fqq31BhXn8HSut2xEnc78IMp5cig?- zoYXw4saQ2tGs94By8-vGX~-GMDS8a^I|D3X74(_I z2cM!!_9ft3ek~g%c>onrM`A+>vx8jUPR|3+0AB^@?=<3l@*N7DzKZdf@rqtVU4>jZul9|Mcen+}MGlhy^mRJSv*jyQz&nma-KK{{2koxhWj z4CB`Li@i+m6VC{b!7cTy^tKIDh8Gj-kfr2A4l*ZQjHpkL7#QiS*1LS+d&9r;1PwiXWX9Cj*IBYt-AM-dJhbD!-*wb|zm6rN- zjSLOWsIyT$qkgS>Vgso=?4$49>kWU^-!9EB6c^Jfm#b!)`+MGk-gdz?g{wgCHivfv zLtYa2mghV72RzsTv7^Px^y!_7+Z$8IM~&p2#ES5p;CDtsBe^y02+P3>itsX=+%K8N z&w_rr?A@9VHppv}>bj|V8!DYA!`-ll%x>JtT$FVhgZ;_1(|Ayw-KcBmt@@yyWI`QG zFCU&?gx^4DhIOF}BzliHJK1fvbmw=_xBKJ*`d-c{J~-8cKlwYjQq~#jblB&5`K#Qi zE~=~C_0+9*Gu^$N6g$sqF5^sf-%+39Zn_^@ZWt1w*8F7Hj|!iKzdlF+SdHqSohY0FVli+}#GS^U zBBqgFV|S?QXkwZHYJX;9E((XSo-;>SDz!$mh)hS+Ac8#{`T5YxJ zlVO!(iH{LF{r~pnL&OO3XM>-;+Z_YVNAyQ^rwjqh6ju+(@P;@bZUBok>QwSlbvlhBdIkR@~uXCd~-uFy&BTby+qCF>+(Fx2|P&^Dk?Nj|SFOP`}^ zQGdO5a(#|!xh~VfbQZcNdjhTrjv>|~rsoE`zO7*h)FYm3jdP52U-RX{d@I2U2nRa> zi^vI21ITFrn#o!u3`)%qU!TtHL8%PgbNpryYC5Ykb^T!9KF543=O-Fk-fi8KR?{LO z$|EweNT5%(?kLN*W~Taf(={#2G~QwMz6l_Le z%NA>*eU+2pneR&s@Pb`~`oNyR#sC^@4|4pC;3GNEINBoSeW>_7O*BH%Uuu<*#UuCy z%p$N`hkI(QkBnywTGMnuz<)=kv+ju|MGTItkSl~PST}xBGN}WP1nCwcw`W#W0SRAqOBu*q&H7!+qaf z4-C^8fi(Dj2!1=hEiO8E8W^7Ln6mWoI--7&ai?XAUFEz883en0LjxJ`yA=F7Kw#l; z#PEjDr(f?0T@V3fA7cc!NH|+MPrgciRyt9%lJ|^tkG`CmiwRK~atHRgg~8#l6WTqy zJsUhfJvY5lzb{Y*|Nk8R864otbN05bH5TX?%{Mer>S3y!#)QU)%6iQL({L9WN+va- zohY?f9V$Yr$R4tpJcT?8nE;jAo*}vHrL2AXNHWW;y`)xsB!p)(rc0N1U^Vv$ylUGwd1(tl1D}07!Jdnf4u=?&>Ha45AVatM%?jawa_ z29_>-k6VNr0`KpHw}pSL%agd@@Opd};SAv#q61!fgpVfje(4AfOGLCa#G42dfWl zH94LzB77X?K}Q%(R#28u8!3lS4AyJcz&NlsvfZye&Axx2-KY2w1VE)iJ-e=ct*6>0 zah|pxv}M||>^#>}SeF%uo|?(L%SvZ$W6Y(^rJO_`A$@+?4S{~1mCmz{L9Q%cSKJSB zfc~9xmp@Oy;d|Np>0{6j_>qAD?w>Y_*$5d6G6V^k&3IYE(J}}U? z3u-3xbv=Wa&{@|O&kVmNw2AN#8I1i&y-FX?I?eS9{uPgtMu$_s z)3ppU-Aj3m48@-fPV<-gwEiBU@%W3RztHZKeUwbB2suF*9PSg?;eG5HZ|`irtGlP( zpla9DrI~E$0QNu$(tvfQ+(oA#3B=hj{v#n%AjSXHyUe@6*8uYwBw)xb1dnVcR^v~> zyqUscU;+jlN%&dlzhGA2yl)=>I8&hRBgBoC!~3gnGbw@hA0SWvUZ8Kl;g9!s z^V*z8t(b8|vqgh8|6@3A^?|Q&d+67>bTT|Lyb@l|;%vC*@IO{~PAD7Z5y;fZf6d1z0y6dRijhm9Z1Jo{Id z!Z#+s4bBcc20iHQLI8uj5ctK$>F?>j=<7^p?QMWJ_c9?Ci6BRlOOSKOddPUBlDdK% zSfSRz8jzQhkcXT^9cUxDlC%ImzZ0AUdOXd=b!6ENfCU$6%eEc2wYN1|$J=^1a^157 zz44up`RER`9N~Z-4+~E8_w|u|hkZ5vH_+Z7!Z4B^>56W^Y?S@90R19^#ppo023F=c z#EX1H7g0t~I)VN7!M)4;XR}4!MLW)L)q28}>B|pn1bNKH4CS`&>K8*&Fca>3B0~5c)fI6|2TFpsx35iUahJLdXY1@MQlIzclbA&?|T& zxDwjyO?V49=(p$wv=8v9jv|~0zxEGujWfG6`UXYahx+&GgXWK*&p(NsNni)!)`Uie zB4C~dgU0>?uU7nT$Y@H4zJ>LI9naa!naI7u%j0|auYsZNH1i<6h!#U%LLW>!ght~{ zo_xza9a}rBInS8tfP@m*;{q+ryLRF7QnDw+i2bD!IF9(;0mk8MM#nBl!NGU}Zo9 z^7i8o5tP7wAa)P7%8V6yFH~Dy26o>(B#ttSHk+=Zd+2Kbq46C91SE{_XlUX8$F~`@ znFV^N#kC+_Cf8G=p#se$!6zYJL=c|konU>X%q9qY0qbtPOq;EZG}4{g&`-?Ct`=y7 zLp!Y_V*hB$uDX zl*2i0@osVYZ8L2*?H62D_??T?h=r&DY8<5%j;v_WTN6r)adbEsi8rT;BFGQ@uM9esD0w2#G#~sWLFjH7c_C{W_z#_aX*vUCY z9f+Ul!s(aRw<^C@Y%kte@l`n$?$zmx0^t^!T((BojnxU{z1NrI-sxmJ6P>f1x1DLO zPVQ*$uR4X>Ng@iJQOQc@L<0(|8*?0eJM}s?lRStB+V9)$*l$LmI>Sso+%K&Ou43=0 zz!Ko-2LA$XX;>LV{105wR;Mnxsj)t?Zd-kF(_X_JXa6t-b2B&dP7AIHy7Lk4-z*RP zJoPYUAr~V|a1Z3_56%dz^?&z`h3vR~mfPCA z`n#25%imPqXgI2$4fin*`78A^?Jh9YuK>gr=ym8(a2EXk1O5de8Qx1ETm3Yg!`(0o z_fo{h_~gXD<6cQO(#e5``a?DRV*WSNm!kzI%cRP;7H>#Qege9gZm?7zs(H~kP`}Q(FC2mGU^a696C4#)ikC`AlFlLxuN&(YrI6S*lmz@+JFQ&v z0VBmY(Kyhw)V#(LX&r8jw!XBqvn(>VHgz)0Zmw>^HO{I%RmHEwRnDqPtZm(xr8#2U z!cfHL%GdLXG&(?`%%FjgL zS5SELNenHc1@khqlGV&v!+$M&BKk+z#BImePHu~v?a%c@c^W)&AICq#FYzz(Wqa3q zjIKkDan`?$C!7CP7pRP?d^KK&nEKmByUM&LAYr zP5N4TLMB(#M9+zTksyyNkzeQBAzkxGEE^3bW1796uO)sQS&1FM_97X$bZ=kl@#fvi zDfM~vr&N!1BdrHqJ%QVGs5`>h5xB^PcyEQyk&>xTnVq;l`45C|L{CJch5LCWtZLdK zOh7h}Zjx$9Es<5xp!~)bdQq<5;_B0QE>iouj%UoT?{MgYRYd_ENd8HIu|@g=Ss4pAI6u&8)GsQ1W7Vq%05N6 zVacRJ!Bl6He!Y@aJGzG6cwN89<3{k@kaS|yl$iU`CdDx6H{K%JDf~#c(U`A(+o);S zuY9atrCVUivF&j4gSCWuw1Qek{e~_f^a)hDU^Uo2+U7e(xFnFF^&jBD?}x8~w*d_< z@Fd!Anz|a67zk#sEykVgqX#XaFs_`qAKeSQ-uJ=hJR4hw>*Jnn+OI)X^HfK*AIv$P z@Av}B0~V9V=1=9RI2+mJtTgsmP64-qcagV-W2LjnGlEuognp#zPW_kqy{bX5j^_s- zkx^C-AeF_k)#5LldFWOj-Wb_XS-!BuRl1{kor+<6?Rf8Vg>K;&5wDPTAsjM~JPm0g z=HP81Y2X^`8OOhbS*%j=`3Pf# zP=<)IcxuQLO=irYv?12|ep=CYlYQo7PMP?nH5!NZPr29N4NovB(^ zeX8!E>YP5?e%QZ{umy$N61E!Ih0FH0@XUATd4GiFA_M5txIINAnN0CLN*i-CmIjDG z6W>m`6RfaWG$r+~t2b2rtj?*IXoBVozO~2{CWYtX1gX2joosmZ$ePI&gvylKrK%VD z)%KKWhv3BBy~-iM=HKNqmT0pn6z3ZU(0{^;>9{bw*QV^?-_7mE!tV+9%dG z{vAjf^ElKIo+;F@0nZZZ=j>~aGhQ-XwB@)D2fpBjl7^r^D2HhAkoSIq;z#Sz->LIi z8o@qUZG=y{g8LUT#nr5tT1_eu7PTloRYt4}X&sI$_y|UT-%+IDzQY85)OfkUS~;si zR6Vibh^EZwx6knXiI1ifvrmXl$>&GPqY@&6@-oRK!7w(L`U=owEd!H-8H6_!Kj((H zLAFzRfL}~~6$)EZ)t9Pgm3oWEl+;wLt+!}W%%_|K{JU_^ke5^)vpndI4| zcI2+KC!C;gp7<~RNZOB3p>>#MZ^NC28=7M_5?;)?8r45-f4c|m7-=UIYT9DYVa?^L zUL~7~Z~yF2Ho0;_ZJqLW{Q$>+;5yP8OoiPb?*!)Eow(8XZ3H55I-v``61SI7OhyxQ6MGoGt`s#4mDGS^f|PD zDc#X}(qY11IAv&4h=j`}4x?OS?GkXMEh6ScUXG^3U5H;4*G6%QGazhj{!kM7I14KI zJ^IqRqMvq{`x!EsH#_oMi(5%+V@`9Hc~j~}ect%8S8i1Ppz2MgY`+{I3QzYNTs}vY zGs%PZ4TJs8?b+k231Ntzx`vs_L}+Ws4B|+*iJpKQ**xl4$|&>!xePnX=*62ON*4DK z^ke^|jihd&lQ=K<9r&#n7s3XkrRvGIwjZ~Blzs1B^;F+4c!1GC`YCEmWIMr4Vx(z$ z#l-xj@8{-q`e{-2wTr^z$kmkjv~BdW3?}m>J&A&lI)uqVPIw`*hp7@VBA>@3$KH#s zk368*8gW+kO0=6(N7+Ca9-QyL69|O$#9Xw1Hi*+(ayoWaOJaxLJM>Leh?2aLviFZx zpZ>7dx_fZ;&g_{s*?V9wL{U~5^Mw1nr}k^Uh~nI*{gtb)k0?+uBDD90f0ssJS{ zBp~Di)MVC7UTaaFbdus#biX)X92%RbPzpCOuaTQ@okPu`orLyS33CF}vHK4YUyjJE z2!UiXFGxRxFoF{M6-`D}tAf8ibbsCT4f*q@(mvX$J{6@qpDd0ST%-Sv>u390^{xEz zkI~g#5Z}mb|wDjp`6}B;u^-0h30W<2`Lj)~lNx zhFW_;@Es*bI3;#Z%U2zCb*btEe0T9b1l`Ed-fb3zG0F7Ay2v@$-QTs@MmLVs9Bn*V zx39KG-Pne8s)1UG0WmwwL1UwKRzsJH<3%e9-u+;g{#UcL>4pA{In&m{iT8cSD}WKU zHcHW=UlKL3Lwrfhu$a==g$c70lMX3=Ok!Q^nK* z???5!oVW8VeIuG`bYIy)r#6bn+93~;gzgu5i44JN?|9d6*Y`~?oG9Z4+HK!xwzo0 z^zzdO*YaA`_i_zJtJoy&SlZRleiNy-ykPFfsqZ#^I#e`Snd`RFoso=|gW9{>Riv?E zj_{9SyKtw2bHY7H2FhXP8csUkxW`G*sI0`YR)X}7t@yDzR%h>XrMp=9HL5^Vk*j&+ z?m+&R6B3V;Z;_1@-lpTi+f3tY;=iwYCwX<~O?1Ae^lej|LxhuK{b~IuD+o6C5&f6C zsIu3^b){44-Q@*9W z>hpHzmvDKqwwEghw-@O|$_hGd*_y94%gVZzd@kiy8S6J{emAvqKJ%yHcae;wC;~3{ z7kIVbI4XT5NG&fbrm^L#j#ImZyCrq$)ox14fygPGSOVR#zxjrWsa6@-ZZhE^EsD29 zJXt;`vP<-dD3dIOdz8qt-K;-TG$?;Tp7?9W;`vqI8;5B(>xUWk>LS!F>)w`@e~-); ze4!MNYnWjlhK%FMWkcl$#Cy3n=@HmN(lY$9;687Po8p~>KhL(ujp$PFTcpAGl~hgWjDumE(cE6w&79%xi))SbW1M70(A$v*1=m^&8=n>a_iF#0(4}P; zU*%9=U#tk&|Dd44Z{lmb->5rqrJj%WC6?2rF)Kd~*$0DUJl6|DlVXoZ*W9_bqA9s*Gk zzdz4!^ND@iz4e~%?o0M7#^R=1HHl@K;yph%mk+3wtDb5<>OUB7n!DSsJLh?i1g3_z zgjNNA3v>ze2t6WnrxdcO;!P3BQ70nf2fS}#FQ{P{Q8ueDTJJo+Xcp{V#S{~Tyo#gCksnc~=FINs$dYf4K zW7esjzM+MLDUchRfj=Br=elX8G@n#v)yLEiS5`D9+9N~#C_!$4z6S6xOyWhBTJzIUXf!X1Q_(McCGK2gX{hi{VuAskUR50=E=^QtA2A?A6 z#lOZG$@q-c;D-nHx$oJtEx=c0m~Wt(?pcE_d!R4jBhoDVXyWB*cDCtDZuePfN$WUjGK z@V*NVBX^^F*=_hw1KywZjZ`*1nz=p^JpIh>g4sJhKus11iBtiJAZRx9ke@(FcGvRpYt!Uc53dfidFuAy5t{iGVs%^A@(8Vga0BNxa&LM%>k6r z=s-O9HNN4O5U-PD$Q;rXLRxsfKj^M>K61WyAMNSlvoX~}J`%F(EZ^c79D>6ncNx0EM5g&{^C!z4J(SPAp=6VJ8urX7QH}tT~20Ujc zY$9_2=QcY)7lYSrvqxc-=^iP^*Iud|S>dgGU6-h#S$^TS$CFkf58!#5{0Z(Q_5~J; z(X4+8F~uC?VEcFfAhM9tPnsN68@)WTQF4hpm=*!Nv^_u`KEef5%;>~@FL(=8FsHE3 zQj?G@kXHpnS~?*o$kpUoWVnPQ!^xe2Nd6vUcH0`VnRu|DT1DewO`_>H=d-{#5{}-D*FjPv?;g=C zN$2&VKE!qRq}kHU{mg8e-?cbcO4>?4$&*VWGB$n*RtwHt1^x0o+83r+DCRK*TlL9xY8VZlKNc5r5_i+MHh){ zKboDyuflI}FH#~>65={a#EkZYWZxlYhGV63qDKt=uLHzC$&0b|lw8V6iW}R34TQ=S zEbKb^p4=4)!t;6u^4+<%mB#(r3RP=m4^>~S+SDE9=_|T|(VcB#y`c|Br{O9*m#kOx zw>3l5k2SaS+pI%8oUjN%sHKeS?7iHvyvtk}`yR~-`a9Xb$V&vj*aUPQYrYUC+bNHb zjT7Ox&uIwKJEU_zu>EN^n$}n=Tv4GQLT9w%=JL1kvpG`w1kxv8M+-sSShKgHb!AiC z-ew-?)iBIOmx1Ldrn1ljK#EDpO!s58zkuOAiRvYr3;89qy{E?T@eA+|WGZeu` zdpB6q^m*!RRgQ|SIjOI`SD9{_F^*_}6%AJnOva{)5n7IsO>I^UJ>)nC!je z>;xP}{ftI^rhd9!r@w8O57oiCnf97Sn9Y{6_B1!%|66DzE+5)`7BWC(R=^h$QV}(| zJKCPyi$sF<7lp2|YyT31D8z(e={)qhU9f89LfRZY8Xf6ucWZ z3Vs&>m91Wfvi!}?dFK3Pq2{7?fe8oe0Y!IGM^IM+BT)(N9JDLIe+522z(0osz{UZ7 zVA3{96LSG?yf8&{P_UBYqn;y-`X5JE86Gv(g(nlwxYbKbf#NQUySuylA`2|e0*gB= z4$Cf57I$A<7k8&nw~>xpl1V1{PCxjOr=2Eq?>*<9oO|Sb`MzF@-Bxt6XpJr6$>;B4 z3SHyao=JZu2NNEcOI0rrC;Qg3-SN=Y!ZE;&2J3@;{)kPWPBB+hf2rmI8c`d(H?m(G z#D4RgcCNQ|DA``}+J4uw6UMWF45(M@n(8vt|4_BSj_nd#0t?j-zJoXpu%j7Os_wA< ztJXmWl&6Icp(Fl<{yV{5oI=Fp6l9}vFWMLI@ls(wIU~!15!X9g{gRB5hxWhSV!*~( zVLg6V{*ARJP_i%a44o`r;r9ocy0Jo%uHA$qXsK*H>DapkVY4>8`P&maec z`dm9UjWb6s!u{>BJ@h7BMGF-lmrxQb8ZWBP674~ro8+iG5Y{;;!I@i}A}j@_@@L^= zf$rW@uKmt6u2k<~$Z*F;x{HBmnO*!x9uhss{p zSodwp=GYaOM|FO$_p&V>c+pll7QlX;&DadrEh}S=SXP-8=AX=G&HtJUOqGp=x?>uv zDu3hY-0ygZYy$=B`HT~YT0yW(QEcn6d= zwa;{PbFT~B*mE$9!Kt*@4y`}B4y`uYbFhlH& zU1Ab+6%23nle9lG3-O+c3&I30ne7d%-Yu2i>1&38aUGLpC(VvKqwh(?3qkLCd!Tr0 ziK=vyKP8uJ`3gBQs<~M;$;27ul|1iI{^p8%OS6beIt_?Qni9vNLfMOx<@VsdwUzXHJ;D@ zJg{S5i2CXQI$qaRdxg0V?*U8XdhlQGC-{F6tW1-z!#FA zsS)%w>Kd^d}f>_T7h^%yv5(68L%(=$-CqgQ0+Gjor5P+zo=BYMn=K3 z-E>j^K=lyDcUb7h$MNGLqr{fdQqVI&ektFF>jFgq#FhakKzbDUIXvDs+1bNZRD82I zV6Edk>}$y)Vhy>hPHLpoO6&^tlLNU)oQ*rq?}>aCc7r}DpqB0%)tq@w&mrH#_y$IO zVoy00o5K9iw~cYeKS->UbTsjwgqrc1SjiOBwNNc1%JNI?<-Uqs5b3!fHI+3;Td#7S0 zU|}m1CS?S_##{hy@(#K!sy_*pvYC7i>}x$`EvyyZ7yktNe>CdO-WJEj`8S$SRVB<6FpM?KM{E4Ox3&V~1U=~@c7v)1rLa}hO4Z8NS+QG5<67z?^bR*tH7BZIXV<(ga@=oDpsITV_yQ<_zaYZ}s z$qKpPxsQ?m5J;OU%<}r^#E3uG-F2|Yn$`JRmv1eztCuADJ`0<%QOp<(@VaQ5YH)RZ z=6BK!>vUSw2}YRrFv6+PSke-hq+b)PmSeicv=YmQ=h+m`luxnVLz+P&lY+} zWjGs?q~$^tt}S4i%=2sfYkgb213dHGXWZkwvjYv-w~?z+s~kd3VT-Byn)e2;xu0c? zv4Z9Tp+R0ocYq#cq6f$X<^@wsSD?BQ->@%$i}?`7_aHh^n9kSX&H-W|A6^u?;D6>` z=y+H1y{Jp^Pqri%>p#e)!Tqg}F34qAj}`Jjxhx-UQ*=~XuympVb(KEOs8uagHvmJT zKs(Ox!*tZrCFYRji>aNVIh0Ay2V70)pAt!aQg}_UZJ?ijo^PY~u;-h*o_n#YpL2*~ zxwTc%^868bhw~>EmpWc~*Mw*;9O+R>=2S~n z(uPtjFW37Y1*bq31(N6g;1f$>;eTk~LTEwJ+;{=d-nfHCkUFebE>trwBR z@p1;tqZ;I0F8kZNde|3PA6YlqrP7J+Re%6%3r3)Z67WT^5#&E~7Smc4uiDMrr#Ay) z=`wr_))bqKCs40duXMFd6)klv9Zb)4GgW%>XH-;pU|s%E)Ib4nF>6);QJruxvtG%)QqToNQg3SJz>szQtf`5B0@|GpIA&{%BL{-w2m{-QFi+9J8P+fA8JfuJ(D}r~tX`Zj1!~T-+ zWw<|I`4TOwvs?;YC=~%o@VCI)K=a^|P&f7he;o9EI^b_LrMs&<>S5ZeI*nGZFWKts?f)G9gC7&w%m2r239Sh*{zg95Tg7+PcilfbSeK25@eRj^ zgJm@6Dg9Kfq&h$c$Zq5w@-WqgS)yL98*Ta!t4_)+=PfrYaiIB6=!@GEHhTxVTDm^~ zL-lpIzXQ4n&i2*FBw_}dB5&gl1bVvva1_~k*~gSFcc=T&aD>Msh4KV`jJn4t)KApM zHI;S08)lk^#V(4ko6tF45$iSG)@NxJs%Fuv$QAfZbUDbaq8yQifxP}y6#u_o?H_5o z_>gZLj`yE+jj*36HWwNToCPO}Ij9J36-?%`cr$MbfARh2s#9unIGszqivSCvYlITL z(eH}#co*33KN+VP*XzbHd%%9`0Y&H>>Y!igE5#-x)kuj=y<09ZakwR@IgKxkP7c@h zHFPfl#@lngPobCKKVHQAWF8eDnkW|vQlO)2i*0cUVclu(?V96T9_q}$6)Py(pfu5g zve6HjZ%l1qp%sW+ETXg`t(2p&Hc%7v*|;9kgl?gjz*O^SZ=WzCl!#Ly!?1LXN zD_h{YRX!%AM+*U0a&cg)o3b}6(&tahJ5eyyI?`1UEX+}E99unD&wItW&f&9PbqsM% zb^i<1i)rDJd>J3y5#&Vemb6%rj-8|{>ubcsBpgf_8O!RuR2{%x z@6GY-I=(nEB*^LN9EJ> z(N)w9*GyxoksE+n^d!0%Pp7O*l4hLth&HM@q0VNWP%DXsmMbiqp=lk=2FFI`7>ly(J>rdI+Y>#joV4wwzSZ~1l zm+zE+NDyPaAcup{kGV@*LElTaQGFdw`xlYNawE`_1m$wTPTs3)U@44WkVGZ505k?a zoHtXriTzohr*g( z(!{pa*}&h9Ybbqze!UXS3&T&qM0?ZtPJ4vz3-b6YdLr6XLE}qRFO0VMQz@BgbJB$5 zOO`9ldnqNj%K4Lhp<}lHIN1iS3ZlTRK-D3zJ6Z*?{;qR72FY?+D z=P7b5DA`rmpwLn>!MPmb3u>m&Feav5Y?4LRV`>)8Fm5Np9mUkYg@CJDd`#0QhGp*AeW_V|=v=7-bD{dY7sk%=1v>_wb>>cVBIv-*-HC zl)t3NqwecJ#l$BJNvIsR&b&l_THTsXC;FlY@-N7#qOurY$`ojmjYmztm_8YL>pH1R zsloVNWR+A?tS?*>dPWoA@62*3)Mu^oHgMz?{gl5d_fGDad`t0N`*6>^P+u6wQYDY* zB%Cd9bt`IvDx0@O`g+mzBrE6 zB-VlcCGmT_BduF=W_|7cvBgK**XH>h+?^rN)FduB^+nqFR58AVK?!o&7+KAYfPMCs zT5lR&E~WC5>b`12D=Ol{%nvcmx556Z;=~RO2nd(hj-`! zp{ZdUPY?Aa`=N>M^!U+UwK>Q236{%&5H%-3`GLposEI{ zO(J7;vtz0!KTaEw{vh3(b|pd5K0(%omN*&PVp|;-8~jCTK$YlCv7_Q&#njTxMURB~ z*h;e%-*Ub@$Q+bAqhzqV2)@5Hong2WJ2*j#8)`*gyH82kkYG$;C zZ!v!XI=^hXq*+TU(8KU~RYfmF4Y<};Jb;>W=+_gI!w+0-i`_ZDE_Nti{pv6HR#VzWDm_p!(wy1<-BQwzPG1ehoC=*V(zZlr~*0kpKXnhx5(Gy&BMx)Eu@x+yA%)i^vv z1;RdyZ-+U zx?BbC&0N*G4Es&1O@AB8YxmRFKu=!_soaO)b6*|LVpk`Z=>Ee$m~9FR^Bn1gc%*5< zA+~1l7vEOTGxrqFC#Zl83dza{oPc)_iHd#U3eMER?b-XXI_G6tll}S8GzN{?pPE%= zMa`dUx+*tM-k^p03D>K*vRjKx&`pNWNiT|ko5G%U37y7en zXaAJ9qWJI9^}hV@Nw8;M(f7n8stGleScO74FX&Gr;+49@bUJ=}QcmKh*sX>G%&-4{ z9~q(o*bhk=CcZ-@!EZ5x6YeGE$8R!sRM%GCW=FZ(TdNdyFIZdD%l^d^&tlScU<{MV z&45VUPkIm7vU#pi_BwzRQLaSmSmV9Mbw^%MC7Kf5Yi(;)OX4LkknLh$1YZS`LRH~? zB~j;$e8Q=e_>@<1m%(2C;ZL&dD|k{+)ApOsBi^U_oBAdm1l*Qi6G+2kY-gCW|Hylm zRWT>6Nau2boH~=M)JlDG;}X+D(@W!M{cd%boCHhd!WU$m+{7GmgR1{yrJl7}@+(TR^UlDm|jBMnK{f}M$I+{BgIPIRn!Hry&VEMnl z+Cwq%|9wLHcytg*j;fkmfM;c=uS4f zR9EoiNA>TwvdfiJfqgeuGdfO_vMtq{RNnFi?9SP6AMat;VmIQqh6_MWIm#1QE^Z*V zP@R}Bs!Qq_>JYP*x(ryMb)a7i_N`)*Vl`Yb8CYS&JmI6TQ!MKf_92td0NM{-iX4_` zLCxZUo}RwWt&Tm8FV5+{FuPPfN(ic%+LM~9ObWzvDDekBov#_0DqIs|;eJ^;iY!sT zGDc&UC4Nh)mXw&VF!s4wGR)I^|p~{DNzhL0NHJy^gK z{}_52nC-9Q?;kLRC&K%sIZ;)8T))>?#gM4xP<^DWcf0Lq(V*guj%I<^(N$Ewp(L(+ z^4@YMlJgU+F@ufgH0kt3*q3)B*}+Wr8+!-qdD|EFF#b=%Z(36BU4@fr4HK<^?-=j` zZpinf&qF_De5G=_mW18s!b@OX=OJs65*X)4i2|;g^Wn$--JS;S_MW5uU)VByj64MW zf8G2jd4yc%KZ)`K`8Zr(Dz+n=VO&xLR|ZyV=#hs8;IpTfSq z!}PP$dU=8kPp&rPm z5_Gk8<>&aXINlZ>$=#Y8F4Q>7!8q=dmDLfoi|IygLW^LXe9)0top?trWh)WeAtO^~Yj?sLB`r`!UTz;o} zL4oOO%-i0tuY8jq}xf~b2&rIn$(<{)LlEj~4|6u3x`$0b*D|cg8py#9prDJZZV$>94!Ouq1LjzoH zt5kf{+Q7NmKMm~oIe5cz>WIdn{y}#DIp~C*Y~9e!;3+uul>@tWjWFxXai3BG6?&z; zj<+(G+3AG`-?x0MzCYw)qnDq)@wU$V5^}G>5L4IOSFKQ7_YEtW{(aKtDWAt>9>`r< zVsbSLjEC=0l1DZBjJwS?V~NHC``ReXTHS_cv$) zHA>xB-^0|{GRHE_bV=Kf)JnsH)to)7&XTy&`+k#{hObj!G`uncwpfhXQpHda^ff<# z*{bFq`Y|_0VJ#2-ki^U~7FeEHSYx`j4DV(M+s^X|SfQ4eV5NtAN!%RqP?QxM5LH_f z$_f9&?-9>R2Voz5Q-qaciT1!OT0@(mZcI1EBS?FY<4nbE#ZtJEfb@PJJx3e|RG)6J zZl(OGU>Dyf&pOXMPhZa`&jSB`Zjo}ec1OHFCA-`m%VwfoXmCl#>?J=s4$y9~06^WfPPv&Q+PkI^@t2aXmaCjN z+|(EHwW*N^_Y~HBnsU8jKTmkB6{0_8e$9irZ@JV9dVd9D zan`}^oRCcz0_(CxsxD%Yd``>Hio6FsD?vw6l%}b{V$L)-FfGt+X3pSc@!jt-2bkN(>DgGTcf9~f;BDsopW zt>rjdig=akBJk-U4+ecM+hCvWM`oZOf$4FJ zW~}j#nEP?>Vr!as(4%DmXUX#)8$K?2yZT-9%kY94zM9A@-Jt|u3X+1wg;brzdyXNQ zU0?jT7kj(={nf8J=AI7G>QChsRS(s9R(oUB`l<6R%iuZd!iDZSjuZB(?h7n{X__<3 zpQ(PZwz+0z#p1YUW&KNRhJS-kAMl2NR}IEF9D7RN*X5aO#=bWf!hUM+y_dJ((}LG6 zUwwEx{Y&qh*(FEal_4)Wg}MhBh`pN0STLMs8~Nk6k9qGqe2L3#=6DubsrXK>H?)nN zm#{IRcI zLBFH21_>(_aH`u*JObqHQGiDJ8syqhY#m%4TpoVK&>}uH@l5O{y$t*B3RFg&^H20;_;Lcr;QgFL4>X>S zpOc)IG(4`O;b(e)k`ov6o7jut-QgzC8iTjK&e2sqgeaCZ0YH`|KSz*4MwZnk~Xh%E zl9{l`Y*m+FZKH3(WBm8MzxnU7Ns4(4ZaI@YEiF{uko3Y(2|viqbknx?#oeqMN{{)} z{4``HGthJ=;afR>a+O#oQ(Le(x@CX=^zVmNUvA}$w?7O%lEZXEQ|-jW@~6_pI^N__-?@0g9U$j%ga^NM9i7Q7fG4%PSY>ydzd%6llu}p;aB>}03X7I z5Ta%<<|w}Ztx5F=fJU31p53AT}#F=>zIMmDYS2NL->Gq zl{3R}-_goB#J$iv4dUJ=_A9J!55lhAVYGpz{@0*`ebY7ct+Ye&ih{F+6~S-X8J;8^ z!5u2MzKeN|CCAi2cap3qr3Y7)I*X?l{bb$e>KXnd=Td9*&0{XcA4s?v_tliHo~}&b zXphCd!Ft{C#n(u9j@?ze4Lb8CQ+2~h%?i2{^d1#fggZkIN=D!`yDeG?Q!{DWhtNC5 zsA=jk*oh|5lR_Us5(Y-}r1}?)Nqroc2f%(tZ$$Sfrs74+e%&tP0@GgO5q&G|8P#1% zj}KNnj3`63Af8{tb=lJ=Xohteg}-uQ_Vzywr#l3-Nj!}iOSsxX^(L#ePw*X{ix@puCdolAx%|sx?*3Xe7Is@mv4w~sDDmyG~kR} zgZFYnWC-#I=6MkC=eHrXu-oKU)mPnPn8|m2S9osIKuSsLAGS}we0rUEzVNH(y|5K8 zRISoxYpyXlpdT50^S}_-PTSVv_eB{cVaE*LK>jK+o1PCH??X%xI0sHAv*A59N`9}% zKr@Ly=?SX(>h5Zj`n77MO0BBRyrD!QjA@`obTy3QE3n7}KrRSmw#)LS35sTL)&e{& zIS{QXHi&HE{${ta^|)g02lq4EHyH8sE8T4^EG{V7>geIsvge}d=xu_g{KRSWqV$Z5 z_{X~`=O(Aln-qQ&{fHYh(~VUvPV-m8Ms+IqEqj68s|=R_h4VlKLu2zeO9cyRu4!zm z+s5oh|A^f5rrEfHa|Qjazk1e#eJc;FB&T3Louyh*T^L^r`6sb2aAgm5VWn}UPUm>f zdS7f{XOLva2prOu@f#;6R847-+9i2O%v7-d+x_9JxY=&L%SGLZ%)45B56RGyR>WRufVt1SNs*bqH1fn zZ1z|%i^14YgMt0-$Bz!K@n3-M`vurY-T3%Hsb|u@ru2{dRpV3q6RhNTP%u1qO5Ug9 zLU--RTl|8Kj(HthWRBC`!8%96o(m<_bIsqYelvcj^5)qyq2A^!HCS^~Q^1^sjKvEW z?<(0WB?^t%>h3tR(OcRgLl^eFLJ z>WcFF%B_q)W;&tiK|17`>@(Mb;%Rx^^9B}M+$HdwE#Nsd0Qr0tKkzBxQ{Hs@iM(x@ z=e}^68w6QnMP*2t0)f$ zEkod%B%h4F6~6Pexyq~%{+DgWcNX@8UUg7*z~15Kh;zgPT#G4`l@w|j1^NDsv_U6h zU$JZ0GVDI~3g1g!qNk}Us#mHG(_x|t_6ZrODC@sp3X#ZH@ce1uk8b26VK!(FV8I5V zG`2V2RrpUl8AYWZ(fH^};TuPWMtb*@p11y0>@U7*Ywz0cKg*sHT1#8tIiQ;V{}+%m z<+t$jUd3gkKcE+WB8M?I)%&z3bysu`waJ=hsz2!^)OT_axt^$xuR&9kZ;-lZe_{=@ zNlP0T!zArjdLOJ~zBo$YBPGBzx00{SeF^7<+J#O9PX<(e!rRil##9{SfuyIYld?E4*CU4QxG!H=j${K=eGcT^vyd*i5LNn}l^z}Mbe*>@U{ zsP-U5lwbRY5h$y5DkfdIjX&!9v$TzkwXSkB@fbp3(6htn4SWt!ji?DcrB3*s9@2gaTBmO){X^>ON<_+aT4_dPTox<%~@if(VH9k)EGMRI=pDx;q~BrNc(EMAy9 zCZ~JBV8@r>RoP0t)2`R!TA6$&r?A^RvmHxqckF+;;sPdqi&UU|h4&+mk)=dK=p`4W zZ;@l+^gt4HZ%+ywXDdYCDmRdWnSJ1)k44j>`@->%9q!?};Talq@*as)P6qojTgVNq z^p1C?IKJD*m5y+a^oPRdz}}8VhXIz>UCgf3B3_VFTcjVlAJ|ix)0-Hd>b54N>tJNe zyUo*0^YxNy2a&JXBvfGQ1X~221A$p~Aq_Vo}UZCOS;d5FsPyvrs{){H$ zxqvs3M^yvHMN}CS`>|QRdahgct=8Qof0rz?HnzVgtp?R$$?W>bEwNj)iOBN3*;=6i z!A7AUtV{T;u;NPqw=`Y9L*HIERy~6Hj-HW^inE3J;zju`=3%B92E=+3uaxVbaxXb1 z;fz_-oFW#>J$XJ@E6_DqldB;m;J>S^x~2w7cY>~zJ zhtjz^5uZ>qdNSHGT36g1`N|DuXNJdzhp`j*DPnb~>@-tps-Sw1W{i3UBa_?kWvGZ~ zpt{AU$U>H*AMg!SO;wWSg{DNkh$$p4D;G(BN4l`9gZ=z(yiL7@-jKgnczWb_=`d0o zwJYP1$XWy(%Ah z2OX-IBQ=qx$r}+Pwx0-7nCcjy#+=s7QRmSK#6H9rZNXm;XN4|=-CV4=R^FgohWW5l zXeOK~N&a@o9*72_p}+aT(p9t%ou@UM7sbs;Y?jnJVY_9GwhJ*pGINW9F9UytbbQn3 zG^8hRZhcZ!(*C3ysf|(f!uLljgu1$hTFpfp3ab`Jtz_v&R~--HZS8I4ZQ&i_-Q_Lv zHt}unz40mimHe&z!~MJb%D|mKR&aj!5_e0uB|pN>F@(Va%~52^os@><0tqi-wi-*c z2DOH{Mxj(1HI1&To~M6cF(k%7O!;EDQSluN|B$ZetdPaisPut-fMcd}g-7tW3E$!} z`IdlBN(W}SefE)%H>Qe46?4|HrHwsJ0?pZq!hcd0l8#LUoH+?AMBgi0E0-ycpeykY zWFuyj+N5o$yRJK^3j>GZTs5W2q1(}u=|N1kYOhAtuGZbu&QRZ@%$P6&%4fjC>Z| z;Qt;*-YGj_IwA*f$WG8a)O$_FpQC1U1)77MAi7f%=r6#qHlBV%{UDnWM^KI8qi~L0 z9Q64I2j+&3LA_@)c^hIui_jSC7&=YaUePpqo*x=c4BiQx1*XV{@VjtqE9vTH&^Hixc=KS#F0o8&!4 z)6F#l^y>^W^o-^-`4w;s=KCf(9zgE8eV(OYPRSEzY_PM?4AJ2oh)-B$L?Lz$v);$f zrjFhAdNf zzUDf`ROyJgLFguY6*9&DqHd@wXZejGmnX;Gs;DrxZ}z#YOW7ZDrxi#=ovc0VElRt% zmV2K0vID<|)`tnUB3q74Wt*@g*_G@$R?U6q`b9R0ljNPstM~%yDl<#{R^!*&bSL$R zhRTLV`jGCawvA@Iss-&Q-eWD%eM%F$5}QKAQQ1^)Y9g@^jZwT7kw_jpBYY;bKh!;Z znf;63Bg97!MMGjMLEv75>x8BSrw6%Ub|{tgb2|i`oQx)rt(kxdQQv{eodl939^(nb z38DshiX2I8q+6&eYW@WF@cEjK%xyeF@d}Wj8;3pyb_Dtd#n2?Eqs62b@@=_ebOd+M z*TAv3@OaMTtT$Psa(flNwYGB(@;U;c@I>AsybwkRZ~5`;U%_PmUfA8e{ii~;B30$D z=x&k(J~)eNIN;oGr^-`xskanIA5tCG+|}(g+=usXIpZ(-LE2I3Ak&Vi4t4YGnalJt z>Iv}(`&ao<(NK0q_0fBxIjWK7%DGUBc>x)(ASAVr4Da0I!QH{xq5SY_u3F@=@JqCv zJP0|7CKDT}N6Z%WRSl^fqj{_vL9ZZ=qm7ZJ@;a%GR3Ff&#z-TjUQ#0|6zvmLiFjl% zJ0^J4ci6KZ`VA_2>`>Rb&Rf@e(4+MnbI){-buVx~cdz$^yfp*y;qm-Vv6gI8+(z<| zE64`q5TaFPLA~M&KqUUc0NAF!y=j~IsQH|!f^naIr4H4(wGQnGtx-Eb^HP0VT}P9t zX{3Fo8KJJhydd-O66`zH5ub|h!DcDb<=(;#Ruei97#45`e1L1wlikdH;WdIRECIXU zL98QQ72~BYP=nV4r^zg`fc%Zrk@hB%I0YEF0E6TgwT;S0he(t8 z?2sb3CEyIq1(f-FfRFwmI486-EI<`#@5u2;!-$tF4tESr^X0pHxH`Kk0qg5-_AKCm z*TU}NX5u+M3m*%(9}n^UxC&VDPM|l@o7f&gL9bU8YFJ&K;kId|r6gub?2edf=Ars) zDh26L`s5Tqi0mBw8nuI68?GFPg@G0BAYeWWLh}?|q{~7qKQa7oV3aQ(x=*_Jh6I1+ zu8SKKBTyy27PsJU&^Jgsc_Se5HRc8Wp;!s21Mj&Fnl@TiJ%X7)W@GiyiOL$vs>(`A zvyxUCl~t7KN*gi=S)!OH50WZG9}20FHr$}_h2Y*mtw7(vg+MSM1iXQU!CE0M+=lNd zbQU)W7+)*Id#Ad(m0BIC&YkWbzSE(vd}C>oau%@|&dycTlzO`A9Al?x+Da}X67km9 zVl0lxqiU%u=orHhLrcSNdS1us8t6@WUi*)FByGjx(RD~?#D`dw7tn^-ZR{jol@zF- zncWb-Met2>KYl=9p!=cYf-S)ov5hQk?HLx7_zLn7_Pja#ab7P8LmKfe;ol#5&R;#cI?{%p$d9F}4ZeqLqXP zyv%0^wWNB;6znn?V%(a_dH|Wz+tpt3F49sM5_;{`yZgKLx$pZHhMSA6l}a}km9ofLCyls-yhHc#BrP1rVxjp%%=<4;v7km_aIH6pKv|C z0-ud-R5n(K(Z(VL2zVKMKF6^Jc7Awt_*3{GJBQN%X5kJ#%4bEIi0`A-`z60KR~AoeJ4NS}pzk(d0C$gjdV@kR79V4sdtjD}iz z71TGML1alI^bPOyopyCEednm?eCMv@pBZY*^@zL`&}da@fIJGS<}N@K2DtVjAHoU2 z5q=vWxqb^iVe1ITq)SK^`Y*-5~ zB3i(yxT+{p>_Dz7byzk0AYL7I$0KBh+yJl~u}DMS$<^j{kv+ooXfwrq)>k@7*0Y>~Q(x((Bn+>EZ1i-b}98g3J01`fzjXNk`xEfh^1 z1IDpO_;2Wc^3lkhV6rFVsALb>d1r55IFuY27~LelQ`AIuC`#bWd683v0zS$M82#Q~ zzz34UQSNlaBDRm#mA*;6)7v!iX0E7%#bJ+oCkM%zrcRQpubj68#k z6HjqN*k#;rLMu62`3$>g?k+xdyYG^od`r8uW}2D#4a5VuK`w*4Q0y%c;wpS&0KLmf5F z45YV{o$#QtMA1|(jf#Nq`&|4H{ZHD*tL_WoqBQ4->szeNtW!zqKbP6x^FY})A`u%~h zC^SNz;v%rw_~`=j5!N12N!tYrbP#{%uSA@}*eE7nR}592M}NmoVa>5QP)puTxm4L4 zZH1+Q-EV`B#;U-1+pTB~h^%vEvwT%rD*Y{`$v*jtf<`hF)8r%3e}#ld41a>#%uR%> zo`yxkl|%1?)qum8b@Y={GV0#s#^QC{Jy^>%0Rj~^FOb$`+(KXg@)_fu>K>KpB z_iJe@J7xdo=;ZeJhjBU4b;|B|FTAC4hqxrP-QCxIs^oTw&oHvi!J3` zRQM3a@SIlxl#R%kraxm7H=tq5{M=}4{)h|)4;7K?DLNx=cxUt-bWBba?}GLCm;Egq6K)Tu>#y=C z;;fo6Fh+&09hE4L4k=vgtZ$3$*4C~U!S>NHctn+|?`#;Vd&msKxT2$i@=#&`8VO9kEp&e4hX2$0*=CARz=bK zqQ>^ip2^|o0u6G5IEI`r#X&q{7V;CCiU)`xl!IzVwFC^yD)s@$} zRT5nl_UJXHy$WZ3f_Y5kjJ~azM_$GF7}YnLr2F*O|q`DeuI9uuA}B5y&CU?yoeqU1!}PxJ-ehavSxEpq&X zq-ROX5*<))_DpvT^0SM=XIxXPb&9uJgRaMrr+i0Zn(Dfl+HR`eM5El}K z?&+V;E`+%FT%<123FO!TX@iZSPN*yDdl@U5{6?eky#6L|spnAR@Kzu%k7Sd61v&qu zh*gxK$3cz?(ZRa^4DW4Cu%G+6jW6s8h;(nW|IYohaGJf77iE8k=g&lj{vW3~54u0w z!uJdWOZE`tv_k2?577-Y8IXzlEACkGe`#0JRaJ^AjZJ+QSKTmNHJz$K^`#py8)*$W z3*yds(KXSHa9>1WK-R)@5XCB3!?L{EW{&qKy0?}#a~68KhOS3WNJHWG3#IqsTj9LW zUThZq1@7yl{2kv+J2kHjznW)Svdr~N@dlHwzviCG%%oEybV2gSdARSX{ObRF%T*ME zq2f=WxGnV%uX25YuRKu6XJ2b2tdvz}{a&)JWKGF%>ty?7XSTO}s5#$V>;bW+vbf)N zi2Om_iIf`M4#cE%!9=wF`y#*Pt&4Ax>D9mzt@_0c`1>edPX}{~==>%05(Em=6x1qcKT(8sf(zDAu z)n7Z*pL0Y!!p%rucD?_OYouemU34sWzw_S+&)^?LG=eo!80jvgi(R7I;rq@YM**>Y zH~9;7kbDgLg^K8dt31bCZ9UV2OT==-pSr5CqY~#Ou8CczH-kRr@qYs&LkcHHE=Om= z`0oPlb!WJagXqj*>?&THNGBfRsdziA9C`}$X^13=>0%C?_1cKz#jc`SEE4qMX)z;u zQThY!^T?Cr6;ki$S0O%9lN%gXhV}urVjnij-xdpCd?ZxL50XwtcSU2QvOI#IC`R5Y zAEGC)p3qsP$E#p~S*TovG=*-)YKkM$DDgjjIomosF`UDegSW^7h;DZkXY$uVSA076 z*wUsBzhl2E&*u%F7dUv{VQIc_hTRwJ8)zBSumzFp(nI7dx);mGa5&|r%5I;(FL4UeR9fiRx z74W+ME}d(?VjE*0R65WN>40#Qp9cE26v;-~gFI$R4)MI;i)2Q22xUB1YtcR=AepP5 zstoi^kpEyY80iV~bYoZOs+xP+o!XX~ajI$b7%~Ch0CuCkBt%Q(*XTuxQ;XUY8ZVWF z)QDUUWP1`k@jgCyhu%}SzqiWetclC zduD0*(wQ#W-<9hsyYLv*cgAU0)3CvSNsC{RSH=3b;-rl5wbt#kj8SA z{6}1J=`&ZU|9ZrPozhf_8JILE<$Squ32M_L*rz>M+3RybuGgQ+-IeX+WnHtFO9|7G zn8fojar)KNOe9IH#1G-JxE=gOK9%3V$=o!^LkRe1)jQo0eMAG*y$CMs2tD#m@+7%$ zxE8p7csqu62sz3N^lYtK-&t#;`vE7D34HVB++D6*WCn~|iKnUh8Qg%W^Tu=$@(_4n zvo$5JLr&F#U5=5U51ptN>Nw3g`Y_r{
@OYDuU|CQ{pt#LN>m*HO|6HAy3^O&mu`Lt&B^D$U#~lEChD>!e>OR=vvC&atN6-Tv ztxAniolx&mEu@-(eC~*j$m~dQd?Fac4MXrSR1$O%W@c!oQ<5LIrg~Ql zv%?$Mq{yCVf8-nHArCRMrh@K}-ld5Anb$nR6| zNvTwvRMfSkjk7Jt?=(}zv^sWKjG(K4*XEv=jwo1=y)^q?{wG_KuNuhDj5nl4)90wp zz@xf5+JIyI+uYYmqmH-EW)LBP_bjzjx7EBiW~T)=K2bj*o+*Y3Md2@j$NnzCNAUeL z-O%_){LEw~`BK~^!#nzjvO;t=Ux&>PmF>M4uAzu3S5S?$_YA|0{q#@Njpz?VF@775 zU|p1|=#|hzS9R;f!q!FSY+0U~@Vr>!0x)>=qDG>ahz1VW=jAuZiv6)T=X|lsvj9FH zi}lABfc~@;Tkt#B5$rGA>_{0Of{_)Z@YY2-Uiq9$Yit}@r5d6PO9!Q9-Fke`j4TY3wf+tK);1G3xWGLh1(S@dm-d z)eNKIscb3l2RS}bmU*>0suGn)#KPz8gq(18KoM9Fe9hAE`6cB>6nsuV%D>7lVt0n? zvp@0O;P*Z>n>?y&pzCFvX})D{Y^<$5s{AKh#qlWjdFJ8F_PHJGeM6#RE|aCNZoXrF zVXUs*L^y?1kFj9<*Yoe!eaOztD&7NirylB*=7n~@roU=FFvo)b%NqjqU4-L<1td2(I&h{d*y-&s_MqEBsttQ59j8weSYyA5=Qsliop( zV0NlDsB5Ty1B}U5;^M&W(h((8agnu?`#boJO{nqOcZM3qS-KDOP2?+^?7CIJXHuVD zzCZJEL*|8opz9-l6|1JHZtQ2W=|8Fl;xD8&{FuO56BwO7@R83F!^ zm%ZqzXy=NnSkE|D!oFzBoHC}z^-Aavzr?aimq)7Pt)WWJD}~*%3o}!`n{%oc*K;3* zbx9-E&^?(nx_}sp;UJ$hsR_hT4|4_V8Ey*L-Ar^rnH*7dw}H` zWDR*s-Cl1qEHpgP-lg{|%SRUZQXNx^D-?Asp-aL zwFx95?_wAEvYl7$ob`s)Y?n)uy^n(B`Ay``)^pb)sw*ccGfwiDtYbHTgR8w<9(^-9~`WGq>-xU_%(0=ZtZF5?do0cY3qLJEbgeotIS~B zj}caXs~XfO@%#o?9QtA`bY|gx6P!^}xhg&jHW4}bvZ~-b392qNi^yYo)tTZ@_#b2j zfqdvOi{Kw}hxtKw$NLA2FY0-DnA}(GW=^F}@uBXl zA=iD4L(_Z@LSA^BuETr-W-iVn*=ER#)Hvn|dxM+66GC>OIKLd%nb*8QZ^oDO3Bhru z$FVu0N3Lf1*5x^s)#qJ6rUxG;Hu_%oQ~QrMzO+dwmHEfq>g=0kWzMKPaXBYsndi)9 zeo5Q+z0cb#&tE-%^Y-hHaPWUhdCo>I$+0KLwJ0Ie z{b%UZS>dw#hSz}O!6;WePt#A0OXB0Su8I2+2K}Cx{428}_OY^TTcMSsvCxQZMD$T} zXYTp4_xsP!YriD?Jd=`$&rd|IQ33PqglY!zXWU71WE>1sz~7bR&w7@Hy^p9CVIZom z!LekN`CjXO@|2F@}iNyNgXU%lp44oKJDy*UFAyGFt{r7-RCtsa>ZoF*t zv2Q|Otji(JU`QHPgQKip-E9wPryLNLzO(r4KH zY#sWGRbL$+7?Tzb*V~-nsZ|e5RGgSkKZ#F30vkbLJs(WBoBAMQmVQ%N7i2Qpfgd|9 z`4t?hSe!3BKppd4jofWq=Y<;>M{t^!cZ*r2L}k5MmrfAod4~C_hZ-TK`<+k^uOBsT zYNNH2dN->8{QTFus{4M09|SWr)mPW~i1ewKGV`V0O>UXGFJpc%Ngk*bM}|gVKK`(8 zn#G~B`IJ=rSLC-9UuXaL@;h7Fn?NUJy-wo1n#%|uJ&n1;gfrKvnt0#t_B?wmkwUJcbFkI8Kd5s@^KXHh zMzb6L|NY-vm#n+?LGmJ9f*s3FW$scX$R$K4asVx{GCx*G;TF=GRY5B*O%IIo(*Y?+ z{-1vw#jNEyXGvF-%j=vhoZucaN2pwI#hd|F;i|wq|G+>cX$!{L1?b!3Lpp>rS!P5m z4ejENW0zU0l|R8M!BNs*zNog?ysMcVfO!jzrMnhmuba5 zV|TGClSyx;3?iTXkJ(DUt?p5R=#Mr^o+>{=^w?V+t2NSN^xIl(wWs`2>>>`4c=ej` zj#$9d?r&Gfn2I6d0RNloJ#IS;t&ddj&8bopJ((A0T*5B9DE zART{sqK%mCLOeJDrt7BA6Z7Dt5vu3cCm2JlJ_JRT0HdS`^OgQU$wV`&yxtad)w|+D zDW{sL@e_EcWPHTEqKnaBHvWfLo)LPJ|Lr+ub(#mLDz-9eY;*-m(BhL zs(9*7xv|twEG%9V8_9RnT&5eHuu3}SVLu<_BSS+&Gkl+Y-{DWu))yU;7ycamd7LBM zFtcDBw*@H5V|1poV3fM@dWw5eoE;H2Ny<3?!qjI;U6O7k$E1DBTqCwpzv29vPz~t6 z{hIFSqok`D3zC=p?jGMfKIZq; zLE&1NAtqtolXT68MnbJtZpXM&j1w7U^nWozmPm zV%@{%e6p|npZ{4LNNHZ&pL9EDD zZgKxm9?R0&sJ!}Joe9kRwH-z|nbvGi_9*Vd5-mqCKDBaUj^8Q2+b0jmNEYvE)$sib z5q|3=a*x)M?eCJ|N?(=k_2-gOwDI-CZ3jy zsz23%Y8&}XpecC%WfM~pccm^1l)?IyL|;PBj!d>LH<-`iIOH7Xs^aSGT<5szsOK_0 zl|mg6hqFA-vMXYD*gzmRz1(Y^kA)C^IlG#v$1G$lCM$X&Z^HNSnuE1C#V>b)6KGKD zW2`e5S#5D$mf^~r#dW%5p#rVnLNCd-MzYnJ%FgX|#JVqgdEXpg6|4hQJ!_pau@1DR zhC$I3#+>1bI*!51?H}h0VK%>l`@sJEzfavey|U~<M2yK z+-XT^L(|u!KS|%8vCKbIJgf`?XA=b)v?F(#MdhPS425< zlH3&9%A4k_Bpl|3bLIIpLJsF5*HbslyuFJ;#)k~_)_1pXIJlg&1NZj{&Z!{IX9s-E zhU5QV5yQwD`291p3VO`GlfBABb&2^(G5ILK!!-YCp zxi-6#-1Cr+ws-OHz}(?1?wsRDhSE5nqoi}aD*>KYOFT>6@0{m^(NGoAxUY3A&FE@O z0Y)~!`iplb*k$dK)<0P3zv*N3B(1DA3SA|NsFgJ5|NFp-#AD)wUCjDr3^7Ebi&cUM zlD*;6)0yqhUFDlO4myJ@V&#n2@RoWpv z!JsW0zpqLDpy1-gy%bnyE%5SI@L_B(e9i#G=M(UH7%HWo*hh$n4js58`MPz=qH zYDw>eeScT;b@bK7ChG~4OFl}FNYakjWaTBcmo_8Knq^tJvw-;(VZ`=s^TTxR;Lwe}BkEVG|;3I7S9LJhtp zH-H@me%U@;=d;!j=p0H=PuOLS;qF5ox96+#HGdT7#0mR``B2}X4v|-j2ZLF~uhM38 z^~-O^5ufq--EfbiHA1n)n!%vI6MBB03XBm)$hFihT8wV$V+|ih$yuv|JqNztI9ik7 zw~+{3@Hi92J?Fk~L%5^tdqxBMw;0u$JOb490MQyz$R{c%eVMumCSe`@pIl6zV2ZMDnOAf->LO9mw#|Rc8^$f8B$y5P?KyB#>O@aq>VnV5bN_JF zx%ym99KD(NF~<5_w5noMGP299UyL5+l*L^ zQlo$@)mJDu3dd<>^mY0R{g3_z$0B;HMnl6<2JWflRCMOkXhf6qt%76-^N!;k^_|n4 zX%61eP!NPJ0`EBN2slpQc?01+@5A%W&iU?9r~>2>O^IO5(~{iO~W!ugF%{#V;yt4ImWb%7RG%& zNw*Bm`j32x%&9T1z%zCpdxjmtb>bsX!+(Ih;XX^VcbO>mKD!nk)0_Dnd<%XTx07vy zY$H3l8oh##nlsINW*@6KM7f{Ab|Wd9>_z%*fwlHc-Jnkb1IS5s zqzckg>6P?px;h-6Ozsc3z=V6TySclci*mLD>t-YKfto@7g2(#@ss&p@xaXYge(S#F zigC2(?ob8nefm5_hG+Y_K(63%F-fkdISj!pY92Ej#u|MpJiZ)84XpXu@w^ti=?^d^sCcQ$C&E$x|eyFke0r3p)=QTg)z5J+i6s{lgUt~D%+affNIxN>O5J3+>2@{=9=Zf zy?aXfn1-Cpe-a2soFMc0d7Z7mEF(?xm3mOD;CEyUO0Stw!T&aRNbUq4w}39gK5L25 zT6-rS5-S8Z2KEK4K!sqw;QPQO99N3B<>^`-bAw%jEKePxK2zz`FB~gVv7`jmRdb>X zaO!D9E5b*xIPSn~tj0d!Ht}Qm(OeQ!iC#%&+W9TN-d>$2X@N@qt(lGBquWqAjSktT zjWCM@FRG0_(fV$j)EX)g((K@yKqxS{F;b{(9t%E&C~nY2Yp7M>SP` z+;h}NSJ{DXr@OwZqVtDPme0rDr=}4n;I#J}Xyy{41l5y%OOK;VP?r7Gc&?a%r)epP zITQF_+%GGBSmN5$o0+4;E~uGY*Q*;Z^|4xMrIR?)ACd7gO@*)Ozky{^S?!^jkIcqw z1uxCveC{M&C!7vvF-Li!F0SYYbZLIYj^I9g#$N%a9k(D{n@9Y(Qk*UCT zXB)7;86Pu~iX{FR-P92Hsc-j}4pbCdDYCxFK1+RL?y+szU34z8qowI9wXRw%@N~~w z!=Yfx4PD`Pb{fw+{&n7UzIE<&&c|_(>tFENuel4lbGjI31;=OM4={)dj{FW?2nrP( z#~rzxKKwq04`s(vUi61uU{HD~HBo9Ae3qG+o-2K9`t6L<{*S>#jPRn`zxr0axn5N3 z4vxdcV4)x@_K|;SY1SL?`Fg@HrmnNGQ*fSf#5nppraR6!5>Y?@i6hi9jz>f^N!>H#%You|&ik<{92ZL|Yw1Er@l zHs}k)`AY^S1$n85Tp7E_F{~sd)igC+-)gL~(uketlA#C}oE=;ToEgGiZWwcoN+4g6 zU8q|0C`Mt^_$JQB(9hKORq!43esDDeOY0wUnAO&}t!-03D6{a6gAk9U>s8DOh&L&l zu_g4Wq%i;T*ipdrQmyY`98vH9U25kdD6$zvG6GjdsN{U?+UcI*ZtU_ovT?7er>NFU zF!m#=izZ?zg;BVv=-!dxIOW(V{KwU#|Fth`i=~Qz(wW;b24q%*C)ILgq)uB&_5~sj zL0d<(`%Co|@uw1sC5kDlGP_7|+7ok|U5t2d*Rj*A!qBkp zBeIk4@w~rf8hMNm<1)DBZ>;h57Q~Ak&~g5QT>}bv9L=NpA~iy}Egw@RYcmYD6=%7v z8AfMKl@E#=0$(zBXOzgema)peP8_AqGyfsq(yiDdY)!TsQ=4X~)9C1O0GMPBxVi7( z7C5JPHhO1!7C7f~YpIp?E3=|m%bbA~dc1WD_vHXngv|WFNsv6ipHvq)t6c;vkNhj{>d_Y9e5!Bv?i#HgPk*~rp2ee zN$Z^P1MHp6!AjCUO0+gzpQayGi%SPHo1_*`ib$-Q)FgFV<_c-8-jg`b3>Nabmb;f? z?Jw_$JYJtG)=s~k=t#Kxt6;+Q#2abh!Gp>h zy{j1m#n2nQx?T{S>)+_1hG2Zcv9?j(oNL{%2SBmB5>9wq)b6rd`Xj!TMl1hn>-2uc zGNZDwPQRx;Q`ah#oL4LwSeE%NV@Br1KyUevJ{<_@Qq=3WvjUw6R=-x2Hh8TK_h+MmF-bbCjd^ zTfg)J+ANLKf9YA_7F3qFOunV6&`+q#WEVJM{x&(YtWjBaXb+Sp@<92T{6J|4XY=D` zoUM~VW|;89wa6O>W$O=D3jdZ~OB}JXSdFY5))A|xMOiG4@pgHVr@oTwiQ&*})K@9l zCpHc&$^4YwFRgoON{TCWQEJ6BJB`Vhm3c5QR+_E0Ho97U?G<(_ILr36Y2p^qgM2`C z11G%|x+<&>ks@Yho0{`#u2Z=pb4|_Z%Go2w^K7Y+cf;~}qn#PZO&U6Pd6Img;oHNn zgeCd*c>i?|a(Fm`nr)U=A45I6IWQFplxI?;yj^-MMvAY3xy8rgA!)5VPg#LJT3_^2 zxO*{1j6PWVqmI!^8HrXTm4l7plZBCvXh#L%A?IN8&~3?7JGY%;<+NMi*v-mn?$Enw zOVsje9yJ5qB*w{gr5s|V;GRJ3z;l0j|D?=c888A8tdMA~JyL%J$ z3Aj^btu{myy@}iAob0V1S|IFzFVs^}=u0Qq$E_^H8TzD<>gg0#B1=dlnWa;)-XIcvAhz={TmdGW{AhBzh?TTGG?Odj zL-Gb3^T-4;s2=h~S(AU`*iB-^J=mR_`BVJ!f<3pHibM2{Nf^U0e(8wU4*w1l)Dd53kl;lGD)b+ z9V7RU=gD7WHR=U5j~1AC`WCg8IBH(k)+m?cu8Ie^Rg8JXJa1gp#w&}Yabg?M6!Xbr zm8)u!_Dhd75{+v{L8GOP{DB_A|GcZ9tetGBL=rw$hEMF~kBZo4LyHnk}psb{6s`x?WTw>jGmhZ6;`i zlr~~`pol+vz!2MNmFz=wGPgzeg3NsidZW3i-u5|Ti*`aCr==JxiM~t&exz`Q--a=q zpDaz(0&h2j+(0z5$C``uO!bEHN=}x7Vs7z%urWHh_d?c^p!QYTi81gc+mc)o)oM2V zXXc1tN9lxo7hSF2Y1{R0dLO-p_DVS{*O8yg4b(RJJaYkh6ji5EshV^HIvf23sC+2> zfzHRCyQ=;Y`jl;Xbu&3QvrCSTg3m1@1Umy$>6b8Kn;4ZoAW&uv04yFoxy zCQ{p}(sU@}VxIzg?Z`f&w~`&~)#i6&w%N<}Qb7hPC&yIBY#}?>k(z4yl=uE8X&+M7 zq|%vlCBG3*72!!oXW)?WgAr}?(syZJ)J)|Ic7$?L zQ)nVynaS9L8n6r`AI$CDjZ*N0*vEKoWKaI*VO! zm+oo*0lCuE8cILuP4IJIU4ROPiI*i!IjJA97Ld2-pUfsE4Y)*jYazx-qw5JH-3vl) z`Ywd*akmph`XVvc-a?$Fd10%2y{|!d<}pj1%$B7KxUC{NXAT7sTpEH=BFg$+s@Bxe)f1abz0Vu}K-vmHtvAhSX* zWtokQH`;AApDHRX)Kc0)y`Xu@`eL^w0`@&{Pm93`vxKr(o-Z31Yc14+>SpYaZPA^5 zo0Llmh~ZLC>7rCnJ}2)~!nG)4E_e+EfPg+=N-!DJ0&*zv2c0TD6JBaG{gtW04dXit zU*Sj6Ss2Fm;a0NM!57fMr78er%XF$W{PQXGU-_nVOkS@h>)EY?b}QgybFH#QY4xc% z$$u#QLu!}QMrr?Lj0+r<7ORW(p+*ry(`u-FHwj1~@@1<9PEB@Po<5Ez2X!mDaLQUs==P5@cK1kOl&Kn<820b^@O=pJ{E)|fI}SH83`R7m3ExgWre25^&wRxZmkH>7Mxf;-yri>XK^SuL!J zb^&rCeG#5UUxYsBQaOfy%CsP-n+3Eqxw%|Q$)c5mR=11w)Es4;&Kw zKXN|xnf}b0L$(Iql!YD&_3R0Ax#2TtBOf~0wlroN2I{{1t)bYBW8hW)$t(ffIF~us zd}dxZo0{A4Z$Igg#!6JHQjK;-Q(zQ5)#~bdmC*-)12hF$JB?iakCk9$TAW?lo@?g- zF(?<+lI{om@g>^=JwJM;)OI$1tJPLm=3D$!GLhuBwA| z4Rlp-9)O?nVJMA0Xoc05N?GM7>eENTG+%Bczy&4>-VYNr6T3$Z?Ws0ik1`G!HO-Ia zWQ(y|BR~HD7nr@~1T)bLw?7af#jpqAb^4j_hF*OJv!3om^(LiH&3WFwdCBY+-&Y&i$1$t7|;2ZcGIr28(rH$>c<^toieo&uih~^9X9NCkO zWty@7u{qe5Oeu85dP77L;lz2uCbt7q9myqgi@0{sBR3%}2~5 zF$!I2I&e4n-a?eX@e|kwbZ4OE<;V+UDzyn+XNn0e9lIP}$6o#vyN=#X<|VS))2!mi zSyIg;)M4^rG!MmI9E20-dH>tT=O|7&xQ2V4U(EAu8ky(}{-je^(FPM{T zCOe0n!{n#q;KS}g7sC*%9deSk#7n4wFEAU~_v|os1e29MMb0D++YPWfjId9j#-`f8 z>@#3x4YMcP7wkgBQKAZYi2RF|4JA(zckG8a&lTorC^+xiLrDkSnE3{5XFqsiRf)dV zBO_LSqs`D(Xu5Vv-(hSq2U*vxG1dSuE|#Kp+FzuDEd%ufR|9i{J;i*|VdSlnbVceT z5z;wvpZG)UD}~DshIrHu5EYpOu=5j?sVn3GLNKd4SkXJSPM)FPR0jK$&op zCCM`6Z=wX@v#*)+jr~|ftHV*HGj{GWS_yPen=aRo`^cw}1)Y#nq>bV)c%ik=Akr(P zMWt;^yOG{J)Alb2Zbc`Vc(JB9I`}-WJ)i|12NT6?as{QV8l?ra7y5BymRZgEVJ)@0 z5<|%IR8eLQyO;~*-Fz&U%GPHe;d~D=8BBZj5!;-5%vIrM@yq$nd;-@3b;yG3Mbr^0 zGfw6+eUUy+-v{%!DYK4w#Qb31F{ha6h7b^^Gk4!1!YQ~m~ zWf{9Nl#JDxef`zo-O)F=DR?IMF_=>vD_#*li4VY@c_L1bo`OH-LN(%!y_q-%j`V$a zLzIKF`y)FBt_?Gxg(`xxV)=g%?`PviVa2G!eg(t$C_fSI_EQ~C9Hf&)m(S9UVZteR z)UD(P3OgO~&buxK%9?{vV@?oO@b9@g+#NOskI$$J_JRY;ettc7otaAwAYNcJzl2Xs z2aM4!nCanGeyhIK*wW0JW+}s|=h8N-tJHUD8O*YNS`{r^%c3>X)@jeRhuRqJty)yw zh*8>D`A<2hJXCJMPopE|>3+Gk4DVVgtCT~UC-s(BD`{#)y(ae4nr2J0D%eY$Ntsb* z6LY6o(0Xdc+7rRz7(h*+H$qvL$bICe2@iy4!WH46kl*nbyq&qw7<9nSBe|ZUEBiQi zQO`S1L-4`#dhdGXc>V*gBi7T|v&G%Rb;prQI0$W?iio#0IElmQH&jbXCclz8S&XVd z`4BtpfOagzI)i)nOS_^kd>j=F(NCWzkaGr?T!qU2C5^|!WP4>3~p zN_wRBLU}BALT~o|$^^9!cCZ!NX?3bnQ*J7~7YXUSlpyy}34N83V7{`d+qtkS9Ww74 zS&dhEPD3;Lqt;!UssstEg>=-UDf0UPy`!$4PLK)Veli(opf@G*`R2tn0j*7AT6TTGx8m@{BsP9Bh!a~Q* z7vMd#CJT|bpflNIU$a)4B&xhMl-<&Cahdp!R7GwKhtzAz9XVPm9qjBUGyltYlabq> zJGe-q)WZ5PV><3o2gE?@fivDQBF(mDZ?id8>bYiRtB?JXs7AG*U(-+N0rV$o7*(Ds zNmaqh6bkmme`J2_Y_qVlO}C$6WL-6nnxqwrs`o*xug&a9Rv)vtVQHFLSEbc{s9iNN z=2?&69=?X2L=OT|lVXiA*BOJMoZo2fLhUFIIf{yOw1-_f%8BX^Cvih@9k00a2&VJSU(Y=7}i_sz7-D5ae49$ zHHtaJw&7ZFS5bA}KzF3_APy*EZ?&ddkDx9t4X?`jWHs^v;?fybno(TuqV59vHCpTh z45pmCU(wVL$a3PfF4{e~#1vO^fKB*ZnW$vPH~A$OMjtX$yLN-+lyUyt@*>KYh2PB z>Myj0S}bN(s+?UOh>ouRiCMu}YZCk#SPnq$_zS%Q~aB@0%m|Rab zA%74vh`PXmred#Lg^E@dI-Wkm%wWrK7deJ!`Rl-}pR)bfLf9+v!`&v7y#?0b9-4zE z9ZhW@^MPA@AC<9C=$|hc&5WOVSD6G|6I6628SygHfmfm7~a6&Kz*WrVI zl)r#KnE4wngDw28{F?&Xg73tV*!$n8FnontbE=saS#(d^uvZe<;8w&_vwPPQ*KJ(N+8hs;`Ufe&GMzu!kYW?M_vv@6ccARkQ>3waQd`>K(O*Udi-lEw%~Q zl=EPoHr)>6W>laq}TE-~-xMr!X)m`Xx*ipVOwU{_-7dl+1AFWnE`87wt`%1YCWz&f}@pfB;b2C;k=eky0MN7*=Z zE_;BQe?4{vGaKxRy<|a>Axojdl|=Re9#@{;M$2><^N@Da@!;FHvZq)z;jPlut_wed ztzO6UaI1Js$75prLd%|;MQ$66n+Q?{*ykMgCMgynD zD1!XN1yTdd70}94v@f} z!ETZlqoWeHiMxr8#xuCm+%2{^+m5L~{~%`&4e)J$B5(9qF;>7DXy1gx_IF(8ZPo-6 z-5>Ne*!k0dM2t3fT7&Fub|?FsrJBvmSH>RWA?}jjyl=(f3T-6T5e?9(9(^P1M{xOA zimFFR%z{GJL}>h{po9Hk%)B~QC3vC^)n6gn&!Rn2m#Kr)KI$}ezxoi59cmk7+~XBO znJ2sDtx{*Ht+Z7t4-c+mN-cF0cw7H!H?@734=Jc~_Czi;!R~1f#<$8qrE?~_uhaq0 zwly+=_S^!#qR>q!B7Ejg@~6R`+{;(wM{-lxEey+?qnFTC=!IbAMxf_T(2O(Bm^ zkLpcqwMg@(9;%DLbUEzx1I-tv!^&$FL&jXh;-G$LZNA3{?24$@WnMJLVHccZKd~wF z-CTu=|7_C2+}S|wpk^Qjc#ph7#YpIakPpiCEP|L2V>MQlAI!CPsVa7&fl(JQ6h@J2j)Qwfp zQ`HUZ;=Pq-wMO1{1KkG8Q8$3Ap8*>n2ARQhrUH{nf2J!kPPQhuh5sr%gz~bfQv||3 zUs%ZZ5xKSnb1=~UD$&XC2hCai|)F`Z~g&Lqc^k>pt7 zf}O?QkGU`w+}++*QDCEO%qxb|_*XBfA7R`B)8H%+QbQjP zXNR?jI0c#%0GB1(}Zsc7Ul51BP=U+y2c2IUbVg{yo^p5yZ% ze%vOUa_n-JcGYoxbCz)q27}JzhXB1A#EEPy+l{S()xgDG#U4M0K94<#>xMKC}O31StLQ&Ac?t(n03wmVcvtJ|gIAZ?wf4QVxz^osnG=L|cq|61&rHwX9 z8>aQbom)-7v<^%Xf#9b+`=_N_ERyRJ@A^Q)JXUP^JL zj4}@1t98HwoUb-hla)ots751JJFXswUhoba;Ti!^h_c(;v$1Pmvp?DnqCY&woKzoL zVwQ6m{BNO~qdPRlZ-qP9{uVn*JFh!kF4MWy8R4Ama5_%ly5th}@+ud`S>W3qq}Gw! ziFX*&Ux3Y@v0hpkmI$1F9M;2r<|pL3WsF?Ff*TnlfFte&aHP!5kSx)Fa9erHBg1I_iUm+Yro?Tu}e+!kzzU^|9U~1Dt1EMBJN2>jMwQ znM!-5nbKXE57tt3to?&ENox%(Awr+6JyxUC8A@)&kP9fAl;P?|)H7G1lBC)Tkul%I zez%ZZMtwjRIS)6H8v(TYJNpg{;W}I!E|S~BHe}o3S$qDEP{a8N%&)eNt^5~u09bA@ z)OMWpRC^HevvKAnT-AGs?`7+W^$;uD8tkiS7@OO$CPu=w%~bZP2ec*V2x5W9@KSGP zR55$}Wj@;qltTUO#`swT1nC>N+4qqt9tQp$Z8oziA=}xDac-mL-`YB6ZZdp&bM>

R7c7?tC4kurfy}g-rV&&8=-wOCsj)snXgJ?EZhWKFANIpts6JEgJlZ zBKBZn33(a*tut95mHc@A7}t;0=$FU{|B%U~Mp9HhL?@f+GHf`1S9s)j<%k!$@)y_% z%yr~e^-&|fPPQTLSkv)cPr|2mFWd{)fx%S_nAdrnPer?$l?qJfBeML-==<;-jNHel z-UQ58^B9=R1;9W(MzE-Jgph7BjO57cSQ$rPRV$A5Y$w>e)rkjK^G^P)qS`$W2Q0Ue zz-Z119_LB;j`Y#$YVXxBWH8mBrSA%Sps~goUEwZgnX}E`7-!Wm+Qwm=eKrc3>)@P3 z+xNkNc})1oJ~%&#YQ&spW$qIHlaJx|atE=CJ)uMBNjUFlY8$nI{tb3)Pd<(R&iCXG zbD?ld>5811U=~nY3Gn&Nd`1EIu^DCr=4U753D3}Js4Mm?p3FzQhg0KXu!H|9;2&Ej z?5yaDv>L3&Bz%iLh=_V2uda%^yNBw4SU3qtTLe+segI#!Fk&qEm_lDTRHvVVMc@UG z;1d{UtF8Mu`&eT-A~y>e%UL5)|Abs?ta?aY2p3=#y)9kV4RE5DLDf~t{P#N?oO&|3;852a@eaWSxg$I+ z1o>@zS^f^!fxE)Kgr4pZNfVPW(yPG%f0cC-4x*EE)q*sz=$-lyqYH0z=G2 zm!o>YFBS?HyOQNJU+8C0`%6_=AcHH!D#1DdGO!iu+zpZ}$6<$?FAqZJxngi{i5I6x z0jVW=t&c`OmM5AIm9;76Aw-n(fj+DwH`3kNxzJQS#p;%m(^!p7XD_flSPx6#zdUz> zv$>PpEA|Agd}H_ro}_O;doiBOW3MzuLj6dqhfyC+(jObW%v0bmQx;*pGuNAaO>_`3 zGr_~GLVkjKR0lE^(HRe%_Y%Cm`Z3ekj+~4+G>fmz&*!?betJ7~g^VF@lYOa7Dhj*C zX>u^(w4Wj>ENR@)7wi4>qWXUAkm^yMi{%2_GFGRJNgb5hChZm66aV-#(CtPJR!82y zS=ZSQ3BCHpURWnf_Q`ViYjjnfc6{@SmPzTrngt zm)3(7c~bwO-!*FE6T%UTiNrTlVryfJ|FCzHL+RW2#50J4`mn{=@4$^;&_(F8)GcZd z9mCAS{<51b#iWp3Y?nCW~ku`rFF#Ko|)m>BvWbT++j)iwJX1F#!Swe}+dY($*I zE}q4jidbbkDzSb&9G;CbzhLR-9(-HrdnOAtnW8ETC&}Qtbn;# zf}Tr%hvW26T=lk0NhX?Uz$^hm?_|SSlljP;hv%2TWT$t6!TTA^^3#Zavzn_d(f$bD z>lH$T#_ggx)`$iNqMBMtC6H-vHs_-PR)>6sJp4Q&nF~-%J+>~{@N_22P+O@h)B?&& z%_NJ$H!YPU>5{-t2C})?LCjiUt1(m_;E0cb5lzJPYGxiZ$D)qA5E*?fi#FfEKXRJ( zul5c3YHn3Sv}UWi_FeC3bjNykR)2`v`Ed9S_<HIK~uog1ZQG0x!J~EXzVgx8||OG zMQ^EBM0PqrpRKRdS0I<1uWy3$e{;i${Jl0j6Q+|KK8y3e3Z_AZ@!q%yjzP5f z#9RV3;A>=$i;X;bZDa*I_48&HA}iIHo`VXbL^Y zpYnf1aRN~eej76|LRRZMvbt;LDC?{>7bscKc&DF79*`5R*bUWa^i0`ktS~bWZ*4K^ z>!KQ>zJN=4d+n$mV%7i(wiUX zuht%G`M`L7rx!H3AeJA8#~MR7&H**OVK0R%=Mv26msm62f~_Iou0&uz7!I}gU_==k zuo{K1x0xnP67)?m%rJH`SDPOP$80B;$#|K~^j<^-hf&Y|i2dR+5C@5y$8BL7LvhxM ztV-;|wS8?@1O|HuYy<~H4{T~lQFDcBD$!M$9JYVT1cLb)dYkS8eZ)M`-O zZO~bxo3Rh~{=Kmd`Nsxe$h~w~hbj)MSAEp>_aoNMYIZYjKzYIGvQ`X#liQeT{0BeE z+uBgIg0cWe_i*s^%Iecl9i|Y0PXyjO7d7nWR$F8`d+mS0sVTxf<*xJJpq1;x&BKaQ z1-zNkaF{Iu7rQ0ye4c`yW$vM_6h{{*nxlcE_|UH)6m=MaFTy=ziqVsR^W=xGZf)Q^ z--*s-PSQ!#vkGEmJfw)=nZ%&BupiORD_~q@)z?6^e98{F5~`w~r0jB>+*AoFqt$)j z|MUaa9||7IWA!{xrq<>LYXRIRHrhw5Ze|cnFzm!0TnHzIRzQS1vC|PZ2#6QrfQ|~>627A& z7nIW{Jw3cfy_dYJz16+9JheTa+~?iD;k+^3Q^}JRu7EZ88q7Y#QY-BWa1(FTplZ8E+7@#v`$(g6tz{<0^t7+Q1?Jzn@uaKxeyx- z#km$!*Q@_&d-b4^!-_{`s1O(|d4QPCgf8&F|NnBenoqN|H+mbRtT9pVp*2>wC~KAC zY7woJuIk;5fndMu`b~Y6-dC@wH^Pp5+^CBhTSe+6eS^_ZLu-Q??*V2L`;Dt2Om=K= z#<^nA2_W42k9Ut}r+cRBrt`J)oofLci0AmW`(}lV@Fc^zcMI2$?Tc)wAx+U?SX+vN zeL9V4N>fnSeb@RcP8qr%=$c-uRW(~}1?}cpxv+dsS}U!P9wDQtgbKEWI?8ADl`=!_ zD!mnpOHJf&a96*tFM_Hy4wd6~`cR$L_dwy$8YsshFy6qS*J$;EyhM5q{!b05fE=b= zh6CjfWgV)lrIgyr1m(I?Ks}+Z);{Qy&3aJY=B6&7ay^F1O=ZJs8%tZvLeAie0T7rE zyhC(GxEi>&qnDx$#eXsw@1efLkWwKey2%u9r8?a3U8x{2!aaT@ROMZ{1BiS?Xk{0W zE-+fMnuO6+FOAyKb3H#Yrjz<>?J&-Dk{mC+l&Z^=5>P&qn zdIVG=Kk}l}=56eG)r`aNOBpXOmHJ8}2#k`9rxEaiCXy2FH z-GN&@G&&mDj4-1rR?B5ZE3j6E0DoRD@0Estk&+8BIjctiBb7BFT>fi-3L?DCSOCF;M_>xa{mT~v;Sf2debk7=3 zb&tKWnm$!|3p7RyzTaCCmEW_?%YA`2hmC8>2 zOSUEAfVLj7+FR*xEINQn$u z<hdp)5bk=O0&Yrzl zn@*jZ-QBZAojJQ3>;O>|3pnxi{|o=m`M|>=4(EN|=egs$uKRw#n${R|LC{vIDd37l z$V3bK8u&(Vr!M1fN~!JCiRwo64kvI7(30sFbXM2(H^h;qHdZUI#ju}4h#I!g86sV4 ztQ~-#Zl!ip`>L%~fAt``tavcHHXw6byr~|=)7$` zQ8gcYc>&Incn+d?!*|bj8>_jS8pcjigZlb65L-x0esNyVf%0Ta3&yQ%fj(bLVzXP*;O2k%13xTg# zstwks85_;b)-d`A%|J1=p3WHe!A|zbg@}`8JJvd{yYAzOHu?A9JhZQ{yenNh(Ki4;?tNOlFP~=G(S-Nh3PnIz>eV5(@ z25u@oXoVI|-tsfKa)g;~j5R)@jBSRh>wA3$`Ngk#e}1#FUJ9MJpZ-yMs(sR$>hIaR zpG_ZeWGuNvIpGbpy_@VawgW`^J{iCvF$inTDN1~9F8wIol)jbS@^pG{i>RyiDFZ=o zlI10GSGl&phjuUxzuv3U5 zzq2NhjkG4qkc9H|`zT8PlFMLq^=^RYB&al3T5@uUjU3-&f`jRMho=Ll8i% z%7ds+BD1;$liwaTHjr$<2JhIwm90Zpdx2g$QK&K}!xncVcPl}4=2AMXZ?KA!fgS|Q z%qw){9G6s~uo%k>Hy0DY2u^YWR337i0#XDS&lqXHbdQ>-p5#2y;!f_yFku61UJz$Q zzQf+k=iJQk^adElRTZJn?-lUMtK?k!;Cu2@qtz2kxG))|i)ej6Y-JO(jF}hSPe%>* z)(A2OnSyo5O5m^VpgJgMZ-pJ10tO$068R{(g9j}6n2-Rw_!E^bzeyeGVO2@`UfLtQ zlRoBlC^#wh7aNmp^#vP_6`!Kls6(&#cETcX%@^R%i-oS#D}|Ejdg-E)bN9A0v*mfu z`+4Q%yz}pczo=|ND=QV1N61sKXO6s?tGdCj5RnDu8*Mvauce zTaFGRqtt6^oVJE;cGFQ7P9xhKLZw6jYY?h9H2e1NLWH=GQ`?GBAHPv@@@_84Ss+5Y zc#kpiSMmhD?h4<#6y?cL`nfHXqIri&;#qMU9M(8cu6d|g&cMWdj}EvU2=Ev>@~>rA zEfb3)C29@BOTs~qori= zJGil(LM`DpdyO^EoNTPnRanWUs2QeF?J$@~A>CWpH^ukKS5IxIX8QX04tV{%(>>9i zJf1S>0?%_sT()<&?*}zV`$K!F1u$dbdKDt8)0}Sh+1O%sCLj2pJ)3)V5EjkF9lDPa z=@zzhw^TyjA~#Y}mC@*r-Z>KKk&%ohti0k>d~%AMD36n?%c87F;c^GQ9x9iSH=#&d z%E=Z3PyiLBV(K4hC)OyBv=V;y7OVLnTG-3tS@4$|%3#M7hu{cQUP`w>_Mf4#StC}J z7QrfXhBa}D&Do0u?E`3*V>pX%57^KZPCyu>uh#D3a|ZdYdFy)jdupQwTbvKH;A49^oEN@1K?Y;Ly_GfZN0P_R zN53?`nSLJEa91I8T2Zcnt|cfQCcBz(?qDW7K_EFmS$=ZL+0~hhZs;J4{TTV4^p&(m z3`4tCPPoU)TMqB?m>H*2jy(+qZ=w}KjmE#`S(Ktj(73n3hkT~0LI<&b1}3JXFib?3 z7EFFy5r(U-rV}?l@CCziO(X;CL$8by-0{6AFZWa7y2_j3E$ORCu297H&b!0g*IUZ# z^|(D1sQIEpyzc_sP!)W&rPl*LJclLUh34V3Rn?x(N^pV23RD1}pt3wsSxko-_l*MA#5#>WLN30xbvGw^DlfNr)M@s$?%GhNXd2ORZ}_s{ZM?N`t5nyUlq;1SLO z&NGhQpv{eyDe`V9Rje)U^yA zX__&J)+==J`LG7Bsd;qjNoa)MYen@AL{o{NPtWvIbW}6I?2b`Ix0vhp^O@dY81IfK z9@nG!c#YaI5CrUNtVjl{pb}k~`k*Wdfx8)M?lztH`FmXbG^}?g>ORuM_tG1ApHi6) zA>TU&(xtny({#GYleYUu1|$Rw2~KV zN_Wn8c5}wT5x#INb5ut|=2BY8Q>pbX3a zzOTDnPI(P;k?AN;|EoOCO!%RKC^{ZFJG)NPxx1cUN8-xi?1zo$sW0=flA5L3e)-Az zfq(om(S=lT4R=nbT4II#Sn7cP*aNET6c=-L&?e##2Q?3~(1ou;vpoW3=}{^^XX`zQ z17~a5>S(y?SamXYtQ&YwqP7KOybHZ0tCK^-kUO!THX3m-iTt;;~_$o__bJ!{-B9uvhBLwWosilk8DyZOXik@hI! zo_C;?4e{ew@nv(^3ol?m|0E9hmMG=}xwD&ib0J#3;Sf5NV^&S;B=^CDwfzxI?Qt@MP2>RM$bc^Uj-YqX z^c4fws-o)PGZys@Lp^)k(dZfbxhr|T=Jc=M)ghXxwblpHyJ$Rd^zUHxi}h8^pj&Te zBpOA`1oI~p)iq& zlniAvF==zuA0zlLoaM?X4Oe-66||mHO6WVcg({=UV7A?jcd#y;Yzhw`Siiwkx7EY7 zy6P3qIQi(St`1jMqfijEhUn!MYrjxU9-@69yPTp`*0c3pIejc(V2`6#`$Q#ZeXFZA z03FRr)LYfhp-jR&w3bgwDj1&$-K6utR6S@pr(1-$O|_o4NWA-CP~> zn%Rrfsc!H(H%%2UJdOM$K#u~i+K=8l));2&q`tj0Y(ifiomgQ}D0nJ@^n7d1fDL_5 zHC21-M{5Qt2|}2_wZ!afo!A zzId&OF%$UcDG!pz$YbT+ay0qgEU7ws@xFM6_%Dk}Gavf2^Wt)%uMlxF5$y)MEd1pP zYc<(`N^Mg<`%VroM-A3s<1L8jP&$eHjasE3_1Bv@cP@;RB)_JvqYiecIo4&ZnQBIX zx=bcN?@E*&fK4w7F20s*Bn~a*B9tHxu{1HW5lh0iPyyv#A(~%>h_>=ZY2dsm)P0OL`gg?%oIAX$`zuM z2dvmDxmv56%=KJyBGJB#-sUiC%gMSgiH>12d*vD0t!8EsQ(&z)&GPKF73L#!7m+Aa zi&^>ksKGppu!hpBBd?Xh&-}^aEZCnfb|mv%khrTlJ0_b`(jJNBiH+$^CxvpqUdauV znM$IPq11HjbTp-cZyY+)!>-P*15OiNU50Xq-2V(rX1el5c>tfeKa)wM|| zxtHRpt6`~A*$rpWhh3pg;RSYbht-kFx)}H}jSehr^{TK10h|$*$PVp<;xe!1Rqv~N z)OqNO=BcOEFl{hD%g{J5S9kE6CBOiF1{*2`CKhWxfx`@AMK6af+rpfe6HW+|#D-+< z31so>sDK~CRdnIeQy$5)qjClv#=0qUz~P@L66|Uz_}raTF#jbR+~;!G-nXpk6z21~ zcmmzs&r~B`K{J-48mdIp*@G+lk9&Ipefk8R)j~nH3Vv&sIhd^WleymN%1@tBulETR zp#U;c)0#~_xdA_M8^7tspG-9x5l62FZ6ATfh(r{+t3|j0qVD;m)&3LrCWO6B5klqP(woUicAIZAi z)XUVhLhb8zYpg_<+7_jp2g#sP%`)Y?UqtwYLSw->RY0Ka;uPZPDEA2 z4l4p5yN>fi0?B1Zu?8mF73in36FlLP8D}0h<{10oNbg|#Hd=FtN`{yRL2{Q-?@?86 zg?FiFJU3RERk3TWnS*Meo?vr~Je72uuBB#Vh90{Yr~Y0gD6w$665k7~hxcc3fia^V_v##O{#ShQE5`Lm_- z(km&6{B8^i(&A`x)2Zb60seFf`|bkkZ5QkPHU2G=sQfVKKs)lVG_3Y@%S$i7<@R&z z>~q+%mc*wnyACTyrz)hs&;x~XIr5kmba9=JqIUp1q(bN5Om>&eYb`?k^Mv``Z9PCw zTnSv|CYjDla|l&x`;4B3WUSHak?HIO6CJKKXT_W$LhXfWX|EcfC6K``8AQOGd+CMs80%ag%xDn4EN`R2>ZyE+7^c7cEkE5P-y`e4C$Gh4lwu!!#BRQqQn0A^$<)7K zOANkRB(-2o6_a{1r`^#6eZfj?kmF?&o3#KmCsKOJew!iA0vmWHs_enPsqWlJW?7hO z>bP7DrvZvH44>FASFu~y&II$KlO&PsdsaKzb)rDGc0#+RMN)4>7k%#a1md)%=3nOT z=0vk6{w5OCsSe-83eol9fmMXMfVChS)zOL0poY5-|FI6bl^;<_zapoN=N(-G?-F1$_2TtlBC3AU)}V`m`DYKi!)|Bqiv3L1*2g;jkdk7v#|4DlzuM9P34$! zTDhUzRE}Von

  • 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 Date: Fri, 5 Jun 2020 20:22:40 -0300 Subject: [PATCH 221/235] AutoPickup,AutoStore: auto store code was getting messy... reworked it to use exellent PCRE! - unified PCRE simple compilation code (for sfx is more complex and was not touched). - PCRE is now used also to interpret a container's label for auto-store items on it. - PCRE simple compilation is part now of FeString (highly related). obs.: some new code went unused after this PCRE rework but not yet deleted. --- FeLib/Include/festring.h | 2 + FeLib/Source/festring.cpp | 42 ++++++++++++++++++++ Main/Include/game.h | 2 +- Main/Include/id.h | 1 + Main/Include/ivandef.h | 17 ++++---- Main/Include/miscitem.h | 4 ++ Main/Source/cmdcraft.cpp | 8 ++-- Main/Source/game.cpp | 84 +++++++++++++-------------------------- Main/Source/iconf.cpp | 5 +-- Main/Source/id.cpp | 13 +++++- Main/Source/miscitem.cpp | 28 +++++++++++++ 11 files changed, 132 insertions(+), 74 deletions(-) diff --git a/FeLib/Include/festring.h b/FeLib/Include/festring.h index 3c1af2a54..5b4d93895 100644 --- a/FeLib/Include/festring.h +++ b/FeLib/Include/festring.h @@ -15,6 +15,7 @@ #include #include +#include #include "felibdef.h" @@ -119,6 +120,7 @@ class festring void ExtractWord(festring&); long GetCheckSum() const; void EnsureOwnsData(bool = false); + static pcre* CompilePCRE(pcre *pcreExistingRegexWorker, cfestring &fsPattern, festring *pfsErrorMsg=NULL); private: static void InstallIntegerMap(); static void DeInstallIntegerMap(); diff --git a/FeLib/Source/festring.cpp b/FeLib/Source/festring.cpp index 60a15da98..ba7ee6495 100644 --- a/FeLib/Source/festring.cpp +++ b/FeLib/Source/festring.cpp @@ -13,6 +13,7 @@ #include #include #include + #include "festring.h" #include "allocate.h" #include "error.h" @@ -912,3 +913,44 @@ void festring::EnsureOwnsData(bool Unique) CreateOwnData(Data, Size); } } + +/** + * + * @param pcreExistingRegexWorker it has to be freed if was already set + * @param fsPattern + * @param bWarnOnError + * @return + */ +pcre* festring::CompilePCRE(pcre *pcreExistingRegexWorker, cfestring &fsPattern, festring *pfsErrorMsg) +{ + if(pcreExistingRegexWorker) + pcre_free(pcreExistingRegexWorker); + + if(fsPattern.IsEmpty()) + return NULL; + + const char *errMsg; + int iErrOffset; + pcreExistingRegexWorker = pcre_compile( + fsPattern.CStr(), + 0, // no options + &errMsg, &iErrOffset, + 0 // default char tables + ); + + if (!pcreExistingRegexWorker){ + festring fsErr; + fsErr<<"Regex validation failed, if ignored will just not work at all.\n" + <) diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index 9563dc088..bfbf50d0b 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -2096,11 +2096,9 @@ struct srpInspect : public recipe{ material* matM = it0->GetMainMaterial(); material* matS = it0->GetSecondaryMaterial(); festring fs; - fs << it0->GetName(DEFINITE) - << " (" << game::StoreMatchNameKey(it0) << " or " //used by auto-store items on containers, so here is the place to determine it's value - << game::StoreMatchNameKey(it0,true) << ")" - << " is made of "; - if(matM)fs<GetName(UNARTICLED); + fs << it0->GetName(DEFINITE) << " is made of "; + if(matM) + fs<GetName(UNARTICLED); if(matS){ if(matM)fs<<" and "; //actually, there is only 2nd material if there is main but anyway... fs<GetName(UNARTICLED); diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 949712a99..698f0f163 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1371,10 +1371,12 @@ cchar* game::StoreMatchNameKey(item* it,bool bUnarticled) fsRet=""; fsRet<GetName(UNARTICLED|UNLABELED); - else + if(bUnarticled){ +// fsRet<GetName(UNARTICLED|UNLABELED|STRIPPED|NOPOSTFIX); + fsRet<GetNameToMatch(); + }else{ fsRet<GetNameSingular()<GetStack()->FillItemVector(vit); - static festring fsMatchGeneric,fsMatchPrecise; - fsMatchGeneric=StoreMatchNameKey(itToStore); - fsMatchPrecise=StoreMatchNameKey(itToStore,true); - DBG2(fsMatchGeneric.CStr(),fsMatchPrecise.CStr()); for(int i=0;i(vit[i]); @@ -1401,10 +1399,7 @@ void game::AutoStoreItemInContainer(item* itToStore,character* C) if(lRemainingVolGetVolume()) continue; - if( - itc->GetLabel().Find(fsMatchGeneric)!=festring::NPos || - itc->GetLabel().Find(fsMatchPrecise)!=festring::NPos - ){ + if(itc->IsAutoStoreMatch(itToStore->GetName(DEFINITE))){ itToStore->RemoveFromSlot(); itc->GetContained()->AddItem(itToStore); ADD_MESSAGE("%s was safely stored in %s",itToStore->GetName(DEFINITE).CStr(),itc->GetName(DEFINITE).CStr()); @@ -1414,40 +1409,21 @@ void game::AutoStoreItemInContainer(item* itToStore,character* C) } } -std::vector afsAutoPickupMatch; pcre *reAutoPickup=NULL; -void game::UpdateAutoPickUpMatching() //simple matching syntax +void game::UpdateAutoPickUpRegex() { - afsAutoPickupMatch.clear(); - - bool bSimple=false; - if(bSimple){ //TODO just drop the simple code? or start the string with something to let it be used instead of regex? tho is cool to let ppl learn regex :) - if(ivanconfig::GetAutoPickUpMatching().GetSize()==0 || ivanconfig::GetAutoPickUpMatching()[0]=='!')return; - - std::stringstream ss(ivanconfig::GetAutoPickUpMatching().CStr()); - std::string match; - while(std::getline(ss,match,'|')) - afsAutoPickupMatch.push_back(festring(match.c_str())); - }else{ - //TODO test regex about: ignoring broken lanterns and bottles, ignore sticks on fire but pickup scrolls on fire - // static bool bDummyInit = [](){reAutoPickup=NULL;return true;}(); - const char *errMsg; - int iErrOffset; - if(reAutoPickup)pcre_free(reAutoPickup); - reAutoPickup = pcre_compile( - ivanconfig::GetAutoPickUpMatching().CStr(), //pattern - 0, //no options - &errMsg, &iErrOffset, - 0); // default char tables - if (!reAutoPickup){ - std::vector afsFullProblems; - afsFullProblems.push_back(festring(errMsg)); - afsFullProblems.push_back(festring()+"offset:"+iErrOffset); - bool bDummy = iosystem::AlertConfirmMsg("regex validation failed, if ignored will just not work at all",afsFullProblems,false); - } + //TODO test regex about: ignoring broken lanterns and bottles, ignore sticks on fire but pickup scrolls on fire + festring fsErr; + reAutoPickup = festring::CompilePCRE(reAutoPickup,ivanconfig::GetAutoPickUpMatching(),&fsErr); + if(!fsErr.IsEmpty()){ + std::vector afsFullProblems; + afsFullProblems.push_back(fsErr); + bool bDummy = iosystem::AlertConfirmMsg("Failed updating auto-pickup regex.",afsFullProblems,false); } } bool game::IsAutoPickupMatch(cfestring fsName) { + if(!reAutoPickup) + return false; return pcre_exec(reAutoPickup, 0, fsName.CStr(), fsName.GetSize(), 0, 0, NULL, 0) >= 0; } int game::CheckAutoPickup(square* sqr) @@ -1460,30 +1436,26 @@ int game::CheckAutoPickup(square* sqr) lsquare* lsqr = (lsquare*)sqr; - static bool bDummyInit = [](){UpdateAutoPickUpMatching();return true;}(); + static bool bDummyInit = [](){UpdateAutoPickUpRegex();return true;}(); itemvector iv; lsqr->GetStack()->FillItemVector(iv); int iTot=0; for(int i=0;iGetRoom() && it->GetRoom()->GetMaster())continue; //not from owned rooms - if(it->GetSpoilLevel()>0)continue; + if(it->GetSpoilLevel()>0)continue; //just a guess no one auto-wants it + bool b=false; if(!b && ivanconfig::IsAutoPickupThrownItems() && it->HasTag('t') )b=true; //was thrown - if(!b && !it->HasTag('d')){ - if(reAutoPickup!=NULL){ - if(IsAutoPickupMatch(it->GetName(DEFINITE))){ - b=true; - } - } - } - if(!b){ //TODO use player's perception, in case of a stack of items, to allow random pickup based on item volume (size) where smaller = harder like tiny rings, to compensate for the easiness of not losing a round having to pick up the item interactively - for(int i=0;iGetNameSingular().Find(afsAutoPickupMatch[i].CStr(),0) != festring::NPos){ - b=true; - break; //each simple match loop - } - } + if(!b && !it->HasTag('d')){ //was NOT intentionally dropped (dropping adds such tag) + /** + * TODO ? + * Use player's perception, in case of a stack of items, + * to allow random pickup based on item volume (size) where smaller = harder like tiny rings, + * to compensate for the easiness of not losing a round having to pick up the item interactively. + * But it may just be annnoying... + */ + b = IsAutoPickupMatch(it->GetName(DEFINITE)); } if(b){ it->MoveTo(PLAYER->GetStack()); diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 5d3bbc7ac..d97e68c9a 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -57,8 +57,7 @@ stringoption ivanconfig::AutoPickUpMatching("AutoPickUpMatching", "Automatically pick up items according to a regular expression.\n" " To disable something, you can invalidate it with '_' without removing it from the expression (eg. '_dagger').\n" " To disable everything at once, begin this config option with '!'.\n" - " Containers inscribed with eg. '+key+ring+scroll' will auto store such items,\n" - " and you can determine what to inscribe by craft/inspecting the items.\n" + " Containers can also auto store items when inscribed with a regular expression (regex).\n" " Due to current constraints on length of options, editing is easier to do externally for now.", //TODO if multiline text editing is implemented, remove the last help statement. "!((book|can|dagger|grenade|horn of|kiwi|key|ring|scroll|wand|whistle)|^(?:(?!(broken|empty)).)*(bottle|vial)|sol stone)", &configsystem::NormalStringDisplayer, @@ -917,7 +916,7 @@ void ivanconfig::AutoPickUpMatchingChanger(stringoption* O, cfestring& What) if(O!=NULL){ O->Value.Empty(); O->Value<= 0; +} + oillamp::oillamp(const oillamp& Lamp) : mybase(Lamp), InhabitedByGenie(false) { } From 39bd73727c411f1ecb271e9eed947239c0c0f3c4 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 5 Jun 2020 20:43:59 -0300 Subject: [PATCH 222/235] updated NEWS file --- NEWS | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/NEWS b/NEWS index bbce5336a..cb4605aa1 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,26 @@ Next version ------------ +Version 0.59 +------------ + +Changes: +* New craft skill, now the old value (based on stats) is just a bonus. +* Light emitation is now based on item's main material volume. +* Custom keybindings lets you tweak any command to your heart content! +* Auto store items in containers based on inscribed pcre regex. +* Main menu can now have one sfx and one background for each menu item. +* Every in-game question now has a history accessible by pressing up/down arrows. +* Developer tools improved: wizard code, new cursed imortal code, + many new commands to developer console. +* New sfx for digging and imp's grim. + +Fixes: +* Crafting takes less time if fumbles happens, and much less for critical ones. +* Developer console is accessible again. +* Fixed rare dungeon generation crash (by retrying to generate it). +* Fixable overcrowded sumo house thru a new developer console command. + Version 0.58, released 15th March 2020 -------------------------------------- From f5b02cc437c83baacef2ec9519b743891ae82d10 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 5 Jun 2020 22:30:02 -0300 Subject: [PATCH 223/235] update news --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index cb4605aa1..d19521f5a 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,7 @@ Fixes: * Developer console is accessible again. * Fixed rare dungeon generation crash (by retrying to generate it). * Fixable overcrowded sumo house thru a new developer console command. +* Lists will always accept up/down arrow to navigate now. Version 0.58, released 15th March 2020 -------------------------------------- From 214654bab37b9e13bfea721d6a676e7677d83f00 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Fri, 5 Jun 2020 22:44:39 -0300 Subject: [PATCH 224/235] update news --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index d19521f5a..0961c9a94 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,7 @@ Changes: * Developer tools improved: wizard code, new cursed imortal code, many new commands to developer console. * New sfx for digging and imp's grim. +* Auto map notes for a lot more simple places (like doors) easifying navigation. Fixes: * Crafting takes less time if fumbles happens, and much less for critical ones. From 51c7645501f04ab4fa4ffd39e7d28334aa089915 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 6 Jun 2020 00:18:13 -0300 Subject: [PATCH 225/235] new graphics for stair up/down (only works on a new game, otherwise would have to replace existing graphics position at OLTerra.png, should?); auto merge spider silk lumps after cutting a web; --- .../Graphics.WorkFiles/OLTerra.xcf | Bin 50036 -> 48207 bytes Graphics/OLTerra.png | Bin 11586 -> 11673 bytes Main/Source/cmdcraft.cpp | 109 +++++++++--------- Script/olterra.dat | 8 +- 4 files changed, 60 insertions(+), 57 deletions(-) diff --git a/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf b/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf index 46509975eb1363ffcf14824806cced3bb6124876..75ab1052a34e8b6cf82b9ed5290426943c8da0f0 100644 GIT binary patch literal 48207 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 f>-HJ?y+F? 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 E9~B{9g8%>k diff --git a/Graphics/OLTerra.png b/Graphics/OLTerra.png index 16a67f6970e066a9a2f94b0df02198e9c6a6282e..677e0b2968879828fb1c3399b68ff2352211999e 100644 GIT binary patch delta 10922 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 Y=CI@Irwv&lii@H71o9a3$Ts5t0a}FIod5s; delta 10834 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 MGrgzPk8KeD2UoXcGetRightWielded())vi.push_back(rpd.rc.H()->GetRightWielded()); + if(rpd.rc.H()->GetLeftWielded ())vi.push_back(rpd.rc.H()->GetLeftWielded ()); + } + + if(bAllowAllEquipped){ //TODO not showing on list... + for(int c = 0; c < rpd.rc.H()->GetEquipments(); ++c){ + if( + c!=RIGHT_WIELDED_INDEX && + c!=LEFT_WIELDED_INDEX && + rpd.rc.H()->GetEquipment(c) + ){ + vi.push_back(rpd.rc.H()->GetEquipment(c)); + } + } + } + + rpd.rc.H()->GetStack()->FillItemVector(vi); //TODO once, the last item from here had an invalid pointer, HOW? + + return vi; + } + /** + * @return the lump where it was mixed into (or the input lump) + */ + static item* lumpMix(itemvector vi,item* lumpToMix, bool& bSpendCurrentTurn){ + // to easily (as possible) create a big lump + lump* lumpAtInv=NULL; + for(int i=0;i(vi[i])!=NULL){ + lump* lumpAtInv = (lump*)vi[i]; + if(lumpAtInv->GetMainMaterial()->GetConfig() == lumpToMix->GetMainMaterial()->GetConfig()){ + lumpAtInv->GetMainMaterial()->SetVolume( + lumpAtInv->GetMainMaterial()->GetVolume() + lumpToMix->GetMainMaterial()->GetVolume()); + lumpAtInv->CalculateAll(); + + craftcore::SendToHellSafely(lumpToMix); DBG5("SentToHell",lumpToMix,lumpToMix->GetID(),lumpAtInv,lumpAtInv->GetID()); + bSpendCurrentTurn=true; //this is necessary or item wont be sent to hell... + break; + } + } + } + + return lumpAtInv!=NULL ? lumpAtInv : lumpToMix; + } + void failPlacementMsg(recipedata& rpd){ ADD_MESSAGE("%s can't be placed here.",name.CStr()); rpd.SetAlreadyExplained(); @@ -1263,58 +1315,6 @@ struct recipe{ CIok); } - static itemvector vitInv(recipedata& rpd,bool bAllowWielded=true,bool bAllowAllEquipped=false){ - itemvector vi; - - //prefer already equipped - if(bAllowWielded){ //TODO not showing on list... - if(rpd.rc.H()->GetRightWielded())vi.push_back(rpd.rc.H()->GetRightWielded()); - if(rpd.rc.H()->GetLeftWielded ())vi.push_back(rpd.rc.H()->GetLeftWielded ()); - } - - if(bAllowAllEquipped){ //TODO not showing on list... - for(int c = 0; c < rpd.rc.H()->GetEquipments(); ++c){ - if( - c!=RIGHT_WIELDED_INDEX && - c!=LEFT_WIELDED_INDEX && - rpd.rc.H()->GetEquipment(c) - ){ - vi.push_back(rpd.rc.H()->GetEquipment(c)); - } - } - } - - rpd.rc.H()->GetStack()->FillItemVector(vi); //TODO once, the last item from here had an invalid pointer, HOW? - - return vi; - } - - /** - * @return the lump where it was mixed into (or the input lump) - */ - static item* lumpMix(itemvector vi,item* lumpToMix, bool& bSpendCurrentTurn){ - // to easily (as possible) create a big lump - lump* lumpAtInv=NULL; - for(int i=0;i(vi[i])!=NULL){ - lump* lumpAtInv = (lump*)vi[i]; - if(lumpAtInv->GetMainMaterial()->GetConfig() == lumpToMix->GetMainMaterial()->GetConfig()){ - lumpAtInv->GetMainMaterial()->SetVolume( - lumpAtInv->GetMainMaterial()->GetVolume() + lumpToMix->GetMainMaterial()->GetVolume()); - lumpAtInv->CalculateAll(); - - craftcore::SendToHellSafely(lumpToMix); DBG5("SentToHell",lumpToMix,lumpToMix->GetID(),lumpAtInv,lumpAtInv->GetID()); - bSpendCurrentTurn=true; //this is necessary or item wont be sent to hell... - break; - } - } - } - - return lumpAtInv!=NULL ? lumpAtInv : lumpToMix; - } - static void SetOLT(recipedata& rpd,lsquare* lsqr,int iCfgOLT){ switch(iCfgOLT){ case FORGE: @@ -1495,7 +1495,10 @@ struct srpCutWeb : public recipe{ if(bSuccess){ rpd.SetAlreadyExplained(); material* matSSilk=material::MakeMaterial(SPIDER_SILK); - craftcore::FinishSpawning(rpd, craftcore::PrepareRemains(rpd,matSSilk,CIT_LUMP,RAND()%6+3)); + item* itSS = craftcore::PrepareRemains(rpd,matSSilk,CIT_LUMP,RAND()%6+3); + craftcore::FinishSpawning(rpd, itSS); + bool b=false; + lumpMix(vitInv(rpd),itSS,b); }else{ bool bGetStuckOnTheWeb=false; bool bLoseWeapon=false; diff --git a/Script/olterra.dat b/Script/olterra.dat index 8a43e75d1..603845f09 100644 --- a/Script/olterra.dat +++ b/Script/olterra.dat @@ -759,26 +759,26 @@ stairs /* olterrain-> */ Config STAIRS_UP; { PostFix = "upwards"; - BitmapPos = 0, 192; + BitmapPos = 64, 112; IsUpLink = true; } Config STAIRS_DOWN; { PostFix = "downwards"; - BitmapPos = 0, 208; + BitmapPos = 64, 96; } Config SUMO_ARENA_ENTRY; { PostFix = "downwards"; - BitmapPos = 0, 208; + BitmapPos = 64, 96; } Config SUMO_ARENA_EXIT; { PostFix = "upwards"; - BitmapPos = 0, 192; + BitmapPos = 64, 112; IsUpLink = true; } } From fa7a9fe3377b22de5fb3ed43777b491809b6bf2f Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 6 Jun 2020 00:19:18 -0300 Subject: [PATCH 226/235] update news --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 0961c9a94..3e73f8538 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,7 @@ Changes: many new commands to developer console. * New sfx for digging and imp's grim. * Auto map notes for a lot more simple places (like doors) easifying navigation. +* New gfx for up/down stairs. Fixes: * Crafting takes less time if fumbles happens, and much less for critical ones. From aeeaec181d4bc38c312d6b3b36bde2615aaaf6a8 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 6 Jun 2020 02:39:49 -0300 Subject: [PATCH 227/235] swapped up/down stairs gfx with new graphics (so kept old one elsewhere), improved stairs coloring to be automatic/scriptable, added torches with configurable colors too, to stairs gfx. --- .../Graphics.WorkFiles/OLTerra.xcf | Bin 48207 -> 48949 bytes Graphics/OLTerra.png | Bin 11673 -> 11646 bytes Script/olterra.dat | 10 ++++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf b/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf index 75ab1052a34e8b6cf82b9ed5290426943c8da0f0..49b8332c054ba4f1ee508d51a53dbe8900b378f1 100644 GIT binary patch delta 2497 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 NOq)!#@`+94{{zGF!TbOK delta 10920 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 bY(D{BA9j4n*pL^Yx)|DzA&)Q*?W6u5whi3P diff --git a/Script/olterra.dat b/Script/olterra.dat index 603845f09..892fa8d49 100644 --- a/Script/olterra.dat +++ b/Script/olterra.dat @@ -754,31 +754,33 @@ stairs /* olterrain-> */ DigMessage = "The stairs are too hard to dig."; MainMaterialConfig == GRANITE; MaterialColorB = rgb16(160, 64, 0); + MaterialColorC = rgb16(220, 220, 0); + MaterialColorD = rgb16(175, 130, 0); NameSingular = "stairway"; Config STAIRS_UP; { PostFix = "upwards"; - BitmapPos = 64, 112; + BitmapPos = 0, 192; IsUpLink = true; } Config STAIRS_DOWN; { PostFix = "downwards"; - BitmapPos = 64, 96; + BitmapPos = 0, 208; } Config SUMO_ARENA_ENTRY; { PostFix = "downwards"; - BitmapPos = 64, 96; + BitmapPos = 0, 208; } Config SUMO_ARENA_EXIT; { PostFix = "upwards"; - BitmapPos = 64, 112; + BitmapPos = 0, 192; IsUpLink = true; } } From a31091a2d459ffba4efe62434a29d1599b216a54 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sat, 6 Jun 2020 04:53:34 -0300 Subject: [PATCH 228/235] new stairs gfx: used a trick to grant it won't look like a ramp with xBRZ. --- .../Graphics.WorkFiles/OLTerra.xcf | Bin 48949 -> 48961 bytes Graphics/OLTerra.png | Bin 11646 -> 11700 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf b/.devsPrefs/AquariusPower/Graphics.WorkFiles/OLTerra.xcf index 49b8332c054ba4f1ee508d51a53dbe8900b378f1..c0d220ae9d6c9dd69bfce51d45f8d8c41e74bb7e 100644 GIT binary patch delta 408 zcmY+=Pbhq)8E`@WKL(Ec2hazJz7h*FYrB00IxFRl)v z2$>n#%px^MB9eoHqr-Ayc*heLp8E9sdY-<|;g>AhPK&nPVY@P!7_ql2u4nvIrb+HQ zZd^GabH0umg4itXRNmH?VdK zY(eITy|gYIU?0NKQpGH(xS^`9?lXlUtESK}Mj8=X>LeT-`lHUrD&H@=Xt7i3o_cE7WR*%1UY-i3y j|8ZE+;vD*u&RhZ6TBfnUzw~`(S;B^bx-rIRNl2WQX@~Y*oL|TTi_2_{ZLtGj^#xe(1d^A) zW*yrAc2ogyJR`ifN^sdufME*vD9F<>`jx;Ko=`~+9dWuMokg6f%CslPfDKbzV zc$QXsckj4Kf4>$6AZ~aTK*j>>Z@^JmP4V6y;N1`#rt{2_3!u0?O*3{iz1c@CNzVT( ga?XHUC97w$xF|aTWMgca9-Kl^g2$_DHI?N8KU=qUH~;_u diff --git a/Graphics/OLTerra.png b/Graphics/OLTerra.png index b2626b7e68fae203d986926f50515313e2dfb68f..43eba8cc3a5d0fa86f21622be5d14513fcf1f8e3 100644 GIT binary patch delta 10308 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 Date: Sat, 6 Jun 2020 22:54:41 -0300 Subject: [PATCH 229/235] FixSumoHouse: just after win the fight, decos and bananagrower may get stuck, so it now checks further around sumowrestler to provide the fix. --- Main/Source/human.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp index 3af408da9..fbd2e0761 100644 --- a/Main/Source/human.cpp +++ b/Main/Source/human.cpp @@ -5246,11 +5246,17 @@ void FixSumoWrestlerHouse(festring fsCmdParams) for(int d = 0; d < SM->GetNeighbourSquares(); ++d) { lsquare* Square = SM->GetNeighbourLSquare(d); - if(Square){ - character* C2 = Square->GetCharacter(); - if(C2 && dynamic_cast(C2)){ - C2->TeleportRandomly(true); + for(int d2 = 0; d2 < 8; ++d2) + { + lsquare* Square2 = Square->GetNeighbourLSquare(d2); + + if(Square2){ + character* C2 = Square2->GetCharacter(); + if(C2 && dynamic_cast(C2)){ + C2->TeleportRandomly(true); + } + } } } } From eb720e74d863fb5c8aac11ebab01b5f36654e544 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 7 Jun 2020 23:24:48 -0300 Subject: [PATCH 230/235] pcre on festring updated cmake for igor and mihail too --- FeLib/Source/festring.cpp | 4 ++-- igor/CMakeLists.txt | 18 ++++++++++++++++-- mihail/CMakeLists.txt | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/FeLib/Source/festring.cpp b/FeLib/Source/festring.cpp index ba7ee6495..bece51dc3 100644 --- a/FeLib/Source/festring.cpp +++ b/FeLib/Source/festring.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "festring.h" #include "allocate.h" @@ -945,8 +946,7 @@ pcre* festring::CompilePCRE(pcre *pcreExistingRegexWorker, cfestring &fsPattern, <<"offset:"< Date: Fri, 26 Jun 2020 22:21:13 -0300 Subject: [PATCH 231/235] craft: improved recipedata::id() to be unique; also fixed load game about recipedata; --- Main/Include/craft.h | 1 + Main/Source/cmdcraft.cpp | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Main/Include/craft.h b/Main/Include/craft.h index 61b9a07f7..cf5e24039 100644 --- a/Main/Include/craft.h +++ b/Main/Include/craft.h @@ -229,6 +229,7 @@ class recipedata { v2 v2TailoringWorkbenchLocation; long lDamageFinalItem; + long lInitialTurn; public: recipedata(humanoid* H=NULL,uint sel=FELIST_ERROR_BIT); diff --git a/Main/Source/cmdcraft.cpp b/Main/Source/cmdcraft.cpp index b6361ca9a..ed586224c 100644 --- a/Main/Source/cmdcraft.cpp +++ b/Main/Source/cmdcraft.cpp @@ -337,6 +337,7 @@ void recipedata::Save(outputfile& SaveFile) const << v2TailoringWorkbenchLocation << lDamageFinalItem + << lInitialTurn ; } @@ -396,16 +397,10 @@ void recipedata::Load(inputfile& SaveFile) >> v2TailoringWorkbenchLocation >> lDamageFinalItem + >> lInitialTurn ; - if(game::GetCurrentSavefileVersion() >= 135){ - SaveFile >> bTailoringMode; - SaveFile >> v2TailoringWorkbenchLocation; - } - -// if(otSpawnType!=CTT_NONE) -// SaveFile >> otSpawn; rc.integrityCheck(); } cfestring recipedata::id() const @@ -420,6 +415,7 @@ cfestring recipedata::id() const festring fs; #define RPDINFO(o) fs<<(#o)<<"="<<(o)<<"; "; + RPDINFO(lInitialTurn); RPDINFO(rc.IsCanBeSuspended()); RPDINFO(itToolID); @@ -3432,7 +3428,9 @@ truth craftcore::Craft(character* Char) //TODO currently this is an over simplif prp->action+" "+prp->name+ (rpd.itSpawnCfg!=0 ? festring(" ("+rpd.fsItemSpawnSearchPrototype+")") : festring())+ ", started at "+game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex(), true); - + + rpd.lInitialTurn=game::GetTurn(); + rpd.ClearRefs(); //pointers must be revalidated on the action handler DBG1(rpd.dbgInfo().CStr()); if(Char->SwitchToCraft(rpd)) // everything must be set before this!!! From faa4e8101be1931347cffe2d790cb61c18e70497 Mon Sep 17 00:00:00 2001 From: red-kangaroo Date: Thu, 7 Oct 2021 17:48:37 +0200 Subject: [PATCH 232/235] Better favour names Plus some phrasing. --- Main/Source/command.cpp | 53 +++++++------- Main/Source/god.cpp | 14 ++-- Main/Source/gods.cpp | 155 ++++++++++++++++++++-------------------- 3 files changed, 113 insertions(+), 109 deletions(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 596065478..296ffa1bb 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -66,7 +66,7 @@ int command::GetKey() const if(Key4>0) return Key4; } - + switch(ivanconfig::GetDirectionKeyMap()) { case DIR_NORM: // Normal @@ -683,7 +683,7 @@ truth commandsystem::PickUp(character* Char) if(game::IsAutoPickupMatch(PileVector[0][c]->GetName(DEFINITE))){ PileVector[0][c]->ClearTag('d'); //intentionally drop tag dismissed for autopickup regex match } - + game::AutoStoreItemInContainer(PileVector[0][c],Char); } @@ -731,7 +731,7 @@ truth commandsystem::PickUp(character* Char) if(game::IsAutoPickupMatch(ToPickup[c]->GetName(DEFINITE))){ ToPickup[c]->ClearTag('d'); //intentionally drop tag dismissed for autopickup regex match } - + game::AutoStoreItemInContainer(ToPickup[c],Char); } @@ -1161,35 +1161,38 @@ truth commandsystem::AskFavour(character* Char) { felist felFavourList(CONST_S("Ask a favour from Whom?")); felFavourList.SetEntryDrawer(game::GodEntryDrawer); - + int iTot=0; std::vector> vSelectableFavours; for(int c = 1; c <= GODS; ++c){ god* pgod = game::GetGod(c); - if(!pgod->IsKnown())continue; - + if(!pgod->IsKnown()) continue; + bool bOk=false; - if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0)bOk=true; - if(pgod->GetBasicAlignment() == NEUTRAL && game::GetPlayerAlignment() == 0)bOk=true; - if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0)bOk=true; - if(c == Char->GetLSquareUnder()->GetDivineMaster())bOk=true; - + if(pgod->GetBasicAlignment() == GOOD && game::GetPlayerAlignment() > 0) bOk=true; + if(pgod->GetBasicAlignment() == NEUTRAL && game::GetPlayerAlignment() == 0) bOk=true; + if(pgod->GetBasicAlignment() == EVIL && game::GetPlayerAlignment() < 0) bOk=true; + if(c == Char->GetLSquareUnder()->GetDivineMaster()) bOk=true; + bool bGodSectionEntry=true; std::vector v = pgod->GetKnownSpells(); for(auto piFavour = v.begin(); piFavour != v.end(); ++piFavour){ festring fsFavour = CONST_S("") + god::GetFavourName(*piFavour); - + col16 col = bOk ? LIGHT_GRAY : DARK_GRAY; - if(!bOk && game::WizardModeIsReallyActive())col=RED; - + // if(!bOk && game::WizardModeIsReallyActive()) col=RED; + if(bGodSectionEntry){ - festring fsGodEntry = CONST_S("") + game::GetAlignment(pgod->GetAlignment()) + " " - + pgod->GetName()+" may grant you a favour."; - if(ivanconfig::IsShowGodInfo())fsGodEntry << " " << game::GetGod(c)->GetLastKnownRelation(); + festring fsGodEntry = CONST_S("") + game::GetAlignment(pgod->GetAlignment()); + fsGodEntry.Resize(4); // Longest alignment name is L++ or C--, so have min of one space. + fsGodEntry << pgod->GetName() + " might grant you a favour."; //TODO: won't for known gods with no favours, or only name? + if(ivanconfig::IsShowGodInfo()) + fsGodEntry << " " << game::GetGod(c)->GetLastKnownRelation(); felFavourList.AddEntry(fsGodEntry, DARK_GRAY, 20, c, false); bGodSectionEntry=false; } felFavourList.AddEntry(fsFavour, col, 0, NO_IMAGE, bOk || game::WizardModeIsReallyActive()); + // TODO: favour F1 Description if(bOk || game::WizardModeIsReallyActive()){ // std::pair GS; @@ -1197,17 +1200,19 @@ truth commandsystem::AskFavour(character* Char) // GS.second = *piSpell; vSelectableFavours.push_back(std::make_pair(pgod,*piFavour)); } - + iTot++; } } - + festring fsMsg; - fsMsg = fsMsg+"You don't know about any "+game::GetVerbalPlayerAlignment()+" favours..."; + //fsMsg = fsMsg+"You don't know about any "+game::GetVerbalPlayerAlignment()+" favours..."; //TODO: How exactly to phrase this? + fsMsg = fsMsg+"You can call upon no favours."; + if(iTot>0 && vSelectableFavours.size()==0){ felFavourList.AddEntry(cfestring("(")+fsMsg+")", DARK_GRAY, 0, NO_IMAGE, false); } - + int Select = LIST_WAS_EMPTY; if(iTot>0){ game::SetStandardListAttributes(felFavourList); @@ -1215,7 +1220,7 @@ truth commandsystem::AskFavour(character* Char) felFavourList.AddFlags(SELECTABLE); Select = felFavourList.Draw(); } - + if(Select == LIST_WAS_EMPTY || vSelectableFavours.size()==0) { // ADD_MESSAGE("You don't know about any %s favours...", game::GetVerbalPlayerAlignment()); @@ -1225,7 +1230,7 @@ truth commandsystem::AskFavour(character* Char) if(Select & FELIST_ERROR_BIT) return false; - + god* G = vSelectableFavours[Select].first; int iFavour = vSelectableFavours[Select].second; int iDebit=FAVOURDEBIT_AUTO; @@ -1241,7 +1246,7 @@ truth commandsystem::AskFavour(character* Char) Char->EditAP(-1000); return true; } - + return false; } diff --git a/Main/Source/god.cpp b/Main/Source/god.cpp index e4c7b9220..88968ca78 100644 --- a/Main/Source/god.cpp +++ b/Main/Source/god.cpp @@ -110,7 +110,7 @@ void god::Pray() } } } - + fsLastKnownRelation = PrintRelation(); } @@ -305,7 +305,7 @@ cfestring god::PrintRelation() const VerbalRelation = "you more than any other mortal."; } - ADD_MESSAGE("%s %s %s", GetName(), fsIs, VerbalRelation); + ADD_MESSAGE("%s %s %s", GetPersonalPronoun(), fsIs, VerbalRelation); festring fsLKR; fsLKR<GetAttachedGod() == GetType() ? 50 : 100; if(OfferValue > 0 && Relation > 250 && !(RAND() % RandModifier)) @@ -589,25 +589,25 @@ bool god::Favour(int iWhat, int iDebit) fsLastKnownRelation = PrintRelation(); return false; } - + if(Relation < iDebit){ // warns, punishes and provides a last favour before becoming negative relation ADD_MESSAGE("You hear a booming voice: \"Don't push your luck... puny mortal!\""); PrayBadEffect(); return true; } - + return true; } festring god::GetFavourName(int iID) { if(vFavID.size()==0) FavourInit(); - + for(auto FI = vFavID.begin(); FI != vFavID.end(); ++FI){ if(FI->first==iID) return FI->second; } - + ABORT("invalid favour ID %d",iID); return ""; //dummy } diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp index 570f64272..ed5c72e72 100644 --- a/Main/Source/gods.cpp +++ b/Main/Source/gods.cpp @@ -133,7 +133,7 @@ col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } * changing the order of these enums will mess importing old savegames (but wont break them) * prefer sorting on the initialization of the strings FavourInit() */ -enum eFavours { +enum eFavours { FAVOUR_CALLRAIN = 1, FAVOUR_CONFUSE, FAVOUR_CURELEPROSY, @@ -173,76 +173,71 @@ enum eFavours { void god::FavourInit() //this one is better on this file { - AddFavourID(FAVOUR_BURNENEMIES,"Burn your Enemies"); - AddFavourID(FAVOUR_CALLRAIN,"Make it Rain"); - AddFavourID(FAVOUR_CAUSEFEAR,"Your enemies will Fear you"); - AddFavourID(FAVOUR_CONFUSE,"Cause Confusion amongst your enemies"); + AddFavourID(FAVOUR_BURNENEMIES,"Immolation"); + AddFavourID(FAVOUR_CALLRAIN,"Call Rain"); + AddFavourID(FAVOUR_CAUSEFEAR,"Inspire Fear"); + AddFavourID(FAVOUR_CONFUSE,"Spread Confusion"); AddFavourID(FAVOUR_CURELEPROSY,"Cure Leprosy"); AddFavourID(FAVOUR_CURELYCANTHROPY,"Cure Lycanthropy"); - AddFavourID(FAVOUR_CUREMINDWORM,"Cure Mindworm"); + AddFavourID(FAVOUR_CUREMINDWORM,"Remove Brain Parasite"); AddFavourID(FAVOUR_CUREPOISON,"Cure Poison"); AddFavourID(FAVOUR_CURESLOWNESS,"Cure Slowness"); - AddFavourID(FAVOUR_CURETAPEWORM,"Cure Tapeworm"); + AddFavourID(FAVOUR_CURETAPEWORM,"Remove Stomach Parasite"); AddFavourID(FAVOUR_CUREVAMP,"Cure Vampirism"); - AddFavourID(FAVOUR_CUREWOUNDS,"Cure Wounds"); - AddFavourID(FAVOUR_DISEASEIMMUNITY,"Gain temporary Immunity to Diseases"); - AddFavourID(FAVOUR_EARTHQUAKE,"Invoke the rage of an Earth Quake"); + AddFavourID(FAVOUR_CUREWOUNDS,"Heal"); + AddFavourID(FAVOUR_DISEASEIMMUNITY,"Ward Off Disease"); + AddFavourID(FAVOUR_EARTHQUAKE,"Quake the Earth"); AddFavourID(FAVOUR_ENCHANT,"Enchant Equipment"); - AddFavourID(FAVOUR_ENRAGE,"Fill you with Rage"); - AddFavourID(FAVOUR_ETHEREALMOV,"Become Ethereal"); - AddFavourID(FAVOUR_EXTINGUISHFIRE,"Put out these Flames"); //TODO consider price vs FAVOUR_HEALBURNS); - AddFavourID(FAVOUR_FEED,"Calms your Hunger"); - AddFavourID(FAVOUR_FEELENEMIES,"Feel your Enemies"); - AddFavourID(FAVOUR_FIRESTORM,"Fiery Firestorm"); - AddFavourID(FAVOUR_FIXEQUIPMENT,"Fix one broken equipped item"); - AddFavourID(FAVOUR_HEALBURNS,"Heals your burns"); - AddFavourID(FAVOUR_HOLYGREN,"Paladin's Holy Grenade"); - AddFavourID(FAVOUR_INFRAVISION,"See in the Darkness"); + AddFavourID(FAVOUR_ENRAGE,"Second Wind"); + AddFavourID(FAVOUR_ETHEREALMOV,"Join the Shadows"); + AddFavourID(FAVOUR_EXTINGUISHFIRE,"Quench Flames"); //TODO: consider price vs FAVOUR_HEALBURNS + AddFavourID(FAVOUR_FEED,"Calm Hunger"); + AddFavourID(FAVOUR_FEELENEMIES,"Sense Thy Foes"); + AddFavourID(FAVOUR_FIRESTORM,"Holy Flames"); + AddFavourID(FAVOUR_FIXEQUIPMENT,"Repair Item"); + AddFavourID(FAVOUR_HEALBURNS,"Remove Burns"); + AddFavourID(FAVOUR_HOLYGREN,"Paladin's Gift"); + AddFavourID(FAVOUR_INFRAVISION,"See Thy Foes"); AddFavourID(FAVOUR_INVIGORATE,"Invigorate"); AddFavourID(FAVOUR_INVISIBILITY,"Become Invisible"); - AddFavourID(FAVOUR_POLYCONTROL,"Control what you are"); - AddFavourID(FAVOUR_SHOPPING,"Black Friday"); - AddFavourID(FAVOUR_SPEEDUP,"Make you Fast"); - AddFavourID(FAVOUR_STOPFIRE,"Unburn one Equipment"); - AddFavourID(FAVOUR_SUMMONWOLF,"Summon Wolf friend(s)"); - AddFavourID(FAVOUR_TAME,"Tame this Monster"); - AddFavourID(FAVOUR_TELEPCONTROL,"Decide where you are sent"); + AddFavourID(FAVOUR_POLYCONTROL,"Control Shape"); + AddFavourID(FAVOUR_SHOPPING,"Bounty"); //"Black Friday" + AddFavourID(FAVOUR_SPEEDUP,"Haste"); + AddFavourID(FAVOUR_STOPFIRE,"Repair Burns"); + AddFavourID(FAVOUR_SUMMONWOLF,"Summon Nature's Ally"); + AddFavourID(FAVOUR_TAME,"Song of Taming"); + AddFavourID(FAVOUR_TELEPCONTROL,"Control Warp"); AddFavourID(FAVOUR_TELEPORT,"Teleport"); } int god::CalcDebit(int iDebit,int iDefault){ if(iDebit!=0){ switch(iDebit){ - case FAVOURDEBIT_AUTO: iDebit=iDefault ;break; - case FAVOURDEBIT_AUTOHALF: iDebit=iDefault/2;break; - case FAVOURDEBIT_AUTODOUBLE: iDebit=iDefault*2;break; + case FAVOURDEBIT_AUTO: iDebit=iDefault; break; + case FAVOURDEBIT_AUTOHALF: iDebit=iDefault/2; break; + case FAVOURDEBIT_AUTODOUBLE: iDebit=iDefault*2; break; } - + // can ask more favours if very well aligned - if(game::GetPlayerAlignment() == game::GetGodAlignmentVsPlayer(this)){ + if(game::GetPlayerAlignment() == game::GetGodAlignmentVsPlayer(this)) iDebit/=2; - } - + /** * if enough time has passed, a normal pray could provide the favour freely * and even with relation benefits, so make it cheaper too, but not costless. */ - if(Timer==0){ + if(Timer==0) iDebit/=2; // /=3 too cheap? - } - + // skilled in manipulative praying :) - iDebit -= ( - (game::GetPlayer()->GetAttribute(MANA)*2.0) - + - game::GetPlayer()->GetAttribute(WISDOM) - ) / 3.0; - + iDebit -= game::GetPlayer()->GetAttribute(WISDOM); + /** - * max of 20 vafours (50*20=1000) (too much?) + * max of 20 vafours (50*20=1000) (too much?) * in the best case (master prayer) only */ - if(iDebit<50)iDebit=50; + if(iDebit<50) + iDebit=50; } return iDebit; } @@ -269,44 +264,46 @@ bool FavourTeleport(god* G) bool god::CallFavour(CallFavourType call, int iCallFavour, int iWhat, int iDebit, int iDbtDefault) { - if(iCallFavour!=iWhat)return false; - + if(iCallFavour!=iWhat) + return false; + if(iDebit==0) //came thru normal praying AddKnownSpell(knownSpellsID,iCallFavour); - - iDebit=CalcDebit(iDebit,iDbtDefault); - + + iDebit = CalcDebit(iDebit,iDbtDefault); + if(iDebit>0) if(!god::Favour(iWhat,iDebit)) return false; - + bool bWorked = false; if((*call)(this)){ if(iDebit>0){ //was a favour int iTm = 10000 - Relation*10; //by reaching here, Relation is always > 0 if(iTm<1000)iTm=1000; AdjustTimer(iTm); // this is a kind of debit too (counts against next safe pray time) - + LastPray=0; // to make it count as a pray too - + Relation-=iDebit; } bWorked = true; } - + fsLastKnownRelation = PrintRelation(); return bWorked; } /** - * + * * @param fsWhat * @param iDebit if -1 will be automatic - * @return + * @return */ bool sophos::Favour(int iWhat, int iDebit) { - if(CallFavour(&FavourTeleport,FAVOUR_TELEPORT,iWhat,iDebit,100))return true; + if(CallFavour(&FavourTeleport,FAVOUR_TELEPORT,iWhat,iDebit,100)) + return true; return false; } @@ -346,6 +343,8 @@ void sophos::PrayGoodEffect() DidHelp = true; } + //TODO: If still didn't help, reveal a bit of the level? Or detect material? + if(!DidHelp) ADD_MESSAGE("You hear a booming voice: \"Alas, I cannot help thee, mortal.\""); @@ -442,7 +441,7 @@ bool FavourExtinguishFire(god* G) bool FavourTame(god* G) { bool HasHelped = false; - + for(int d = 0; d < PLAYER->GetNeighbourSquares(); ++d) { square* Square = PLAYER->GetNeighbourSquare(d); @@ -483,7 +482,7 @@ bool FavourTame(god* G) } } } - + return HasHelped; } @@ -497,7 +496,7 @@ bool dulcis::Favour(int iWhat, int iDebit) void dulcis::PrayGoodEffect() { truth HasHelped = false; - + for(int d = 0; d < PLAYER->GetNeighbourSquares(); ++d) { square* Square = PLAYER->GetNeighbourSquare(d); @@ -535,7 +534,7 @@ void dulcis::PrayGoodEffect() HasHelped = Favour(FAVOUR_TAME); if(HasHelped) return; - + if (GetRelation() >= 50) { ADD_MESSAGE("You feel the music resonate within you.", GetName()); @@ -617,7 +616,7 @@ bool FavourFeed(god* G) PLAYER->SetNP(SATIATED_LEVEL); } - + return true; } bool FavourCureVampirism(god* G) @@ -721,7 +720,7 @@ bool FavourEnchantEquipment(god* G) item* PairEnchantable; int LowEnchant = 99; truth Pair = false; - + for(int c = 0; c < PLAYER->GetEquipments(); ++c) { item* Equipment = PLAYER->GetEquipment(c); @@ -760,7 +759,7 @@ bool FavourEnchantEquipment(god* G) return true; } } - + return false; } @@ -826,7 +825,7 @@ bool FavourCallRain(god* G) Square->LiquidRain(Beam, WATER); ADD_MESSAGE("Silva allows a little spell of gentle rain to pour down from above."); - + return true; } @@ -864,7 +863,7 @@ bool FavourSummonWolf(god* G) if(Created > 1) ADD_MESSAGE("Suddenly some tame wolves materialize around you."); - + return true; } @@ -874,7 +873,7 @@ bool silva::Favour(int iWhat, int iDebit) if(CallFavour(&FavourCallRain,FAVOUR_CALLRAIN,iWhat,iDebit,75))return true; if(CallFavour(&FavourEarthQuake,FAVOUR_EARTHQUAKE,iWhat,iDebit,500))return true; if(CallFavour(&FavourSummonWolf,FAVOUR_SUMMONWOLF,iWhat,iDebit,250))return true; - + return false; } @@ -928,7 +927,7 @@ bool FavourFixEquipment(god* G) break; } } - + return true; } @@ -948,7 +947,7 @@ bool FavourStopFire(god* G) break; } } - + return true; } @@ -956,7 +955,7 @@ bool loricatus::Favour(int iWhat, int iDebit) { if(CallFavour(&FavourFixEquipment,FAVOUR_FIXEQUIPMENT,iWhat,iDebit,250))return true; if(CallFavour(&FavourStopFire,FAVOUR_STOPFIRE,iWhat,iDebit,50))return true; - + return false; } @@ -1084,10 +1083,10 @@ int CalcDuration(god* G) { if(dynamic_cast(G)) return 200 * PLAYER->GetAttribute(WISDOM) + Max(G->GetRelation(), 0); - + if(dynamic_cast(G) || dynamic_cast(G)) return 300 * PLAYER->GetAttribute(WISDOM) + G->GetRelation() * 5; - + ABORT("duration calc for god %s is not available here!",G->GetName()); } bool FavourCureSlowness(god* G) @@ -1180,7 +1179,7 @@ bool FavourEtherealMov(god* G) else PLAYER->EditTemporaryStateCounter(ETHEREAL_MOVING, PLAYER->GetTemporaryStateCounter(ETHEREAL_MOVING) + (PLAYER->GetAttribute(WISDOM) * 100)); - + return true; } @@ -1246,7 +1245,7 @@ bool FavourShopping(god* G) ToBeDeleted->SendToHell(); OKItems.erase(std::find(OKItems.begin(), OKItems.end(), ToBeDeleted)); } - + return Success; } bool mellis::Favour(int iWhat, int iDebit) @@ -1664,7 +1663,7 @@ bool FavourBurnYourEnemies(god* G) } } } - + return Success; } @@ -1712,7 +1711,7 @@ void infuscor::PrayGoodEffect() if(!Success) { InfuscorFavourDuration = CalcDuration(this); - + if(!PLAYER->StateIsActivated(ESP) || PLAYER->GetTemporaryStateCounter(ESP) < InfuscorFavourDuration) { @@ -1790,7 +1789,7 @@ bool FavourCauseFear(god* G) return true; } - + return false; } bool cruentus::Favour(int iWhat, int iDebit) @@ -1830,7 +1829,7 @@ void cruentus::PrayGoodEffect() if(Favour(FAVOUR_CAUSEFEAR)) return; - + if(!RAND_2) { item* Weapon = PLAYER->GetMainWielded(); From b7719518f87781d50e163563cbf64eb8470fedcb Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 23 Jan 2022 13:07:40 -0300 Subject: [PATCH 233/235] Update database.h fixing merge --- Main/Include/database.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Main/Include/database.h b/Main/Include/database.h index 61ee26e6a..987c8ea53 100644 --- a/Main/Include/database.h +++ b/Main/Include/database.h @@ -36,7 +36,6 @@ template class databasecreator static void FindDataBase(const database*&, const prototype*, int); static truth InstallDataBaseIfPossible(type*, int, int); static void InstallDataBase(type*, int); - static truth InstallDataBaseIfPossible(type*, int, int); static void CreateDataBaseMemberMap(); static int CreateDivineConfigurations(const prototype*, database**, int); private: From 5048f49f474f6c9b702ecb049b899438dc5afe13 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Sun, 23 Jan 2022 13:12:58 -0300 Subject: [PATCH 234/235] Update command.cpp fixing merge --- Main/Source/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp index 15008b35d..4e4fad451 100644 --- a/Main/Source/command.cpp +++ b/Main/Source/command.cpp @@ -1186,7 +1186,7 @@ truth commandsystem::AskFavour(character* Char) if(bGodSectionEntry){ festring fsGodEntry = CONST_S("") + game::GetAlignment(pgod->GetAlignment()); fsGodEntry.Resize(4); // Longest alignment name is L++ or C--, so have min of one space. - fsGodEntry << pgod->GetName() + " might grant you a favour."; //TODO: won't for known gods with no favours, or only name? + fsGodEntry << pgod->GetName() << " might grant you a favour."; //TODO: won't for known gods with no favours, or only name? if(ivanconfig::IsShowGodInfo()) fsGodEntry << " " << game::GetGod(c)->GetLastKnownRelation(); felFavourList.AddEntry(fsGodEntry, DARK_GRAY, 20, c, false); From 691da54746e72bed6291120d072191cfaae93991 Mon Sep 17 00:00:00 2001 From: AquariusPower Date: Thu, 3 Feb 2022 20:34:19 -0300 Subject: [PATCH 235/235] Update game.cpp removed redundant checks --- Main/Source/game.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 5b6861660..5ae2c91ee 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -393,12 +393,7 @@ void game::InitScript() truth game::IsQuestItem(item* it) //dont protect against null item* it may be a problem outside here. { - return it->IsQuestItem() //TODO this line should suffice instead of this game::IsQuestItem() function - || it->IsHeadOfElpuri() - || it->IsGoldenEagleShirt() - || it->IsPetrussNut() - || it->IsTheAvatar() - || it->IsEncryptedScroll(); + return it->IsQuestItem(); //TODO this code (used outside here) should suffice instead of this game::IsQuestItem() whole function } void game::PrepareToClearNonVisibleSquaresAround(v2 v2SqrPos) {