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);
|
||||
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>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ItemLoadoutWindow.cpp" />
|
||||
<ClCompile Include="ItemScript.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Key.cpp" />
|
||||
<ClCompile Include="LargeStone.cpp" />
|
||||
<ClCompile Include="LargeTornado.cpp">
|
||||
@ -1071,6 +1075,10 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThrownProjectile.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PoisonPool.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
|
@ -1190,7 +1190,7 @@
|
||||
<ClCompile Include="PurpleEnergyBall.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PoisonBottle.cpp">
|
||||
<ClCompile Include="ThrownProjectile.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="State_Dialog.cpp">
|
||||
@ -1253,6 +1253,12 @@
|
||||
<ClCompile Include="GiantCrab.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ItemScript.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PoisonBottle.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
|
@ -3929,28 +3929,28 @@ void AiL::SetLoadoutItem(int slot,std::string itemName){
|
||||
inputGroup=&Player::KEY_ITEM3;
|
||||
}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.itemAbility=true;
|
||||
|
||||
switch(slot){
|
||||
case 0:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
return game->UseLoadoutItem(0);
|
||||
return game->UseLoadoutItem(0,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(loadout[slot]);
|
||||
}break;
|
||||
case 1:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
return game->UseLoadoutItem(1);
|
||||
return game->UseLoadoutItem(1,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(loadout[slot]);
|
||||
}break;
|
||||
case 2:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
return game->UseLoadoutItem(2);
|
||||
return game->UseLoadoutItem(2,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
||||
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(!ISBLANK(GetLoadoutItem(slot).lock())&&GetLoadoutItem(slot).lock()->Amt()>0){
|
||||
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);
|
||||
GetLoadoutItem(slot).lock()->amt--;
|
||||
if(GetLoadoutItem(slot).lock()->UseSound().length()>0){
|
||||
@ -4008,7 +4008,7 @@ void AiL::ClearLoadoutItem(int slot){
|
||||
switch(slot){
|
||||
case 0:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
return game->UseLoadoutItem(0);
|
||||
return game->UseLoadoutItem(0,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(Item::BLANK);
|
||||
@ -4016,7 +4016,7 @@ void AiL::ClearLoadoutItem(int slot){
|
||||
}break;
|
||||
case 1:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
return game->UseLoadoutItem(1);
|
||||
return game->UseLoadoutItem(1,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(Item::BLANK);
|
||||
@ -4024,7 +4024,7 @@ void AiL::ClearLoadoutItem(int slot){
|
||||
}break;
|
||||
case 2:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
return game->UseLoadoutItem(2);
|
||||
return game->UseLoadoutItem(2,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(Item::BLANK);
|
||||
|
@ -363,7 +363,7 @@ public:
|
||||
int GetLoadoutSize()const;
|
||||
void RestockLoadoutItems();
|
||||
//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.
|
||||
void ClearLoadoutItem(int slot);
|
||||
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("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("bomb.png",4,{24,24},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::OneShot}});
|
||||
|
||||
CreateStillAnimation("meteor.png",{192,192});
|
||||
|
||||
|
@ -351,19 +351,32 @@ private:
|
||||
std::optional<std::weak_ptr<Monster>>homingTarget;
|
||||
};
|
||||
|
||||
struct PoisonBottle: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});
|
||||
struct ThrownProjectile:public Bullet{
|
||||
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 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 startingPos;
|
||||
const float totalFallTime;
|
||||
const float originalRisingTime,originalFallingTime;
|
||||
float originalRisingTime;
|
||||
float originalFallingTime;
|
||||
float risingTime,fallingTime;
|
||||
const float initialZ;
|
||||
const float totalRiseZAmt;
|
||||
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;
|
||||
int additionalBounceCount;
|
||||
};
|
@ -63,6 +63,7 @@ using MonsterSpawnerID=int;
|
||||
#define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS;
|
||||
#define INCLUDE_ADMIN_MODE extern bool ADMIN_MODE;
|
||||
#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;
|
||||
|
||||
|
@ -142,6 +142,8 @@ void ItemInfo::InitializeItems(){
|
||||
std::string scriptName="",description="",category="";
|
||||
std::string setName="";
|
||||
float castTime=0;
|
||||
float castRange=0;
|
||||
float castSize=0;
|
||||
std::vector<std::string> slot;
|
||||
float cooldownTime="Item.Item Cooldown Time"_F;
|
||||
std::vector<ItemAttribute>statValueList;
|
||||
@ -167,6 +169,12 @@ void ItemInfo::InitializeItems(){
|
||||
}else
|
||||
if(keyName=="Cast Time"){
|
||||
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
|
||||
if(keyName=="Cooldown Time"){
|
||||
cooldownTime=float(data[key][keyName].GetReal());
|
||||
@ -302,6 +310,8 @@ void ItemInfo::InitializeItems(){
|
||||
it.description=description;
|
||||
it.category=category;
|
||||
it.castTime=castTime;
|
||||
it.castRange=castRange;
|
||||
it.castSize=castSize;
|
||||
it.cooldownTime=cooldownTime;
|
||||
it.slot=EquipSlot::NONE;
|
||||
it.set=setName;
|
||||
@ -452,51 +462,6 @@ const std::unordered_map<std::string,BuffOverTimeType::BuffOverTimeType>ItemInfo
|
||||
{"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()
|
||||
: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.
|
||||
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;
|
||||
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
|
||||
for(uint32_t i=0;i<amt;i++){
|
||||
if(ExecuteAction(it)){
|
||||
if(ExecuteAction(it,targetingPos)){
|
||||
return RemoveItem(GetItem(it)[0]);
|
||||
}
|
||||
}
|
||||
@ -681,9 +646,9 @@ void Inventory::InsertIntoStageInventoryCategory(std::shared_ptr<Item>itemRef,co
|
||||
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)){
|
||||
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{
|
||||
return false;
|
||||
}
|
||||
@ -1540,3 +1505,19 @@ const ItemEnchant&Item::ApplyRandomEnchant(){
|
||||
void Item::RemoveEnchant(){
|
||||
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 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{
|
||||
HELMET= 0b0000'0001,
|
||||
@ -217,6 +217,8 @@ public:
|
||||
const Stats GetStats()const;
|
||||
const ItemScript&OnUseAction()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 float CooldownTime()const;
|
||||
//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::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.
|
||||
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.
|
||||
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.
|
||||
@ -328,7 +330,7 @@ public:
|
||||
private:
|
||||
static void InsertIntoSortedInv(std::shared_ptr<Item>itemRef);
|
||||
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::vector<std::shared_ptr<Item>>blacksmithInventory;
|
||||
static std::map<EquipSlot,std::weak_ptr<Item>>equipment;
|
||||
@ -356,6 +358,8 @@ class ItemInfo{
|
||||
std::string description;
|
||||
std::string category;
|
||||
float castTime=0;
|
||||
float castRange=0;
|
||||
float castSize=0;
|
||||
float cooldownTime=0;
|
||||
EquipSlot slot=EquipSlot::NONE;
|
||||
EnhancementInfo enhancement;
|
||||
@ -398,6 +402,8 @@ public:
|
||||
const ItemScript&OnUseAction()const;
|
||||
const Stats GetStats(int enhancementLevel)const;
|
||||
const float CastTime()const;
|
||||
const float CastRange()const;
|
||||
const float CastSize()const;
|
||||
const float CooldownTime()const;
|
||||
const EquipSlot Slot()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
|
||||
|
||||
#include "BulletTypes.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include <ranges>
|
||||
#include "util.h"
|
||||
#include "SoundEffect.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
if(IsDeactivated())return;
|
||||
image_angle+=0.5*PI*fElapsedTime;
|
||||
void PoisonBottle::OnGroundLand(){
|
||||
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;
|
||||
|
||||
const bool Landed{fallingTime<=0.f};
|
||||
if(Landed){
|
||||
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};
|
||||
if(additionalBounceCount>0)currentExplodeRadius=bounceExplodeRadius;
|
||||
|
||||
float currentExplodeRadius{explodeRadius};
|
||||
if(additionalBounceCount>0)currentExplodeRadius=bounceExplodeRadius;
|
||||
|
||||
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);
|
||||
for(int i:std::ranges::iota_view(0,200)){
|
||||
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);
|
||||
for(int i:std::ranges::iota_view(0,200)){
|
||||
float size{util::random_range(0.4f,0.8f)};
|
||||
Pixel col{PixelLerp(VERY_DARK_GREEN,DARK_GREEN,util::random(1))};
|
||||
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)};
|
||||
Pixel col{PixelLerp(VERY_DARK_GREEN,DARK_GREEN,util::random(1))};
|
||||
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)};
|
||||
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);
|
||||
}
|
||||
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};
|
||||
if(additionalBounceCount>0)poolDamage=game->GetPlayer()->GetAttack()*"Poison Bounce"_ENC["BOUNCE DAMAGE"]/100.f;
|
||||
int poolDamage{damage};
|
||||
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)};
|
||||
if(additionalBounceCount==0){
|
||||
for(const auto&[targetPtr,wasHit]:hurtList){
|
||||
if(wasHit){
|
||||
Monster*monsterPtr{std::get<Monster*>(targetPtr)};
|
||||
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 buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]};
|
||||
monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n);
|
||||
monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks);
|
||||
}
|
||||
const HurtList hurtList{game->Hurt(pos,bounceExplodeRadius,poolDamage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
|
||||
if(additionalBounceCount==0){
|
||||
for(const auto&[targetPtr,wasHit]:hurtList){
|
||||
if(wasHit){
|
||||
Monster*monsterPtr{std::get<Monster*>(targetPtr)};
|
||||
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 buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]};
|
||||
monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n);
|
||||
monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks);
|
||||
}
|
||||
}
|
||||
if(additionalBounceCount>0){
|
||||
additionalBounceCount--;
|
||||
risingTime=originalRisingTime;
|
||||
fallingTime=originalFallingTime;
|
||||
}else{
|
||||
vel={};
|
||||
fadeOutTime=0.25f;
|
||||
Deactivate();
|
||||
}
|
||||
}
|
||||
if(additionalBounceCount>0){
|
||||
additionalBounceCount--;
|
||||
risingTime=originalRisingTime;
|
||||
fallingTime=originalFallingTime;
|
||||
}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));
|
||||
}
|
||||
vel={};
|
||||
fadeOutTime=0.25f;
|
||||
Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,8 @@ Add unconscious monster state.
|
||||
DEMO
|
||||
====
|
||||
|
||||
Save file backup and rolling back on failure
|
||||
|
||||
|
||||
Add Sherman and Blacksmith story images
|
||||
|
||||
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_MINOR 3
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_BUILD 11725
|
||||
#define VERSION_BUILD 11748
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
@ -65,7 +65,6 @@ void VisualNovel::Initialize(){
|
||||
std::string line;
|
||||
std::string currentStory;
|
||||
while(file.good()){
|
||||
|
||||
auto trim = [](std::string& s)
|
||||
{
|
||||
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));
|
||||
}break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ void Witch::InitializeClassAbilities(){
|
||||
int additionalBounceCount{0};
|
||||
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))};
|
||||
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;
|
||||
};
|
||||
#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%)
|
||||
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
|
||||
{
|
||||
CombatSound = True
|
||||
|
@ -138,6 +138,7 @@ Images
|
||||
GFX_LineIndicator = line_indicator.png
|
||||
GFX_MusketBullet = musket_bullet.png
|
||||
GFX_SandSuction = sand_suction.png
|
||||
GFX_Bomb = bomb.png
|
||||
|
||||
GFX_Thief_Sheet = nico-thief.png
|
||||
GFX_Trapper_Sheet = nico-trapper.png
|
||||
|
@ -186,11 +186,12 @@ ItemDatabase
|
||||
{
|
||||
Description = Throw a bomb, dealing damage around an area.
|
||||
ItemCategory = Consumables
|
||||
ItemScript = Bomb
|
||||
ItemScript = Projectile
|
||||
Image = bomb.png
|
||||
Base Damage = 50
|
||||
Player Damage Mult = 1.6x
|
||||
Explode Radius = 350
|
||||
Explode Sound Effect = Bomb Explode
|
||||
Cast Size = 350
|
||||
Cast Range = 700
|
||||
Cooldown Time = 5.0
|
||||
Cast Time = 0.0
|
||||
@ -212,15 +213,18 @@ ItemDatabase
|
||||
{
|
||||
Description = Throw a molotov dealing damage and leave behind a burning area.
|
||||
ItemCategory = Consumables
|
||||
ItemScript = Bomb
|
||||
ItemScript = Projectile
|
||||
Image = molotov.png
|
||||
Base Damage = 50
|
||||
Player Damage Mult = 1.6x
|
||||
Explode Radius = 250
|
||||
Cast Size = 250
|
||||
Cast Range = 700
|
||||
Tick Rate = 1s
|
||||
Tick Base Damage = 10
|
||||
Tick Player Damage Mult = 0.4x
|
||||
Explode Sound Effect = Bomb Explode
|
||||
Lingering Effect = pixel.png
|
||||
Linger Radius = 250
|
||||
Linger Time = 4s
|
||||
Cooldown Time = 5.0
|
||||
Cast Time = 0.0
|
||||
|
@ -48,4 +48,22 @@ ItemScript
|
||||
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