Add in Comet Flare and remove restriction on Summon Comet and Solar Flare equip combination denying. Release Build 11357.

pull/65/head
sigonasr2 3 months ago
parent 050821b1d2
commit e0d58aef04
  1. 5
      Adventures in Lestoria Tests/BuffTests.cpp
  2. 52
      Adventures in Lestoria Tests/EngineTests.cpp
  3. 20
      Adventures in Lestoria Tests/ItemTests.cpp
  4. 4
      Adventures in Lestoria/Ability.cpp
  5. 2
      Adventures in Lestoria/Ability.h
  6. 1
      Adventures in Lestoria/AdventuresInLestoria.cpp
  7. 24
      Adventures in Lestoria/DEFINES.h
  8. 1
      Adventures in Lestoria/Effect.h
  9. 4
      Adventures in Lestoria/Item.cpp
  10. 15
      Adventures in Lestoria/Meteor.cpp
  11. 10
      Adventures in Lestoria/Player.cpp
  12. 43
      Adventures in Lestoria/Player.h
  13. 2
      Adventures in Lestoria/Version.h
  14. 3
      Adventures in Lestoria/Wizard.cpp
  15. BIN
      Adventures in Lestoria/assets/comet_flare.png
  16. BIN
      Adventures in Lestoria/assets/comet_flare.xcf
  17. 1
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  18. BIN
      Adventures in Lestoria/assets/gamepack.pak
  19. BIN
      x64/Release/Adventures in Lestoria.exe

@ -102,8 +102,9 @@ namespace BuffTests
SetupTestMonster();
SetupMockMap();
}
~BuffTest(){
testGame.release();
TEST_METHOD_CLEANUP(CleanupTests){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
}
TEST_METHOD(AddBuffMonsterCallbackExpireFunctionTest){
Monster&m{game->SpawnMonster({},MONSTER_DATA.at("TestName"))};

@ -38,21 +38,71 @@ All rights reserved.
#include "CppUnitTest.h"
#include "AdventuresInLestoria.h"
#include "util.h"
#include "Tutorial.h"
#include <random>
#include <format>
#include "ItemDrop.h"
#include "DamageNumber.h"
#include <ranges>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
INCLUDE_GFX
INCLUDE_ITEM_DATA
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_INITIALIZEGAMECONFIGURATIONS
extern std::mt19937 rng;
namespace EngineTests
{
TEST_CLASS(EngineTest)
{
public:
std::unique_ptr<AiL>testGame;
InputGroup testKeyboardInput;
Player*player;
HWButton*testKey;
TEST_METHOD_INITIALIZE(PlayerInitialize){
InitializeGameConfigurations();
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
testGame.reset(new AiL(true));
ItemAttribute::Initialize();
ItemInfo::InitializeItems();
testGame->InitializeGraphics();
testGame->InitializeClasses();
sig::Animation::InitializeAnimations();
testGame->InitializeDefaultKeybinds();
testGame->InitializePlayer();
sig::Animation::SetupPlayerAnimations();
Menu::InitializeMenus();
Tutorial::Initialize();
Stats::InitializeDamageReductionTable();
GameState::Initialize();
GameState::STATE=GameState::states.at(States::State::GAME_RUN);
#pragma region Setup a fake test map and test monster
game->MAP_DATA["CAMPAIGN_1_1"];
ItemDrop::ClearDrops();
MonsterData testMonsterData{"TestName","Test Monster",1000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
MONSTER_DATA["TestName"]=testMonsterData;
#pragma endregion
testGame->ResetLevelStates();
player=testGame->GetPlayer();
//Setup key "0" as a test input
testKeyboardInput.AddKeybind(Input{InputType::KEY,0});
testKey=testGame->GetKeyboardState(0);
testGame->olc_UpdateKeyFocus(true); //Force the game to be "focused" for tests. Required if we want keyboard inputs to work.
Menu::themes.SetInitialized();
GFX.SetInitialized();
DAMAGENUMBER_LIST.clear();
}
TEST_METHOD_CLEANUP(CleanupTests){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
}
TEST_METHOD(StripColorTest){
std::string noColCode{"Hello World!"};

@ -95,9 +95,6 @@ namespace ItemTests
Menu::themes.SetInitialized();
GFX.SetInitialized();
}
TEST_METHOD_CLEANUP(CleanupTests){
testGame->EndGame();
}
TEST_METHOD(ItemGiveTest){
Inventory::AddItem("Health Potion"s,3);
Assert::AreEqual(3U,Inventory::GetItemCount("Health Potion"s),L"Player has 3 Health Potions.");
@ -249,27 +246,10 @@ namespace ItemTests
}
}
TEST_METHOD(AccessoryAntiCompatibilityCheck){
std::weak_ptr<Item>summonCometRing{Inventory::AddItem("Ring of the Slime King"s)};
summonCometRing.lock()->EnchantItem("Summon Comet");
std::weak_ptr<Item>solarFlareRing{Inventory::AddItem("Ring of the Slime King"s)};
solarFlareRing.lock()->EnchantItem("Solar Flare");
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
std::weak_ptr<Item>extraRing2{Inventory::AddItem("Ring of the Slime King"s)};
Inventory::EquipItem(summonCometRing,EquipSlot::RING1);
Assert::AreEqual(false,Item::SelectedEquipIsDifferent(solarFlareRing,EquipSlot::RING2),L"The game should deny equipping the Solar Flare ring with the Summon Comet ring equipped.");
Inventory::EquipItem(summonCometRing,EquipSlot::RING2);
Assert::AreEqual(false,Item::SelectedEquipIsDifferent(solarFlareRing,EquipSlot::RING1),L"The game should deny equipping the Solar Flare ring with the Summon Comet ring equipped.");
Inventory::EquipItem(solarFlareRing,EquipSlot::RING2);
Assert::AreEqual(false,Item::SelectedEquipIsDifferent(summonCometRing,EquipSlot::RING1),L"The game should deny equipping the Summon Comet ring with the Solar Flare ring equipped.");
Inventory::EquipItem(solarFlareRing,EquipSlot::RING1);
Assert::AreEqual(false,Item::SelectedEquipIsDifferent(summonCometRing,EquipSlot::RING2),L"The game should deny equipping the Summon Comet ring with the Solar Flare ring equipped.");
Inventory::EquipItem(extraRing,EquipSlot::RING1);
Assert::AreEqual(true,Item::SelectedEquipIsDifferent(summonCometRing,EquipSlot::RING2),L"The game should allow equipping the Summon Comet ring when the Solar Flare ring is not present.");
Assert::AreEqual(true,Item::SelectedEquipIsDifferent(solarFlareRing,EquipSlot::RING2),L"The game should allow equipping the Solar Flare ring when the Summon Comet ring is not present.");
Inventory::EquipItem(extraRing,EquipSlot::RING2);
Assert::AreEqual(true,Item::SelectedEquipIsDifferent(summonCometRing,EquipSlot::RING1),L"The game should allow equipping the Summon Comet ring when the Solar Flare ring is not present.");
Assert::AreEqual(true,Item::SelectedEquipIsDifferent(solarFlareRing,EquipSlot::RING1),L"The game should allow equipping the Solar Flare ring when the Summon Comet ring is not present.");
Assert::AreEqual(true,Item::SelectedEquipIsDifferent(extraRing2,EquipSlot::RING1),L"The game should allow equipping of any two normal rings that are not the same ring.");
Assert::AreEqual(false,Item::SelectedEquipIsDifferent(extraRing,EquipSlot::RING1),L"The game should not allow equipping the same ring if it's already equipped.");
Inventory::UnequipItem(EquipSlot::RING2);

@ -57,3 +57,7 @@ Ability::Ability(std::string name,std::string shortName,std::string description,
const float Ability::GetCooldownTime()const{
return COOLDOWN_TIME;
}
const bool Ability::operator==(const Ability&a)const{
return name==a.name&&isOriginalAbility==a.isOriginalAbility;
}

@ -76,12 +76,14 @@ struct Ability{
bool actionPerformedDuringCast=false;
bool waitForRelease=false;
bool keyReleaseRequiredToReactivate{false}; //When the player presses an ability, they cannot use it again until they have released the key to press it down again. So this gets set to true everytime an ability activates, and set to false once the player releases that key.
bool isOriginalAbility{false};
//Ability action function, returns true if the ability can be casted, otherwise returns false.
// Argument 1: Player* - player pointer
// Argument 2: vf2d - The returned precast target position (if the ability needs to be aimed, otherwise {})
std::function<bool(Player*,vf2d)>action=[](Player*,vf2d){return false;};
static InputGroup DEFAULT;
const float GetCooldownTime()const;
const bool operator==(const Ability&a)const;
Ability();
//NOTE: icon expects the actual name relative to the "Ability Icons" directory for this constructor!
Ability(std::string name,std::string shortName,std::string description,float cooldownTime,int manaCost,InputGroup*input,std::string icon,Pixel barColor1=VERY_DARK_RED,Pixel barColor2=DARK_RED,PrecastData precastInfo={},bool canCancelCast=false);

@ -2990,6 +2990,7 @@ void AiL::ChangePlayerClass(Class cl){
}
void AiL::InitializeClasses(){
Player::ABILITY_LIST.clear();
Warrior::Initialize();
Thief::Initialize();
Ranger::Initialize();

@ -86,11 +86,16 @@ class::class(Player*player) \
:Player::Player(player){} \
Class class::GetClass(){return cl;} \
void class::CreateOriginalCopies(){ \
original_rightClickAbility=rightClickAbility; \
original_ability1=ability1; \
original_ability2=ability2; \
original_ability3=ability3; \
original_ability4=ability4; \
original_rightClickAbility=rightClickAbility; \
original_ability1=ability1; \
original_ability2=ability2; \
original_ability3=ability3; \
original_ability4=ability4; \
original_rightClickAbility.isOriginalAbility=true; \
original_ability1.isOriginalAbility=true; \
original_ability2.isOriginalAbility=true; \
original_ability3.isOriginalAbility=true; \
original_ability4.isOriginalAbility=true; \
} \
void class::ResetToOriginalAbilities(){ \
rightClickAbility.COOLDOWN_TIME=original_rightClickAbility.COOLDOWN_TIME; \
@ -115,6 +120,15 @@ Ability&class::GetAbility1(){return ability1;}; \
Ability&class::GetAbility2(){return ability2;}; \
Ability&class::GetAbility3(){return ability3;}; \
Ability&class::GetAbility4(){return ability4;}; \
Ability&class::GetOriginalAbility1(){return original_ability1;}; \
Ability&class::GetOriginalAbility2(){return original_ability2;}; \
Ability&class::GetOriginalAbility3(){return original_ability3;}; \
Ability&class::GetOriginalRightClickAbility(){return original_rightClickAbility;}; \
void class::SetAbility4(const Ability&originalAbility){ \
auto it=std::find(Player::ABILITY_LIST.begin(),Player::ABILITY_LIST.end(),originalAbility); \
if(it==Player::ABILITY_LIST.end())ERR(std::format("WARNING! Could not find ability {} in original ability list! This function MUST be supplied with an original ability. Was original ability: {}",originalAbility.name,originalAbility.isOriginalAbility)); \
class::original_ability4=class::ability4=originalAbility; \
} \
std::string&class::GetWalkNAnimation(){return walk_n;}; \
std::string&class::GetWalkEAnimation(){return walk_e;}; \
std::string&class::GetWalkSAnimation(){return walk_s;}; \

@ -100,6 +100,7 @@ struct Meteor:Effect{
METEOR,
COMET,
SOLAR_FLARE,
COMET_FLARE,
};
Meteor(const vf2d pos,const float lifetime,const bool upperLevel,const MeteorSetting setting,const float fadeout=0.0f,const vf2d spd={},const Pixel col=WHITE,const float rotation=0,const float rotationSpd=0,const bool additiveBlending=false);
float startLifetime=0;

@ -1482,8 +1482,8 @@ const bool Item::SelectedEquipIsDifferent(const std::weak_ptr<Item>equipAttempti
const auto OneOfBothRingsDoesNotHaveEnchant=[&equip=equipAttemptingToEquip,&otherItem](){
return !equip.lock()->HasEnchant()||!otherItem.lock()->HasEnchant();
};
const auto BothRingsAreCompatible=[&equip=equipAttemptingToEquip,&otherItem](){
return !(equip.lock()->GetEnchant().value().Name()=="Summon Comet"&&otherItem.lock()->GetEnchant().value().Name()=="Solar Flare")&&!(equip.lock()->GetEnchant().value().Name()=="Solar Flare"&&otherItem.lock()->GetEnchant().value().Name()=="Summon Comet");
const auto BothRingsAreCompatible=[&equip=equipAttemptingToEquip,&otherItem](){ //Check if enchants are compatible... Currently unused.
return true;
};
return (ISBLANK(otherItem)&&(OneOfBothRingsDoesNotHaveEnchant()||BothRingsAreCompatible())||
EquipIsNotSameAsOppositeRingSlot()&&

@ -47,7 +47,7 @@ INCLUDE_MONSTER_LIST
INCLUDE_GFX
Meteor::Meteor(const vf2d pos,const float lifetime,const bool upperLevel,const MeteorSetting setting,const float fadeout,const vf2d spd,const Pixel col,const float rotation,const float rotationSpd,const bool additiveBlending)
:Effect(pos,lifetime,setting==COMET?"comet.png":"meteor.png",upperLevel,{},fadeout,spd,col,rotation,rotationSpd,additiveBlending),startLifetime(lifetime){
:Effect(pos,lifetime,setting==COMET?"comet.png":setting==COMET_FLARE?"comet_flare.png":"meteor.png",upperLevel,{},fadeout,spd,col,rotation,rotationSpd,additiveBlending),startLifetime(lifetime){
switch(setting){
case METEOR:{
meteorImpactParticles="Wizard.Ability 3.MeteorImpactParticles"_I;
@ -87,6 +87,19 @@ Meteor::Meteor(const vf2d pos,const float lifetime,const bool upperLevel,const M
meteorCrashSFX="Wizard Meteor";
this->col=RED;
}break;
case COMET_FLARE:{
meteorImpactParticles="Wizard.Ability 3.MeteorImpactParticles"_I*4;
randomColorTintR=util::random_range(128,255);
randomColorTintG=util::random_range(128,255);
randomColorTintB=util::random_range(128,255);
meteorRadius="Wizard.Ability 3.MeteorRadius"_F/100*24*2;
fallSpdMult=1.5f;
size=vf2d{meteorRadius/96,meteorRadius/96};
damageMult="Solar Flare"_ENC["METEOR ATTACK"]/100.f;
damageMult*=1.f-"Summon Comet"_ENC["DAMAGE REDUCTION PCT"]/100.f;
fireRingLifetime="Wizard.Ability 3.FireRingLifetime"_F*1.5f;
meteorCrashSFX="Wizard Meteor";
}break;
}
this->lifetime/=fallSpdMult;
startLifetime=this->lifetime;

@ -72,6 +72,7 @@ INCLUDE_DAMAGENUMBER_LIST
INCLUDE_game
INCLUDE_DATA
std::vector<std::reference_wrapper<Ability>>Player::ABILITY_LIST;
float Player::GROUND_SLAM_SPIN_TIME=0.6f;
const bool Player::INVERTED=true;
const bool Player::USE_WALK_DIR=true;
@ -1538,11 +1539,13 @@ void Player::RecalculateEquipStats(){
if(GetClass()&WIZARD){
if(HasEnchant("Blink Portal"))GetRightClickAbility().COOLDOWN_TIME="Blink Portal"_ENC["COOLDOWN"];
if(HasEnchant("Summon Comet")){
GetAbility3().precastInfo.castTime="Summon Comet"_ENC["METEOR CAST TIME"];
float castTimeDiff{GetOriginalAbility3().precastInfo.castTime-"Summon Comet"_ENC["METEOR CAST TIME"]};
GetAbility3().precastInfo.castTime-=castTimeDiff;
GetAbility3().COOLDOWN_TIME+="Summon Comet"_ENC["COOLDOWN REDUCTION"]; //This is not a typo, we add because the cooldown reduction in the config is NEGATIVE!
}
if(HasEnchant("Solar Flare")){
GetAbility3().precastInfo.castTime="Solar Flare"_ENC["METEOR CAST TIME"];
float castTimeDiff{GetOriginalAbility3().precastInfo.castTime-"Solar Flare"_ENC["METEOR CAST TIME"]};
GetAbility3().precastInfo.castTime-=castTimeDiff;
GetAbility3().COOLDOWN_TIME+="Solar Flare"_ENC["COOLDOWN INCREASE"];
}
}
@ -2285,3 +2288,6 @@ void Player::UpdateAnimationStates(){
void Player::SetTestScreenAimingLocation(vf2d forcedAimingLoc){
testAimingLoc=forcedAimingLoc;
}
void Player::SetAbility4(const Ability&ability){
}

@ -108,7 +108,6 @@ class Player{
friend class ItemInfo;
friend void ItemOverlay::Draw();
public:
Player();
//So this is rather fascinating and only exists because we have the ability to change classes which means we need to initialize a class
//using a new object type... Because of that we'll take the pointer reference to the old object and copy some of its properties to this new
@ -231,6 +230,11 @@ public:
virtual Ability&GetAbility2()=0;
virtual Ability&GetAbility3()=0;
virtual Ability&GetAbility4()=0;
virtual void SetAbility4(const Ability&originalAbility)=0; //NOTE: Make sure to provide the original ability and not a current ability!
virtual Ability&GetOriginalAbility1()=0;
virtual Ability&GetOriginalAbility2()=0;
virtual Ability&GetOriginalAbility3()=0;
virtual Ability&GetOriginalRightClickAbility()=0;
virtual std::string&GetWalkNAnimation()=0;
virtual std::string&GetWalkEAnimation()=0;
virtual std::string&GetWalkSAnimation()=0;
@ -337,6 +341,7 @@ public:
void PrepareCast(Ability&ability); //NOTE: This usually is not called unless we are running tests! PrepareCast is meant to be used before we use CastSpell in unit tests.
void SetTestScreenAimingLocation(vf2d forcedAimingLoc);
private:
static std::vector<std::reference_wrapper<Ability>>ABILITY_LIST;
const int SHIELD_CAPACITY{32};
int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I;
@ -502,6 +507,11 @@ struct Warrior:Player{
Ability&GetAbility2()override;
Ability&GetAbility3()override;
Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override;
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override;
std::string&GetWalkSAnimation()override;
@ -537,6 +547,11 @@ struct Thief:Player{
Ability&GetAbility2()override;
Ability&GetAbility3()override;
Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override;
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override;
std::string&GetWalkSAnimation()override;
@ -572,6 +587,11 @@ struct Ranger:Player{
Ability&GetAbility2()override;
Ability&GetAbility3()override;
Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override;
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override;
std::string&GetWalkSAnimation()override;
@ -607,6 +627,11 @@ struct Trapper:Player{
Ability&GetAbility2()override;
Ability&GetAbility3()override;
Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override;
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override;
std::string&GetWalkSAnimation()override;
@ -642,6 +667,11 @@ struct Wizard:Player{
Ability&GetAbility2()override;
Ability&GetAbility3()override;
Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override;
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override;
std::string&GetWalkSAnimation()override;
@ -677,6 +707,11 @@ struct Witch:Player{
Ability&GetAbility2()override;
Ability&GetAbility3()override;
Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override;
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override;
std::string&GetWalkSAnimation()override;
@ -747,4 +782,8 @@ private:
{#class".Ability 3.Precast Time"_F,#class".Ability 3.Casting Range"_I/100.f*24,#class".Ability 3.Casting Size"_I/100.f*24}, \
bool(#class".Ability 3.CancelCast"_I) \
}; \
class::ability4;
class::ability4; \
Player::ABILITY_LIST.emplace_back(class::original_rightClickAbility); \
Player::ABILITY_LIST.emplace_back(class::original_ability1); \
Player::ABILITY_LIST.emplace_back(class::original_ability2); \
Player::ABILITY_LIST.emplace_back(class::original_ability3);

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 5
#define VERSION_BUILD 11350
#define VERSION_BUILD 11357
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -201,7 +201,8 @@ void Wizard::InitializeClassAbilities(){
Wizard::ability3.action=
[](Player*p,vf2d pos={}){
Meteor::MeteorSetting meteorType{Meteor::METEOR};
if(p->HasEnchant("Summon Comet"))meteorType=Meteor::COMET;
if(p->HasEnchant("Summon Comet")&&p->HasEnchant("Solar Flare"))meteorType=Meteor::COMET_FLARE;
else if(p->HasEnchant("Summon Comet"))meteorType=Meteor::COMET;
else if(p->HasEnchant("Solar Flare"))meteorType=Meteor::SOLAR_FLARE;
game->AddEffect(std::make_unique<Meteor>(pos,3,p->OnUpperLevel(),meteorType,"Wizard.Ability 3.MeteorFadeoutTime"_F));
return true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

@ -129,6 +129,7 @@ Images
GFX_Portal = portal.png
GFX_Flames = FlamesTexture.png
GFX_Comet = comet.png
GFX_CometFlare = comet_flare.png
GFX_Thief_Sheet = nico-thief.png
GFX_Trapper_Sheet = nico-trapper.png

Loading…
Cancel
Save