Separate ThrowingProjectiles from PoisonBottle class to create generic throwable item bullets. Added throwable projectile item script. Made Bomb item functional. Release Build 11748.
This commit is contained in:
parent
8c3dd0f071
commit
868a089666
@ -552,5 +552,8 @@ namespace MonsterTests
|
|||||||
Game::Update(1.f);
|
Game::Update(1.f);
|
||||||
Assert::AreEqual(int(game->GetPlayer()->GetMaxHealth()-parrot.GetCollisionDamage()),game->GetPlayer()->GetHealth(),L"Player should take collision damage from the parrot.");
|
Assert::AreEqual(int(game->GetPlayer()->GetMaxHealth()-parrot.GetCollisionDamage()),game->GetPlayer()->GetHealth(),L"Player should take collision damage from the parrot.");
|
||||||
}
|
}
|
||||||
|
TEST_METHOD(MonsterCollisionRadiusSizeTest){
|
||||||
|
Monster&parrot{game->SpawnMonster({},MONSTER_DATA.at("Parrot"))};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -993,6 +993,10 @@
|
|||||||
</SubType>
|
</SubType>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ItemLoadoutWindow.cpp" />
|
<ClCompile Include="ItemLoadoutWindow.cpp" />
|
||||||
|
<ClCompile Include="ItemScript.cpp">
|
||||||
|
<SubType>
|
||||||
|
</SubType>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="Key.cpp" />
|
<ClCompile Include="Key.cpp" />
|
||||||
<ClCompile Include="LargeStone.cpp" />
|
<ClCompile Include="LargeStone.cpp" />
|
||||||
<ClCompile Include="LargeTornado.cpp">
|
<ClCompile Include="LargeTornado.cpp">
|
||||||
@ -1071,6 +1075,10 @@
|
|||||||
<SubType>
|
<SubType>
|
||||||
</SubType>
|
</SubType>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="ThrownProjectile.cpp">
|
||||||
|
<SubType>
|
||||||
|
</SubType>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="PoisonPool.cpp">
|
<ClCompile Include="PoisonPool.cpp">
|
||||||
<SubType>
|
<SubType>
|
||||||
</SubType>
|
</SubType>
|
||||||
|
@ -1190,7 +1190,7 @@
|
|||||||
<ClCompile Include="PurpleEnergyBall.cpp">
|
<ClCompile Include="PurpleEnergyBall.cpp">
|
||||||
<Filter>Source Files\Bullet Types</Filter>
|
<Filter>Source Files\Bullet Types</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="PoisonBottle.cpp">
|
<ClCompile Include="ThrownProjectile.cpp">
|
||||||
<Filter>Source Files\Bullet Types</Filter>
|
<Filter>Source Files\Bullet Types</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="State_Dialog.cpp">
|
<ClCompile Include="State_Dialog.cpp">
|
||||||
@ -1253,6 +1253,12 @@
|
|||||||
<ClCompile Include="GiantCrab.cpp">
|
<ClCompile Include="GiantCrab.cpp">
|
||||||
<Filter>Source Files\Monster Strategies</Filter>
|
<Filter>Source Files\Monster Strategies</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="ItemScript.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="PoisonBottle.cpp">
|
||||||
|
<Filter>Source Files\Bullet Types</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="cpp.hint" />
|
<None Include="cpp.hint" />
|
||||||
|
@ -3929,28 +3929,28 @@ void AiL::SetLoadoutItem(int slot,std::string itemName){
|
|||||||
inputGroup=&Player::KEY_ITEM3;
|
inputGroup=&Player::KEY_ITEM3;
|
||||||
}break;
|
}break;
|
||||||
}
|
}
|
||||||
Ability itemAbility{itemName,"","","Item.Item Cooldown Time"_F,0,inputGroup,"items/"+itemName+".png",VERY_DARK_RED,DARK_RED,PrecastData{GetLoadoutItem(slot).lock()->CastTime(),0,0},true};
|
Ability itemAbility{itemName,"","","Item.Item Cooldown Time"_F,0,inputGroup,"items/"+itemName+".png",VERY_DARK_RED,DARK_RED,PrecastData{GetLoadoutItem(slot).lock()->CastTime(),GetLoadoutItem(slot).lock()->CastRange(),GetLoadoutItem(slot).lock()->CastSize()},true};
|
||||||
itemAbility.actionPerformedDuringCast=GetLoadoutItem(slot).lock()->UseDuringCast();
|
itemAbility.actionPerformedDuringCast=GetLoadoutItem(slot).lock()->UseDuringCast();
|
||||||
itemAbility.itemAbility=true;
|
itemAbility.itemAbility=true;
|
||||||
|
|
||||||
switch(slot){
|
switch(slot){
|
||||||
case 0:{
|
case 0:{
|
||||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||||
return game->UseLoadoutItem(0);
|
return game->UseLoadoutItem(0,pos);
|
||||||
};
|
};
|
||||||
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
||||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(loadout[slot]);
|
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(loadout[slot]);
|
||||||
}break;
|
}break;
|
||||||
case 1:{
|
case 1:{
|
||||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||||
return game->UseLoadoutItem(1);
|
return game->UseLoadoutItem(1,pos);
|
||||||
};
|
};
|
||||||
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
||||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(loadout[slot]);
|
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(loadout[slot]);
|
||||||
}break;
|
}break;
|
||||||
case 2:{
|
case 2:{
|
||||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||||
return game->UseLoadoutItem(2);
|
return game->UseLoadoutItem(2,pos);
|
||||||
};
|
};
|
||||||
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
||||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(loadout[slot]);
|
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(loadout[slot]);
|
||||||
@ -3974,11 +3974,11 @@ void AiL::SetLoadoutItem(int slot,std::string itemName){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AiL::UseLoadoutItem(int slot){
|
bool AiL::UseLoadoutItem(int slot,const std::optional<vf2d>targetingPos){
|
||||||
if(slot<0||slot>GetLoadoutSize()-1)ERR("Invalid inventory slot "+std::to_string(slot)+", please choose a slot in range (0-"+std::to_string(GetLoadoutSize()-1)+").");
|
if(slot<0||slot>GetLoadoutSize()-1)ERR("Invalid inventory slot "+std::to_string(slot)+", please choose a slot in range (0-"+std::to_string(GetLoadoutSize()-1)+").");
|
||||||
if(!ISBLANK(GetLoadoutItem(slot).lock())&&GetLoadoutItem(slot).lock()->Amt()>0){
|
if(!ISBLANK(GetLoadoutItem(slot).lock())&&GetLoadoutItem(slot).lock()->Amt()>0){
|
||||||
Tutorial::GetTask(TutorialTaskName::USE_RECOVERY_ITEMS).I(A::ITEM_USE_COUNT)++;
|
Tutorial::GetTask(TutorialTaskName::USE_RECOVERY_ITEMS).I(A::ITEM_USE_COUNT)++;
|
||||||
Inventory::UseItem(GetLoadoutItem(slot).lock()->ActualName());
|
Inventory::UseItem(GetLoadoutItem(slot).lock()->ActualName(),1U,targetingPos);
|
||||||
Inventory::AddLoadoutItemUsed(GetLoadoutItem(slot).lock()->ActualName(),slot);
|
Inventory::AddLoadoutItemUsed(GetLoadoutItem(slot).lock()->ActualName(),slot);
|
||||||
GetLoadoutItem(slot).lock()->amt--;
|
GetLoadoutItem(slot).lock()->amt--;
|
||||||
if(GetLoadoutItem(slot).lock()->UseSound().length()>0){
|
if(GetLoadoutItem(slot).lock()->UseSound().length()>0){
|
||||||
@ -4008,7 +4008,7 @@ void AiL::ClearLoadoutItem(int slot){
|
|||||||
switch(slot){
|
switch(slot){
|
||||||
case 0:{
|
case 0:{
|
||||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||||
return game->UseLoadoutItem(0);
|
return game->UseLoadoutItem(0,pos);
|
||||||
};
|
};
|
||||||
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
||||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(Item::BLANK);
|
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(Item::BLANK);
|
||||||
@ -4016,7 +4016,7 @@ void AiL::ClearLoadoutItem(int slot){
|
|||||||
}break;
|
}break;
|
||||||
case 1:{
|
case 1:{
|
||||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||||
return game->UseLoadoutItem(1);
|
return game->UseLoadoutItem(1,pos);
|
||||||
};
|
};
|
||||||
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
||||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(Item::BLANK);
|
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(Item::BLANK);
|
||||||
@ -4024,7 +4024,7 @@ void AiL::ClearLoadoutItem(int slot){
|
|||||||
}break;
|
}break;
|
||||||
case 2:{
|
case 2:{
|
||||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||||
return game->UseLoadoutItem(2);
|
return game->UseLoadoutItem(2,pos);
|
||||||
};
|
};
|
||||||
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
||||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(Item::BLANK);
|
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(Item::BLANK);
|
||||||
|
@ -363,7 +363,7 @@ public:
|
|||||||
int GetLoadoutSize()const;
|
int GetLoadoutSize()const;
|
||||||
void RestockLoadoutItems();
|
void RestockLoadoutItems();
|
||||||
//Returns true if the item can be used (we have >0 of it)
|
//Returns true if the item can be used (we have >0 of it)
|
||||||
bool UseLoadoutItem(int slot);
|
bool UseLoadoutItem(int slot,const std::optional<vf2d>targetingPos);
|
||||||
//Blanks out this loadout item.
|
//Blanks out this loadout item.
|
||||||
void ClearLoadoutItem(int slot);
|
void ClearLoadoutItem(int slot);
|
||||||
void RenderFadeout();
|
void RenderFadeout();
|
||||||
|
@ -402,6 +402,7 @@ void sig::Animation::InitializeAnimations(){
|
|||||||
CreateHorizontalAnimationSequence("tornado2.png",4,{24,48},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
CreateHorizontalAnimationSequence("tornado2.png",4,{24,48},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
||||||
CreateHorizontalAnimationSequence("large_tornado.png",4,{72,144},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
CreateHorizontalAnimationSequence("large_tornado.png",4,{72,144},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
||||||
CreateHorizontalAnimationSequence("sand_suction.png",4,{72,72},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::Repeat}});
|
CreateHorizontalAnimationSequence("sand_suction.png",4,{72,72},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::Repeat}});
|
||||||
|
CreateHorizontalAnimationSequence("bomb.png",4,{24,24},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::OneShot}});
|
||||||
|
|
||||||
CreateStillAnimation("meteor.png",{192,192});
|
CreateStillAnimation("meteor.png",{192,192});
|
||||||
|
|
||||||
|
@ -351,19 +351,32 @@ private:
|
|||||||
std::optional<std::weak_ptr<Monster>>homingTarget;
|
std::optional<std::weak_ptr<Monster>>homingTarget;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PoisonBottle:public Bullet{
|
struct ThrownProjectile:public Bullet{
|
||||||
PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
|
ThrownProjectile(vf2d pos,vf2d targetPos,const std::string&img,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f},const std::optional<Effect>explodeEffect={},const std::optional<std::string>explodeSoundEffect={});
|
||||||
void Update(float fElapsedTime)override;
|
void Update(float fElapsedTime)override;
|
||||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||||
private:
|
void _OnGroundLand();
|
||||||
|
virtual void OnGroundLand(); //This is called when the projectile lands, damage is not dealt yet, default behavior just deals damage in the area.
|
||||||
|
protected:
|
||||||
const vf2d targetPos;
|
const vf2d targetPos;
|
||||||
const vf2d startingPos;
|
const vf2d startingPos;
|
||||||
const float totalFallTime;
|
const float totalFallTime;
|
||||||
const float originalRisingTime,originalFallingTime;
|
float originalRisingTime;
|
||||||
|
float originalFallingTime;
|
||||||
float risingTime,fallingTime;
|
float risingTime,fallingTime;
|
||||||
const float initialZ;
|
const float initialZ;
|
||||||
const float totalRiseZAmt;
|
const float totalRiseZAmt;
|
||||||
const float explodeRadius;
|
const float explodeRadius;
|
||||||
|
const std::optional<Effect>explodeEffect;
|
||||||
|
const std::optional<std::string>explodeSoundEffect;
|
||||||
|
const std::string img;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PoisonBottle:public ThrownProjectile{
|
||||||
|
PoisonBottle(vf2d pos,vf2d targetPos,const std::string&img,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
|
||||||
|
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||||
|
virtual void OnGroundLand()override final; //This is called when the projectile lands, damage is not dealt yet, default behavior just deals damage in the area.
|
||||||
|
private:
|
||||||
const float bounceExplodeRadius;
|
const float bounceExplodeRadius;
|
||||||
int additionalBounceCount;
|
int additionalBounceCount;
|
||||||
};
|
};
|
@ -63,6 +63,7 @@ using MonsterSpawnerID=int;
|
|||||||
#define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS;
|
#define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS;
|
||||||
#define INCLUDE_ADMIN_MODE extern bool ADMIN_MODE;
|
#define INCLUDE_ADMIN_MODE extern bool ADMIN_MODE;
|
||||||
#define INCLUDE_DEMO_BUILD extern bool DEMO_BUILD;
|
#define INCLUDE_DEMO_BUILD extern bool DEMO_BUILD;
|
||||||
|
#define INCLUDE_ITEM_SCRIPTS extern safemap<std::string,ItemScript>ITEM_SCRIPTS;
|
||||||
|
|
||||||
#define INCLUDE_PACK_KEY extern std::string PACK_KEY;
|
#define INCLUDE_PACK_KEY extern std::string PACK_KEY;
|
||||||
|
|
||||||
|
@ -142,6 +142,8 @@ void ItemInfo::InitializeItems(){
|
|||||||
std::string scriptName="",description="",category="";
|
std::string scriptName="",description="",category="";
|
||||||
std::string setName="";
|
std::string setName="";
|
||||||
float castTime=0;
|
float castTime=0;
|
||||||
|
float castRange=0;
|
||||||
|
float castSize=0;
|
||||||
std::vector<std::string> slot;
|
std::vector<std::string> slot;
|
||||||
float cooldownTime="Item.Item Cooldown Time"_F;
|
float cooldownTime="Item.Item Cooldown Time"_F;
|
||||||
std::vector<ItemAttribute>statValueList;
|
std::vector<ItemAttribute>statValueList;
|
||||||
@ -167,6 +169,12 @@ void ItemInfo::InitializeItems(){
|
|||||||
}else
|
}else
|
||||||
if(keyName=="Cast Time"){
|
if(keyName=="Cast Time"){
|
||||||
castTime=float(data[key][keyName].GetReal());
|
castTime=float(data[key][keyName].GetReal());
|
||||||
|
}
|
||||||
|
if(keyName=="Cast Range"){
|
||||||
|
castRange=float(data[key][keyName].GetReal());
|
||||||
|
}else
|
||||||
|
if(keyName=="Cast Size"){
|
||||||
|
castSize=float(data[key][keyName].GetReal())/100.f*24;
|
||||||
}else
|
}else
|
||||||
if(keyName=="Cooldown Time"){
|
if(keyName=="Cooldown Time"){
|
||||||
cooldownTime=float(data[key][keyName].GetReal());
|
cooldownTime=float(data[key][keyName].GetReal());
|
||||||
@ -302,6 +310,8 @@ void ItemInfo::InitializeItems(){
|
|||||||
it.description=description;
|
it.description=description;
|
||||||
it.category=category;
|
it.category=category;
|
||||||
it.castTime=castTime;
|
it.castTime=castTime;
|
||||||
|
it.castRange=castRange;
|
||||||
|
it.castSize=castSize;
|
||||||
it.cooldownTime=cooldownTime;
|
it.cooldownTime=cooldownTime;
|
||||||
it.slot=EquipSlot::NONE;
|
it.slot=EquipSlot::NONE;
|
||||||
it.set=setName;
|
it.set=setName;
|
||||||
@ -452,51 +462,6 @@ const std::unordered_map<std::string,BuffOverTimeType::BuffOverTimeType>ItemInfo
|
|||||||
{"MP % Restore",BuffOverTimeType::MP_PCT_RESTORATION},
|
{"MP % Restore",BuffOverTimeType::MP_PCT_RESTORATION},
|
||||||
};
|
};
|
||||||
|
|
||||||
void ItemInfo::InitializeScripts(){
|
|
||||||
|
|
||||||
ITEM_SCRIPTS["Restore"]=[](AiL*game,ItemProps props){
|
|
||||||
for(const auto&[propName,buffType]:NameToBuffType){
|
|
||||||
int restoreAmt=props.GetIntProp(propName);
|
|
||||||
|
|
||||||
game->GetPlayer()->AddBuff(BuffRestorationType::ONE_OFF,NameToBuffType.at(propName),0.01f,float(restoreAmt),0.0f);
|
|
||||||
if(restoreAmt>0&&props.PropCount(propName)==3){
|
|
||||||
game->GetPlayer()->AddBuff(BuffRestorationType::OVER_TIME,NameToBuffType.at(propName),props.GetFloatProp(propName,2),float(restoreAmt),props.GetFloatProp(propName,1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
for(auto&[key,value]:ItemAttribute::attributes){
|
|
||||||
if(!DATA.GetProperty("ItemScript.Buff").HasProperty(key)){
|
|
||||||
ERR("WARNING! Buff Item Script does not support Buff "<<std::quoted(value.Name())<<"!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ITEM_SCRIPTS["Buff"]=[](AiL*game,ItemProps props){
|
|
||||||
for(auto&[key,value]:ItemAttribute::attributes){
|
|
||||||
float intensity=props.GetFloatProp(key,0);
|
|
||||||
if(intensity==0.f)continue;
|
|
||||||
if(ItemAttribute::Get(key).DisplayAsPercent())intensity/=100;
|
|
||||||
game->GetPlayer()->AddBuff(BuffType::STAT_UP,props.GetFloatProp(key,1),intensity,{ItemAttribute::Get(key)});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
ITEM_SCRIPTS["RestoreDuringCast"]=[](AiL*game,ItemProps props){
|
|
||||||
for(const auto&[propName,buffType]:NameToBuffType){
|
|
||||||
int restoreAmt=props.GetIntProp(propName);
|
|
||||||
|
|
||||||
game->GetPlayer()->AddBuff(BuffRestorationType::ONE_OFF,NameToBuffType.at(propName),0.01f,float(restoreAmt),0.0f);
|
|
||||||
if(restoreAmt>0&&props.PropCount(propName)==3){
|
|
||||||
game->GetPlayer()->AddBuff(BuffRestorationType::OVER_TIME_DURING_CAST,NameToBuffType.at(propName),props.GetFloatProp(propName,2),float(restoreAmt),props.GetFloatProp(propName,1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
ITEM_SCRIPTS.SetInitialized();
|
|
||||||
LOG(ITEM_SCRIPTS.size()<<" item scripts have been loaded.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Item::Item()
|
Item::Item()
|
||||||
:amt(0),it(nullptr),enhancementLevel(0){}
|
:amt(0),it(nullptr),enhancementLevel(0){}
|
||||||
|
|
||||||
@ -561,11 +526,11 @@ uint32_t Inventory::GetItemCount(IT it){
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
|
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
|
||||||
bool Inventory::UseItem(IT it,uint32_t amt){
|
bool Inventory::UseItem(IT it,uint32_t amt,const std::optional<vf2d>targetingPos){
|
||||||
if(!_inventory.count(it))return false;
|
if(!_inventory.count(it))return false;
|
||||||
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
|
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
|
||||||
for(uint32_t i=0;i<amt;i++){
|
for(uint32_t i=0;i<amt;i++){
|
||||||
if(ExecuteAction(it)){
|
if(ExecuteAction(it,targetingPos)){
|
||||||
return RemoveItem(GetItem(it)[0]);
|
return RemoveItem(GetItem(it)[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,9 +646,9 @@ void Inventory::InsertIntoStageInventoryCategory(std::shared_ptr<Item>itemRef,co
|
|||||||
Menu::InventorySlotsUpdated(stageInventoryCategory);
|
Menu::InventorySlotsUpdated(stageInventoryCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Inventory::ExecuteAction(IT item){
|
bool Inventory::ExecuteAction(IT item,const std::optional<vf2d>targetingPos){
|
||||||
if(ITEM_SCRIPTS.count(ITEM_DATA.at(item).useFunc)){
|
if(ITEM_SCRIPTS.count(ITEM_DATA.at(item).useFunc)){
|
||||||
return ITEM_SCRIPTS.at(ITEM_DATA.at(item).useFunc)(game,ITEM_DATA[item].customProps);
|
return ITEM_SCRIPTS.at(ITEM_DATA.at(item).useFunc)(game,targetingPos,ITEM_DATA[item].customProps);
|
||||||
}else{
|
}else{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1540,3 +1505,19 @@ const ItemEnchant&Item::ApplyRandomEnchant(){
|
|||||||
void Item::RemoveEnchant(){
|
void Item::RemoveEnchant(){
|
||||||
enchant.reset();
|
enchant.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const float Item::CastRange()const{
|
||||||
|
return ITEM_DATA.at(ActualName()).CastRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
const float ItemInfo::CastRange()const{
|
||||||
|
return castRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float Item::CastSize()const{
|
||||||
|
return ITEM_DATA.at(ActualName()).CastSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const float ItemInfo::CastSize()const{
|
||||||
|
return castSize;
|
||||||
|
}
|
@ -59,7 +59,7 @@ using ITCategory=std::string;
|
|||||||
using EventName=std::string;
|
using EventName=std::string;
|
||||||
using EnchantName=std::string;
|
using EnchantName=std::string;
|
||||||
|
|
||||||
using ItemScript=std::function<bool(AiL*,ItemProps)>;
|
using ItemScript=std::function<bool(AiL*game,std::optional<vf2d>targetingPos,ItemProps props)>;
|
||||||
|
|
||||||
enum class EquipSlot{
|
enum class EquipSlot{
|
||||||
HELMET= 0b0000'0001,
|
HELMET= 0b0000'0001,
|
||||||
@ -217,6 +217,8 @@ public:
|
|||||||
const Stats GetStats()const;
|
const Stats GetStats()const;
|
||||||
const ItemScript&OnUseAction()const;
|
const ItemScript&OnUseAction()const;
|
||||||
const float CastTime()const;
|
const float CastTime()const;
|
||||||
|
const float CastRange()const;
|
||||||
|
const float CastSize()const;
|
||||||
const bool UseDuringCast()const; //When true, the item is activated during the cast instead of after the cast.
|
const bool UseDuringCast()const; //When true, the item is activated during the cast instead of after the cast.
|
||||||
const float CooldownTime()const;
|
const float CooldownTime()const;
|
||||||
//Use ISBLANK macro instead!! This should not be called directly!!
|
//Use ISBLANK macro instead!! This should not be called directly!!
|
||||||
@ -295,7 +297,7 @@ public:
|
|||||||
static std::vector<std::shared_ptr<Item>>CopyItem(IT it);
|
static std::vector<std::shared_ptr<Item>>CopyItem(IT it);
|
||||||
static std::vector<std::weak_ptr<Item>>GetItem(IT it);
|
static std::vector<std::weak_ptr<Item>>GetItem(IT it);
|
||||||
//Auto-executes its use function and removes the amt specified from the inventory. Multiple amounts will cause the item to execute its useFunc multiple times.
|
//Auto-executes its use function and removes the amt specified from the inventory. Multiple amounts will cause the item to execute its useFunc multiple times.
|
||||||
static bool UseItem(IT it,uint32_t amt=1);
|
static bool UseItem(IT it,uint32_t amt=1,const std::optional<vf2d>targetingPos={});
|
||||||
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
|
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
|
||||||
static bool RemoveItem(std::weak_ptr<Item>itemRef,ITCategory inventory,uint32_t amt = 1);
|
static bool RemoveItem(std::weak_ptr<Item>itemRef,ITCategory inventory,uint32_t amt = 1);
|
||||||
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
|
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
|
||||||
@ -328,7 +330,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
static void InsertIntoSortedInv(std::shared_ptr<Item>itemRef);
|
static void InsertIntoSortedInv(std::shared_ptr<Item>itemRef);
|
||||||
static void InsertIntoStageInventoryCategory(std::shared_ptr<Item>itemRef,const bool monsterDrop);
|
static void InsertIntoStageInventoryCategory(std::shared_ptr<Item>itemRef,const bool monsterDrop);
|
||||||
static bool ExecuteAction(IT item);
|
static bool ExecuteAction(IT item,const std::optional<vf2d>targetingPos);
|
||||||
static std::multimap<IT,std::shared_ptr<Item>>_inventory;
|
static std::multimap<IT,std::shared_ptr<Item>>_inventory;
|
||||||
static std::vector<std::shared_ptr<Item>>blacksmithInventory;
|
static std::vector<std::shared_ptr<Item>>blacksmithInventory;
|
||||||
static std::map<EquipSlot,std::weak_ptr<Item>>equipment;
|
static std::map<EquipSlot,std::weak_ptr<Item>>equipment;
|
||||||
@ -356,6 +358,8 @@ class ItemInfo{
|
|||||||
std::string description;
|
std::string description;
|
||||||
std::string category;
|
std::string category;
|
||||||
float castTime=0;
|
float castTime=0;
|
||||||
|
float castRange=0;
|
||||||
|
float castSize=0;
|
||||||
float cooldownTime=0;
|
float cooldownTime=0;
|
||||||
EquipSlot slot=EquipSlot::NONE;
|
EquipSlot slot=EquipSlot::NONE;
|
||||||
EnhancementInfo enhancement;
|
EnhancementInfo enhancement;
|
||||||
@ -398,6 +402,8 @@ public:
|
|||||||
const ItemScript&OnUseAction()const;
|
const ItemScript&OnUseAction()const;
|
||||||
const Stats GetStats(int enhancementLevel)const;
|
const Stats GetStats(int enhancementLevel)const;
|
||||||
const float CastTime()const;
|
const float CastTime()const;
|
||||||
|
const float CastRange()const;
|
||||||
|
const float CastSize()const;
|
||||||
const float CooldownTime()const;
|
const float CooldownTime()const;
|
||||||
const EquipSlot Slot()const;
|
const EquipSlot Slot()const;
|
||||||
const std::optional<const::ItemSet*const>ItemSet()const;
|
const std::optional<const::ItemSet*const>ItemSet()const;
|
||||||
|
94
Adventures in Lestoria/ItemScript.cpp
Normal file
94
Adventures in Lestoria/ItemScript.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#pragma region License
|
||||||
|
/*
|
||||||
|
License (OLC-3)
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce the above
|
||||||
|
copyright notice. This list of conditions and the following disclaimer must be
|
||||||
|
reproduced in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||||
|
be used to endorse or promote products derived from this software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
|
|
||||||
|
Portions of this software are copyright © 2024 The FreeType
|
||||||
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
#pragma endregion
|
||||||
|
#include "AdventuresInLestoria.h"
|
||||||
|
#include "BulletTypes.h"
|
||||||
|
|
||||||
|
INCLUDE_ITEM_SCRIPTS
|
||||||
|
INCLUDE_game
|
||||||
|
INCLUDE_ANIMATION_DATA
|
||||||
|
|
||||||
|
void ItemInfo::InitializeScripts(){
|
||||||
|
|
||||||
|
ITEM_SCRIPTS["Restore"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){
|
||||||
|
for(const auto&[propName,buffType]:NameToBuffType){
|
||||||
|
int restoreAmt=props.GetIntProp(propName);
|
||||||
|
|
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::ONE_OFF,NameToBuffType.at(propName),0.01f,float(restoreAmt),0.0f);
|
||||||
|
if(restoreAmt>0&&props.PropCount(propName)==3){
|
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::OVER_TIME,NameToBuffType.at(propName),props.GetFloatProp(propName,2),float(restoreAmt),props.GetFloatProp(propName,1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(auto&[key,value]:ItemAttribute::attributes){
|
||||||
|
if(!DATA.GetProperty("ItemScript.Buff").HasProperty(key)){
|
||||||
|
ERR("WARNING! Buff Item Script does not support Buff "<<std::quoted(value.Name())<<"!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ITEM_SCRIPTS["Buff"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){
|
||||||
|
for(auto&[key,value]:ItemAttribute::attributes){
|
||||||
|
float intensity=props.GetFloatProp(key,0);
|
||||||
|
if(intensity==0.f)continue;
|
||||||
|
if(ItemAttribute::Get(key).DisplayAsPercent())intensity/=100;
|
||||||
|
game->GetPlayer()->AddBuff(BuffType::STAT_UP,props.GetFloatProp(key,1),intensity,{ItemAttribute::Get(key)});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
ITEM_SCRIPTS["RestoreDuringCast"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){
|
||||||
|
for(const auto&[propName,buffType]:NameToBuffType){
|
||||||
|
int restoreAmt=props.GetIntProp(propName);
|
||||||
|
|
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::ONE_OFF,NameToBuffType.at(propName),0.01f,float(restoreAmt),0.0f);
|
||||||
|
if(restoreAmt>0&&props.PropCount(propName)==3){
|
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::OVER_TIME_DURING_CAST,NameToBuffType.at(propName),props.GetFloatProp(propName,2),float(restoreAmt),props.GetFloatProp(propName,1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
ITEM_SCRIPTS["Projectile"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){
|
||||||
|
const int projectileDamage{props.GetIntProp("Base Damage")+int(game->GetPlayer()->GetAttack()*props.GetFloatProp("Player Damage Mult"))};
|
||||||
|
CreateBullet(ThrownProjectile)(game->GetPlayer()->GetPos(),targetingPos.value(),props.GetStringProp("Image"),props.GetFloatProp("Cast Size")/100.f*24,game->GetPlayer()->GetZ(),0.3f,8.f,projectileDamage,game->GetPlayer()->OnUpperLevel(),false,INFINITE,true,WHITE,props.GetFloatProp("Cast Size")/100.f*vf2d{1.f,1.f},0.f,
|
||||||
|
Effect{vf2d{},ANIMATION_DATA.at("explosionframes.png").GetTotalAnimationDuration(),"explosionframes.png",game->GetPlayer()->OnUpperLevel(),props.GetFloatProp("Cast Size")/100.f*vf2d{1.f,1.f}},props.GetStringProp("Explode Sound Effect"))EndBullet;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
ITEM_SCRIPTS.SetInitialized();
|
||||||
|
LOG(ITEM_SCRIPTS.size()<<" item scripts have been loaded.");
|
||||||
|
}
|
@ -37,80 +37,62 @@ All rights reserved.
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#include "BulletTypes.h"
|
#include "BulletTypes.h"
|
||||||
#include "AdventuresInLestoria.h"
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include "util.h"
|
|
||||||
#include "SoundEffect.h"
|
|
||||||
|
|
||||||
INCLUDE_game
|
PoisonBottle::PoisonBottle(vf2d pos,vf2d targetPos,const std::string&img,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle)
|
||||||
|
:ThrownProjectile(pos,targetPos,img,explodeRadius,z,totalFallTime,totalRiseZAmt,damage,upperLevel,hitsMultiple,lifetime,friendly,col,scale,image_angle),bounceExplodeRadius(bounceExplodeRadius),additionalBounceCount(additionalBounceCount){
|
||||||
PoisonBottle::PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle)
|
|
||||||
:Bullet(pos,util::pointTo(pos,targetPos)*util::distance(pos,targetPos)/totalFallTime,0.f,damage,"poison_bottle.png",upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,image_angle),initialZ(z),explodeRadius(explodeRadius),bounceExplodeRadius(bounceExplodeRadius),totalRiseZAmt(totalRiseZAmt),totalFallTime(totalFallTime),startingPos(pos),targetPos(targetPos),originalRisingTime(totalFallTime*0.25f/(additionalBounceCount+1)),risingTime(originalRisingTime),originalFallingTime(totalFallTime*0.75f/(additionalBounceCount+1)),fallingTime(originalFallingTime),additionalBounceCount(additionalBounceCount){
|
|
||||||
this->z=z;
|
this->z=z;
|
||||||
|
this->risingTime=this->originalRisingTime=totalFallTime*0.25f/(additionalBounceCount+1);
|
||||||
|
this->fallingTime=this->originalFallingTime=totalFallTime*0.75f/(additionalBounceCount+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PoisonBottle::Update(float fElapsedTime){
|
void PoisonBottle::OnGroundLand(){
|
||||||
if(IsDeactivated())return;
|
SoundEffect::PlaySFX("Glass Break",pos);
|
||||||
image_angle+=0.5*PI*fElapsedTime;
|
SoundEffect::PlaySFX("Poison Pool",pos);
|
||||||
|
float poisonCircleScale{"Witch.Ability 2.Casting Size"_F/100.f};
|
||||||
|
if(additionalBounceCount>0)poisonCircleScale="Poison Bounce"_ENC["BOUNCE EXPLODE RADIUS"]/100.f;
|
||||||
|
|
||||||
const bool Landed{fallingTime<=0.f};
|
float currentExplodeRadius{explodeRadius};
|
||||||
if(Landed){
|
if(additionalBounceCount>0)currentExplodeRadius=bounceExplodeRadius;
|
||||||
z=0.f;
|
|
||||||
SoundEffect::PlaySFX("Glass Break",pos);
|
|
||||||
SoundEffect::PlaySFX("Poison Pool",pos);
|
|
||||||
float poisonCircleScale{"Witch.Ability 2.Casting Size"_F/100.f};
|
|
||||||
if(additionalBounceCount>0)poisonCircleScale="Poison Bounce"_ENC["BOUNCE EXPLODE RADIUS"]/100.f;
|
|
||||||
|
|
||||||
float currentExplodeRadius{explodeRadius};
|
game->AddEffect(std::make_unique<Effect>(pos,0.f,"poison_pool.png",game->GetPlayer()->OnUpperLevel(),0.5f,1.2f,vf2d{poisonCircleScale,poisonCircleScale},vf2d{},WHITE,0.f,0.f,false),true);
|
||||||
if(additionalBounceCount>0)currentExplodeRadius=bounceExplodeRadius;
|
for(int i:std::ranges::iota_view(0,200)){
|
||||||
|
float size{util::random_range(0.4f,0.8f)};
|
||||||
game->AddEffect(std::make_unique<Effect>(pos,0.f,"poison_pool.png",game->GetPlayer()->OnUpperLevel(),0.5f,1.2f,vf2d{poisonCircleScale,poisonCircleScale},vf2d{},WHITE,0.f,0.f,false),true);
|
Pixel col{PixelLerp(VERY_DARK_GREEN,DARK_GREEN,util::random(1))};
|
||||||
for(int i:std::ranges::iota_view(0,200)){
|
col.a=util::random_range(60,200);
|
||||||
|
game->AddEffect(std::make_unique<Effect>(pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col));
|
||||||
|
}
|
||||||
|
if(additionalBounceCount==0&&game->GetPlayer()->HasEnchant("Pooling Poison")){
|
||||||
|
game->AddEffect(std::make_unique<PoisonPool>(pos,"poison_pool.png",bounceExplodeRadius,game->GetPlayer()->GetAttack()*"Pooling Poison"_ENC["POISON POOL DAMAGE PCT"]/100.f,"Pooling Poison"_ENC["POISON POOL DAMAGE FREQUENCY"],friendly?HurtType::MONSTER:HurtType::PLAYER,"Pooling Poison"_ENC["SPLASH LINGER TIME"],0.5f,OnUpperLevel(),poisonCircleScale,vf2d{},WHITE,0.f,0.f,false,0.05f,[pos=pos,col=col,poisonCircleScale](const Effect&self){
|
||||||
float size{util::random_range(0.4f,0.8f)};
|
float size{util::random_range(0.4f,0.8f)};
|
||||||
Pixel col{PixelLerp(VERY_DARK_GREEN,DARK_GREEN,util::random(1))};
|
return Effect{pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col};
|
||||||
col.a=util::random_range(60,200);
|
}),true);
|
||||||
game->AddEffect(std::make_unique<Effect>(pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col));
|
}
|
||||||
}
|
|
||||||
if(additionalBounceCount==0&&game->GetPlayer()->HasEnchant("Pooling Poison")){
|
|
||||||
game->AddEffect(std::make_unique<PoisonPool>(pos,"poison_pool.png",bounceExplodeRadius,game->GetPlayer()->GetAttack()*"Pooling Poison"_ENC["POISON POOL DAMAGE PCT"]/100.f,"Pooling Poison"_ENC["POISON POOL DAMAGE FREQUENCY"],friendly?HurtType::MONSTER:HurtType::PLAYER,"Pooling Poison"_ENC["SPLASH LINGER TIME"],0.5f,OnUpperLevel(),poisonCircleScale,vf2d{},WHITE,0.f,0.f,false,0.05f,[pos=pos,col=col,poisonCircleScale](const Effect&self){
|
|
||||||
float size{util::random_range(0.4f,0.8f)};
|
|
||||||
return Effect{pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col};
|
|
||||||
}),true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int poolDamage{damage};
|
int poolDamage{damage};
|
||||||
if(additionalBounceCount>0)poolDamage=game->GetPlayer()->GetAttack()*"Poison Bounce"_ENC["BOUNCE DAMAGE"]/100.f;
|
if(additionalBounceCount>0)poolDamage=game->GetPlayer()->GetAttack()*"Poison Bounce"_ENC["BOUNCE DAMAGE"]/100.f;
|
||||||
|
|
||||||
const HurtList hurtList{game->Hurt(pos,bounceExplodeRadius,poolDamage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
|
const HurtList hurtList{game->Hurt(pos,bounceExplodeRadius,poolDamage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
|
||||||
if(additionalBounceCount==0){
|
if(additionalBounceCount==0){
|
||||||
for(const auto&[targetPtr,wasHit]:hurtList){
|
for(const auto&[targetPtr,wasHit]:hurtList){
|
||||||
if(wasHit){
|
if(wasHit){
|
||||||
Monster*monsterPtr{std::get<Monster*>(targetPtr)};
|
Monster*monsterPtr{std::get<Monster*>(targetPtr)};
|
||||||
const int buffDamage{int(game->GetPlayer()->GetAttack()*"Witch.Ability 2.Poison Debuff"_f[0])};
|
const int buffDamage{int(game->GetPlayer()->GetAttack()*"Witch.Ability 2.Poison Debuff"_f[0])};
|
||||||
const float buffDuration{"Witch.Ability 2.Poison Debuff"_f[2]};
|
const float buffDuration{"Witch.Ability 2.Poison Debuff"_f[2]};
|
||||||
const float buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]};
|
const float buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]};
|
||||||
monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n);
|
monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n);
|
||||||
monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks);
|
monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(additionalBounceCount>0){
|
}
|
||||||
additionalBounceCount--;
|
if(additionalBounceCount>0){
|
||||||
risingTime=originalRisingTime;
|
additionalBounceCount--;
|
||||||
fallingTime=originalFallingTime;
|
risingTime=originalRisingTime;
|
||||||
}else{
|
fallingTime=originalFallingTime;
|
||||||
vel={};
|
|
||||||
fadeOutTime=0.25f;
|
|
||||||
Deactivate();
|
|
||||||
}
|
|
||||||
}else{
|
}else{
|
||||||
if(risingTime>0.f){
|
vel={};
|
||||||
risingTime-=fElapsedTime;
|
fadeOutTime=0.25f;
|
||||||
z=util::lerp(initialZ,initialZ+totalRiseZAmt,1-(risingTime/originalRisingTime));
|
Deactivate();
|
||||||
}else{
|
|
||||||
fallingTime-=fElapsedTime;
|
|
||||||
z=util::lerp(initialZ+totalRiseZAmt,0.f,1-(fallingTime/originalFallingTime));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,8 @@ Add unconscious monster state.
|
|||||||
DEMO
|
DEMO
|
||||||
====
|
====
|
||||||
|
|
||||||
Save file backup and rolling back on failure
|
|
||||||
|
|
||||||
|
|
||||||
|
Add Sherman and Blacksmith story images
|
||||||
|
|
||||||
Adding new class animations
|
Adding new class animations
|
||||||
===========================
|
===========================
|
||||||
|
88
Adventures in Lestoria/ThrownProjectile.cpp
Normal file
88
Adventures in Lestoria/ThrownProjectile.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#pragma region License
|
||||||
|
/*
|
||||||
|
License (OLC-3)
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce the above
|
||||||
|
copyright notice. This list of conditions and the following disclaimer must be
|
||||||
|
reproduced in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||||
|
be used to endorse or promote products derived from this software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
|
|
||||||
|
Portions of this software are copyright © 2024 The FreeType
|
||||||
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#include "BulletTypes.h"
|
||||||
|
#include "AdventuresInLestoria.h"
|
||||||
|
#include <ranges>
|
||||||
|
#include "util.h"
|
||||||
|
#include "SoundEffect.h"
|
||||||
|
|
||||||
|
INCLUDE_game
|
||||||
|
|
||||||
|
ThrownProjectile::ThrownProjectile(vf2d pos,vf2d targetPos,const std::string&img,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle,const std::optional<Effect>explodeEffect,const std::optional<std::string>explodeSoundEffect)
|
||||||
|
:Bullet(pos,util::pointTo(pos,targetPos)*util::distance(pos,targetPos)/totalFallTime,0.f,damage,img,upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,image_angle),initialZ(z),explodeRadius(explodeRadius),totalRiseZAmt(totalRiseZAmt),totalFallTime(totalFallTime),startingPos(pos),targetPos(targetPos),originalRisingTime(totalFallTime*0.25f),risingTime(originalRisingTime),originalFallingTime(totalFallTime*0.75f),fallingTime(originalFallingTime),explodeEffect(explodeEffect),img(img),explodeSoundEffect(explodeSoundEffect){
|
||||||
|
this->z=z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThrownProjectile::OnGroundLand(){
|
||||||
|
const HurtList hurtList{game->Hurt(pos,explodeRadius,damage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
|
||||||
|
if(explodeEffect){
|
||||||
|
Effect&explosionEffect{game->AddEffect(std::make_unique<Effect>(explodeEffect.value()))};
|
||||||
|
explosionEffect.pos+=pos;
|
||||||
|
}
|
||||||
|
if(explodeSoundEffect)SoundEffect::PlaySFX(explodeSoundEffect.value(),pos);
|
||||||
|
vel={};
|
||||||
|
fadeOutTime=0.25f;
|
||||||
|
Deactivate();
|
||||||
|
}
|
||||||
|
void ThrownProjectile::_OnGroundLand(){
|
||||||
|
z=0.f;
|
||||||
|
OnGroundLand();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThrownProjectile::Update(float fElapsedTime){
|
||||||
|
if(IsDeactivated())return;
|
||||||
|
image_angle+=0.5*PI*fElapsedTime;
|
||||||
|
|
||||||
|
const bool Landed{fallingTime<=0.f};
|
||||||
|
if(Landed){
|
||||||
|
_OnGroundLand();
|
||||||
|
}else{
|
||||||
|
if(risingTime>0.f){
|
||||||
|
risingTime-=fElapsedTime;
|
||||||
|
z=util::lerp(initialZ,initialZ+totalRiseZAmt,1-(risingTime/originalRisingTime));
|
||||||
|
}else{
|
||||||
|
fallingTime-=fElapsedTime;
|
||||||
|
z=util::lerp(initialZ+totalRiseZAmt,0.f,1-(fallingTime/originalFallingTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThrownProjectile::ModifyOutgoingDamageData(HurtDamageInfo&data){
|
||||||
|
if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY;
|
||||||
|
}
|
@ -39,7 +39,7 @@ All rights reserved.
|
|||||||
#define VERSION_MAJOR 1
|
#define VERSION_MAJOR 1
|
||||||
#define VERSION_MINOR 3
|
#define VERSION_MINOR 3
|
||||||
#define VERSION_PATCH 0
|
#define VERSION_PATCH 0
|
||||||
#define VERSION_BUILD 11725
|
#define VERSION_BUILD 11748
|
||||||
|
|
||||||
#define stringify(a) stringify_(a)
|
#define stringify(a) stringify_(a)
|
||||||
#define stringify_(a) #a
|
#define stringify_(a) #a
|
||||||
|
@ -65,7 +65,6 @@ void VisualNovel::Initialize(){
|
|||||||
std::string line;
|
std::string line;
|
||||||
std::string currentStory;
|
std::string currentStory;
|
||||||
while(file.good()){
|
while(file.good()){
|
||||||
|
|
||||||
auto trim = [](std::string& s)
|
auto trim = [](std::string& s)
|
||||||
{
|
{
|
||||||
s.erase(0, s.find_first_not_of(" \t\n\r\f\v"));
|
s.erase(0, s.find_first_not_of(" \t\n\r\f\v"));
|
||||||
@ -179,7 +178,6 @@ void VisualNovel::Initialize(){
|
|||||||
data.push_back(std::make_unique<DialogCommand>(line));
|
data.push_back(std::make_unique<DialogCommand>(line));
|
||||||
}break;
|
}break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ void Witch::InitializeClassAbilities(){
|
|||||||
int additionalBounceCount{0};
|
int additionalBounceCount{0};
|
||||||
if(p->HasEnchant("Poison Bounce"))additionalBounceCount+="Poison Bounce"_ENC["BOUNCE COUNT"];
|
if(p->HasEnchant("Poison Bounce"))additionalBounceCount+="Poison Bounce"_ENC["BOUNCE COUNT"];
|
||||||
const float totalFallTime{util::lerp(1/30.f*(additionalBounceCount+1),0.3f,util::distance(p->GetPos(),pos)/("Witch.Ability 2.Casting Range"_F/100.f*24))};
|
const float totalFallTime{util::lerp(1/30.f*(additionalBounceCount+1),0.3f,util::distance(p->GetPos(),pos)/("Witch.Ability 2.Casting Range"_F/100.f*24))};
|
||||||
CreateBullet(PoisonBottle)(p->GetPos(),pos,"Witch.Ability 2.Casting Size"_F/100.f*24,"Poison Bounce"_ENC["BOUNCE EXPLODE RADIUS"]/100.f*24,12.f,totalFallTime,"Witch.Ability 2.Toss Max Z"_F,p->GetAttack()*"Witch.Ability 2.Damage Mult"_F,additionalBounceCount,p->OnUpperLevel(),false,INFINITE,true,WHITE,vf2d{1.f,1.f},util::random(2*PI))EndBullet;
|
CreateBullet(PoisonBottle)(p->GetPos(),pos,"poison_bottle.png","Witch.Ability 2.Casting Size"_F/100.f*24,"Poison Bounce"_ENC["BOUNCE EXPLODE RADIUS"]/100.f*24,12.f,totalFallTime,"Witch.Ability 2.Toss Max Z"_F,p->GetAttack()*"Witch.Ability 2.Damage Mult"_F,additionalBounceCount,p->OnUpperLevel(),false,INFINITE,true,WHITE,vf2d{1.f,1.f},util::random(2*PI))EndBullet;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
BIN
Adventures in Lestoria/assets/bomb.png
Normal file
BIN
Adventures in Lestoria/assets/bomb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
@ -315,6 +315,11 @@ Events
|
|||||||
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
|
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
|
||||||
File[0] = poison_pool.ogg, 80%
|
File[0] = poison_pool.ogg, 80%
|
||||||
}
|
}
|
||||||
|
Projectile Toss
|
||||||
|
{
|
||||||
|
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
|
||||||
|
File[0] = Throw Projectile.ogg, 50%
|
||||||
|
}
|
||||||
Ranger Auto Attack
|
Ranger Auto Attack
|
||||||
{
|
{
|
||||||
CombatSound = True
|
CombatSound = True
|
||||||
|
@ -138,6 +138,7 @@ Images
|
|||||||
GFX_LineIndicator = line_indicator.png
|
GFX_LineIndicator = line_indicator.png
|
||||||
GFX_MusketBullet = musket_bullet.png
|
GFX_MusketBullet = musket_bullet.png
|
||||||
GFX_SandSuction = sand_suction.png
|
GFX_SandSuction = sand_suction.png
|
||||||
|
GFX_Bomb = bomb.png
|
||||||
|
|
||||||
GFX_Thief_Sheet = nico-thief.png
|
GFX_Thief_Sheet = nico-thief.png
|
||||||
GFX_Trapper_Sheet = nico-trapper.png
|
GFX_Trapper_Sheet = nico-trapper.png
|
||||||
|
@ -186,11 +186,12 @@ ItemDatabase
|
|||||||
{
|
{
|
||||||
Description = Throw a bomb, dealing damage around an area.
|
Description = Throw a bomb, dealing damage around an area.
|
||||||
ItemCategory = Consumables
|
ItemCategory = Consumables
|
||||||
ItemScript = Bomb
|
ItemScript = Projectile
|
||||||
Image = bomb.png
|
Image = bomb.png
|
||||||
Base Damage = 50
|
Base Damage = 50
|
||||||
Player Damage Mult = 1.6x
|
Player Damage Mult = 1.6x
|
||||||
Explode Radius = 350
|
Explode Sound Effect = Bomb Explode
|
||||||
|
Cast Size = 350
|
||||||
Cast Range = 700
|
Cast Range = 700
|
||||||
Cooldown Time = 5.0
|
Cooldown Time = 5.0
|
||||||
Cast Time = 0.0
|
Cast Time = 0.0
|
||||||
@ -212,15 +213,18 @@ ItemDatabase
|
|||||||
{
|
{
|
||||||
Description = Throw a molotov dealing damage and leave behind a burning area.
|
Description = Throw a molotov dealing damage and leave behind a burning area.
|
||||||
ItemCategory = Consumables
|
ItemCategory = Consumables
|
||||||
ItemScript = Bomb
|
ItemScript = Projectile
|
||||||
Image = molotov.png
|
Image = molotov.png
|
||||||
Base Damage = 50
|
Base Damage = 50
|
||||||
Player Damage Mult = 1.6x
|
Player Damage Mult = 1.6x
|
||||||
Explode Radius = 250
|
Cast Size = 250
|
||||||
Cast Range = 700
|
Cast Range = 700
|
||||||
Tick Rate = 1s
|
Tick Rate = 1s
|
||||||
Tick Base Damage = 10
|
Tick Base Damage = 10
|
||||||
Tick Player Damage Mult = 0.4x
|
Tick Player Damage Mult = 0.4x
|
||||||
|
Explode Sound Effect = Bomb Explode
|
||||||
|
Lingering Effect = pixel.png
|
||||||
|
Linger Radius = 250
|
||||||
Linger Time = 4s
|
Linger Time = 4s
|
||||||
Cooldown Time = 5.0
|
Cooldown Time = 5.0
|
||||||
Cast Time = 0.0
|
Cast Time = 0.0
|
||||||
|
@ -48,4 +48,22 @@ ItemScript
|
|||||||
MP Restore = 0,0.0,0.0
|
MP Restore = 0,0.0,0.0
|
||||||
MP % Restore = 0,0.0,0.0
|
MP % Restore = 0,0.0,0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Projectile
|
||||||
|
{
|
||||||
|
Image = pixel.png
|
||||||
|
# Extra flat damage
|
||||||
|
Base Damage = 0
|
||||||
|
# Additional damage based on player ATK
|
||||||
|
Player Damage Mult = 1.0x
|
||||||
|
Cast Size = 250
|
||||||
|
Explode Sound Effect = Bomb Explode
|
||||||
|
# Set a lingering time to have a damage over time effect.
|
||||||
|
Linger Time = 0s
|
||||||
|
Lingering Effect = pixel.png
|
||||||
|
Linger Radius = 250
|
||||||
|
Tick Rate = 1s
|
||||||
|
Tick Base Damage = 10
|
||||||
|
Tick Player Damage Mult = 0.4x
|
||||||
|
}
|
||||||
}
|
}
|
Binary file not shown.
BIN
Adventures in Lestoria/assets/sounds/Throw Projectile.ogg
Normal file
BIN
Adventures in Lestoria/assets/sounds/Throw Projectile.ogg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user