Framework for Entity versions of player actions and first Shadow Boss (Warrior+Thief) setup. Release Build 13266.
All checks were successful
Emscripten Build / Build_and_Deploy_Web_Build (push) Successful in 8m30s
All checks were successful
Emscripten Build / Build_and_Deploy_Web_Build (push) Successful in 8m30s
This commit is contained in:
parent
fc47c1219b
commit
98b13568df
@ -39,6 +39,7 @@ All rights reserved.
|
||||
#include "Key.h"
|
||||
#include <string_view>
|
||||
#include "olcUTIL_DataFile.h"
|
||||
#include"Entity.h"
|
||||
|
||||
class InputGroup;
|
||||
|
||||
@ -89,7 +90,7 @@ struct Ability{
|
||||
//Ability action function, returns true if the ability can be casted, otherwise returns false.
|
||||
// Argument 1: Player* - player pointer
|
||||
// Argument 2: vf2d - The returned precast target position (if the ability needs to be aimed, otherwise {})
|
||||
std::function<bool(Player*,vf2d)>action=[](Player*,vf2d){return false;};
|
||||
std::function<bool(Entity,vf2d)>action=[](Entity,vf2d){return false;};
|
||||
static InputGroup DEFAULT;
|
||||
const float GetCooldownTime()const;
|
||||
const bool operator==(const Ability&a)const;
|
||||
|
||||
@ -1304,8 +1304,9 @@
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VisualNovel.cpp" />
|
||||
<ClCompile Include="Warrior.cpp" />
|
||||
<ClCompile Include="Warrior+Thief.cpp" />
|
||||
<ClCompile Include="util.cpp" />
|
||||
<ClCompile Include="Warrior.cpp" />
|
||||
<ClCompile Include="Wisp.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
|
||||
@ -782,9 +782,6 @@
|
||||
<ClCompile Include="LightningBoltEmitter.cpp">
|
||||
<Filter>Source Files\Emitters</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Warrior.cpp">
|
||||
<Filter>Source Files\Player Classes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emitter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1382,6 +1379,12 @@
|
||||
<ClCompile Include="Spider.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Warrior+Thief.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Warrior.cpp">
|
||||
<Filter>Source Files\Player Classes</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
|
||||
@ -3184,7 +3184,7 @@ int operator ""_I(const char*key,std::size_t len){
|
||||
return DATA.GetProperty(std::string(key,len)).GetInt();
|
||||
}
|
||||
|
||||
float operator ""_F(const char*key,std::size_t len){
|
||||
float operator""_F(const char*key,std::size_t len){
|
||||
AiL::OutputDebugInfo(key,len);
|
||||
return float(DATA.GetProperty(std::string(key,len)).GetReal());
|
||||
}
|
||||
@ -3873,7 +3873,7 @@ void AiL::SetLoadoutItem(int slot,std::string itemName){
|
||||
|
||||
switch(slot){
|
||||
case 0:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
itemAbility.action=[&](Entity e,vf2d pos={}){
|
||||
return game->UseLoadoutItem(0,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
||||
@ -3881,7 +3881,7 @@ void AiL::SetLoadoutItem(int slot,std::string itemName){
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_HUB_LOADOUT,"Loadout Item 1")->SetItem(loadout[slot]);
|
||||
}break;
|
||||
case 1:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
itemAbility.action=[&](Entity e,vf2d pos={}){
|
||||
return game->UseLoadoutItem(1,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
||||
@ -3889,7 +3889,7 @@ void AiL::SetLoadoutItem(int slot,std::string itemName){
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_HUB_LOADOUT,"Loadout Item 2")->SetItem(loadout[slot]);
|
||||
}break;
|
||||
case 2:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
itemAbility.action=[&](Entity e,vf2d pos={}){
|
||||
return game->UseLoadoutItem(2,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
||||
@ -3938,7 +3938,7 @@ void AiL::ClearLoadoutItem(int slot){
|
||||
Ability itemAbility{"???","","",0,0,inputGroup,""};
|
||||
switch(slot){
|
||||
case 0:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
itemAbility.action=[&](Entity e,vf2d pos={}){
|
||||
return game->UseLoadoutItem(0,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem1UseFunc(itemAbility);
|
||||
@ -3949,7 +3949,7 @@ void AiL::ClearLoadoutItem(int slot){
|
||||
Component<MenuItemItemButton>(MenuType::ITEM_HUB_LOADOUT,"Loadout Item 1")->UpdateIcon();
|
||||
}break;
|
||||
case 1:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
itemAbility.action=[&](Entity e,vf2d pos={}){
|
||||
return game->UseLoadoutItem(1,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem2UseFunc(itemAbility);
|
||||
@ -3961,7 +3961,7 @@ void AiL::ClearLoadoutItem(int slot){
|
||||
|
||||
}break;
|
||||
case 2:{
|
||||
itemAbility.action=[&](Player*p,vf2d pos={}){
|
||||
itemAbility.action=[&](Entity e,vf2d pos={}){
|
||||
return game->UseLoadoutItem(2,pos);
|
||||
};
|
||||
game->GetPlayer()->SetItem3UseFunc(itemAbility);
|
||||
|
||||
@ -67,4 +67,8 @@ namespace classutils{//Classes have bit-wise operator capabilities.
|
||||
static inline std::string ClassToString(const Class&cl){
|
||||
return classList.at(cl);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#define CONFIG_F(var)e.IsFriendly()?operator ""_F(std::string{var}.data(),std::string{var}.length()):Monster::STRATEGY::_GetFloat(*e.ToMonster(),var,(*e.ToMonster()).GetStrategyName())
|
||||
#define CONFIG_I(var)e.IsFriendly()?operator ""_I(std::string{var}.data(),std::string{var}.length()):Monster::STRATEGY::_GetInt(*e.ToMonster(),var,(*e.ToMonster()).GetStrategyName())
|
||||
#define CONFIG_S(var)e.IsFriendly()?operator ""_S(std::string{var}.data(),std::string{var}.length()):Monster::STRATEGY::_GetString(*e.ToMonster(),var,(*e.ToMonster()).GetStrategyName())
|
||||
@ -103,6 +103,13 @@ const bool Entity::OnUpperLevel()const{
|
||||
CallClassFunc(OnUpperLevel());
|
||||
}
|
||||
|
||||
Player*const Entity::ToPlayer()const{
|
||||
return get(Player*);
|
||||
}
|
||||
Monster*const Entity::ToMonster()const{
|
||||
return get(Monster*);
|
||||
}
|
||||
|
||||
const float Entity::GetMoveSpdMult()const{
|
||||
CallClassFunc(GetMoveSpdMult());
|
||||
}
|
||||
@ -118,4 +125,21 @@ const FriendlyType Entity::IsFriendly()const{
|
||||
|
||||
const float Entity::GetSizeMult()const{
|
||||
CallClassFunc(GetSizeMult());
|
||||
}
|
||||
|
||||
const bool Entity::CanMove()const{
|
||||
CallClassFunc(CanMove());
|
||||
}
|
||||
|
||||
const bool Entity::HasEnchant(const std::string&enchant)const{
|
||||
if(is(Player*))return get(Player*)->HasEnchant(enchant);
|
||||
return false;
|
||||
}
|
||||
|
||||
float&Entity::GetBlockTimer()const{
|
||||
CallClassFunc(blockTimer);
|
||||
}
|
||||
|
||||
Buff&Entity::AddBuff(BuffType type,float duration,float intensity){
|
||||
CallClassFunc(AddBuff(type,duration,intensity));
|
||||
}
|
||||
@ -43,6 +43,7 @@ All rights reserved.
|
||||
#include"HurtDamageInfo.h"
|
||||
#include"FriendlyType.h"
|
||||
#include"DEFINES.h"
|
||||
#include"State.h"
|
||||
|
||||
class Player;
|
||||
class Monster;
|
||||
@ -56,6 +57,8 @@ public:
|
||||
Entity(Player*player);
|
||||
Entity(Monster*monster);
|
||||
Entity(const std::variant<Monster*,Player*>ent);
|
||||
Player*const ToPlayer()const;
|
||||
Monster*const ToMonster()const;
|
||||
const vf2d GetPos()const;
|
||||
void SetIframeTime(const float iframeTime);
|
||||
Buff&GetOrAddBuff(BuffType buffType,std::pair<BuffDuration,BuffIntensity>newBuff);
|
||||
@ -71,6 +74,11 @@ public:
|
||||
const float GetSizeMult()const;
|
||||
const int GetAttack()const;
|
||||
const FriendlyType IsFriendly()const;
|
||||
const State::State GetState()const;
|
||||
const bool CanMove()const;
|
||||
const bool HasEnchant(const std::string&enchant)const;
|
||||
float&GetBlockTimer()const;
|
||||
Buff&AddBuff(BuffType type,float duration,float intensity);
|
||||
private:
|
||||
mutableconst std::variant<Monster*,Player*>entity;
|
||||
inline bool operator==(const Entity&rhs){return entity==rhs.entity;}
|
||||
|
||||
@ -284,6 +284,7 @@ void Monster::Update(const float fElapsedTime){
|
||||
lastPathfindingCooldown=std::max(0.f,lastPathfindingCooldown-fElapsedTime);
|
||||
markApplicationTimer=std::max(0.f,markApplicationTimer-fElapsedTime);
|
||||
specialMarkApplicationTimer=std::max(0.f,specialMarkApplicationTimer-fElapsedTime);
|
||||
blockTimer=std::max(0.f,blockTimer-fElapsedTime);
|
||||
|
||||
lastFacingDirectionChange+=fElapsedTime;
|
||||
timeSpentAlive+=fElapsedTime;
|
||||
@ -573,6 +574,7 @@ void Monster::Draw()const{
|
||||
};
|
||||
const auto DrawOverlayMonster=[&](vf2d scale={1.f,1.f},Pixel col=WHITE){
|
||||
game->view.DrawPartialRotatedDecal(drawPos,GFX[overlaySprite].Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,scale,col);
|
||||
if(blockTimer>0.f)game->view.DrawPartialRotatedDecal(drawPos,GFX["block.png"].Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,scale,col);
|
||||
};
|
||||
const auto DrawMountedMonster=[&](vf2d scale={1.f,1.f},Pixel col=WHITE){
|
||||
game->view.DrawPartialRotatedDecal(drawPos+mountedSprOffset,GetMountedFrame().value().GetSourceImage()->Decal(),finalSpriteRot,GetMountedFrame().value().GetSourceRect().size/2,GetMountedFrame().value().GetSourceRect().pos,GetMountedFrame().value().GetSourceRect().size,scale,col);
|
||||
@ -1822,4 +1824,5 @@ void Monster::SetTransparency(uint8_t alpha){
|
||||
|
||||
uint8_t Monster::GetTransparency()const{
|
||||
return transparency;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -85,6 +85,7 @@ class Monster:public IAttributable{
|
||||
friend class MonsterTests::MonsterTest;
|
||||
friend struct MonsterData;
|
||||
friend const bool Entity::IsBoss()const;
|
||||
friend class Entity;
|
||||
public:
|
||||
enum MonsterStrategy{
|
||||
RUN_TOWARDS,
|
||||
@ -128,6 +129,7 @@ public:
|
||||
SKELETON_CAPTAIN_FLAG,
|
||||
SKELETON_MAGE,
|
||||
SPIDER,
|
||||
WARRIORTHIEF,
|
||||
////////////////////////////////////
|
||||
////////////////////////////////////
|
||||
////////////////////////////////////
|
||||
@ -190,6 +192,7 @@ public:
|
||||
static void SKELETON_CAPTAIN_FLAG(Monster&m,float fElapsedTime,const std::string&strategy);
|
||||
static void SKELETON_MAGE(Monster&m,float fElapsedTime,const std::string&strategy);
|
||||
static void SPIDER(Monster&m,float fElapsedTime,const std::string&strategy);
|
||||
static void WARRIORTHIEF(Monster&m,float fElapsedTime,const std::string&strategy);
|
||||
};
|
||||
struct StrategyFunction{
|
||||
std::string name;
|
||||
@ -489,6 +492,7 @@ private:
|
||||
bool bumpedIntoTerrain=false; //Gets set to true before a strategy executes if the monster runs into some terrain on this frame.
|
||||
bool attackedByPlayer=false; //Gets set to true before a strategy executes if the monster has been attacked by the player.
|
||||
uint8_t transparency{255U};
|
||||
float blockTimer{};
|
||||
};
|
||||
|
||||
struct MonsterSpawner{
|
||||
|
||||
@ -184,4 +184,5 @@ enum class Attribute{
|
||||
FLAG_PTR,
|
||||
WAIT_ONE_TICK,
|
||||
ALPHA_AMOUNT,
|
||||
DEFENSIVE_COOLDOWN,
|
||||
};
|
||||
@ -100,6 +100,7 @@ class Player{
|
||||
friend class Inventory;
|
||||
friend class ItemInfo;
|
||||
friend void ItemOverlay::Draw();
|
||||
friend class Entity;
|
||||
public:
|
||||
Player();
|
||||
//So this is rather fascinating and only exists because we have the ability to change classes which means we need to initialize a class
|
||||
|
||||
@ -90,6 +90,7 @@ void Monster::InitializeStrategies(){
|
||||
ADD_STRATEGY(SKELETON_CAPTAIN_FLAG,"Skeleton Captain Flag");
|
||||
ADD_STRATEGY(SKELETON_MAGE,"Skeleton Mage");
|
||||
ADD_STRATEGY(SPIDER,"Spider");
|
||||
ADD_STRATEGY(WARRIORTHIEF,"Warrior+Thief");
|
||||
|
||||
if(!Monster::monsterStrategies.contains(SKELETON_MAGE))ERR("SKELETON_MAGE is a required dependency for Entity::IsSkeleletonMage()! THIS IS REQUIRED!!");
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@ bool Ranger::AutoAttack(){
|
||||
void Ranger::InitializeClassAbilities(){
|
||||
#pragma region Ranger Right-click Ability (Retreat)
|
||||
Ranger::rightClickAbility.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
geom2d::line mouseDir{p->GetWorldAimingLocation(Player::USE_WALK_DIR),p->GetPos()};
|
||||
float velocity=(0.5f*-p->friction*p->RETREAT_TIME*p->RETREAT_TIME-p->RETREAT_DISTANCE)/-p->RETREAT_TIME; //Derived from kinetic motion formula.
|
||||
p->SetVelocity(mouseDir.vector().norm()*velocity);
|
||||
@ -112,7 +112,7 @@ void Ranger::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Ranger Ability 1 (Rapid Fire)
|
||||
Ranger::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
p->remainingRapidFireShots=p->RAPID_FIRE_SHOOT_AMOUNT;
|
||||
if(p->HasEnchant("Extreme Rapid Fire"))p->remainingRapidFireShots+="Extreme Rapid Fire"_ENC["ARROW COUNT INCREASE"];
|
||||
p->rapidFireTimer=p->RAPID_FIRE_SHOOT_DELAY;
|
||||
@ -125,7 +125,7 @@ void Ranger::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Ranger Ability 2 (Charged Shot)
|
||||
Ranger::ability2.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
vf2d arrowVelocity=util::pointTo(p->GetPos(),p->GetWorldAimingLocation());
|
||||
|
||||
float beamRadius{12*"Ranger.Ability 2.Radius"_F/100};
|
||||
@ -146,7 +146,7 @@ void Ranger::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Ranger Ability 3 (Multi Shot)
|
||||
Ranger::ability3.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetWorldAimingLocation());
|
||||
float shootingDist=pointTowardsCursor.length();
|
||||
vf2d shootingDirMiddle=pointTowardsCursor.vector();
|
||||
|
||||
@ -106,7 +106,7 @@ bool Thief::AutoAttack(){
|
||||
void Thief::InitializeClassAbilities(){
|
||||
#pragma region Thief Right-click Ability (Roll)
|
||||
Thief::rightClickAbility.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
p->SetState(State::ROLL);
|
||||
p->rolling_timer="Thief.Right Click Ability.Roll Time"_F;
|
||||
|
||||
@ -137,7 +137,7 @@ void Thief::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Thief Ability 1 (Hidden Dagger)
|
||||
Thief::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
geom2d::line mouseDir{p->GetWorldAimingLocation(Player::USE_WALK_DIR),p->GetPos()};
|
||||
float velocity=(0.5f*-p->friction*"Thief.Ability 1.RetreatTime"_F*"Thief.Ability 1.RetreatTime"_F-24.f*"Thief.Ability 1.RetreatDistance"_F/100)/-"Thief.Ability 1.RetreatTime"_F; //Derived from kinetic motion formula.
|
||||
p->SetVelocity(mouseDir.vector().norm()*velocity);
|
||||
@ -156,7 +156,7 @@ void Thief::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Thief Ability 2 (Deadly Dash)
|
||||
Thief::ability2.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
game->AddEffect(ShineEffect{p->GetPos()+vf2d{4.f,4.f},0.5f,0.5f,"shine.png",1.5f,vf2d{},WHITE,util::random(2*PI),PI/2,true});
|
||||
p->ApplyIframes("Thief.Ability 2.Initial Wait"_F+"Thief.Ability 2.Ending Wait"_F+"Thief.Ability 2.Completed Dash Extra Iframe Time"_F);
|
||||
SoundEffect::PlaySFX("Charge Up",p->GetPos());
|
||||
@ -170,7 +170,7 @@ void Thief::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Thief Ability 3 (Adrenaline Rush)
|
||||
Thief::ability3.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
SoundEffect::PlaySFX("Adrenaline Rush",SoundEffect::CENTERED);
|
||||
|
||||
float adrenalineRushDuration{"Thief.Ability 3.Duration"_F};
|
||||
|
||||
@ -84,7 +84,7 @@ bool Trapper::AutoAttack(){
|
||||
void Trapper::InitializeClassAbilities(){
|
||||
#pragma region Trapper Right-click Ability (Sprint)
|
||||
Trapper::rightClickAbility.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
SoundEffect::PlaySFX("Sprint",SoundEffect::CENTERED);
|
||||
p->AddBuff(BuffType::SPEEDBOOST,"Trapper.Right Click Ability.Movement Speed Buff"_f[1],"Trapper.Right Click Ability.Movement Speed Buff"_f[0]/100.f);
|
||||
for(int i:std::ranges::iota_view(0,50)){
|
||||
@ -96,7 +96,7 @@ void Trapper::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Trapper Ability 1 (Mark Target)
|
||||
Trapper::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
std::optional<std::weak_ptr<Monster>>nearestMonster{Monster::GetNearestMonster(pos,Trapper::ability1.precastInfo.range,p->OnUpperLevel(),p->GetZ())};
|
||||
vf2d targetPos{pos};
|
||||
if(nearestMonster.has_value()){
|
||||
@ -116,7 +116,7 @@ void Trapper::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Trapper Ability 2 (Bear Trap)
|
||||
Trapper::ability2.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
CreateBullet(BearTrap)(p->GetPos(),"Trapper.Ability 2.Trap Radius"_I,"Trapper.Ability 2.DamageMult"_F*p->GetAttack(),0.2f,0.5f,p->OnUpperLevel(),false,INFINITE,FRIENDLY,WHITE,{1.f,1.f})EndBullet;
|
||||
SoundEffect::PlaySFX("Place Down Trap",p->GetPos());
|
||||
p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection());
|
||||
@ -125,7 +125,7 @@ void Trapper::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Trapper Ability 3 (Explosive Trap)
|
||||
Trapper::ability3.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
float trapDamage{"Trapper.Ability 3.DamageMult"_F*p->GetAttack()};
|
||||
if(p->HasEnchant("Concussive Trap"))trapDamage+=p->GetAttack()*"Concussive Trap"_ENC["ADDITIONAL EXPLOSION DAMAGE"]/100.f;
|
||||
CreateBullet(ExplosiveTrap)(p->GetPos(),"Trapper.Ability 3.Trap Radius"_I,"Trapper.Ability 3.Explosion Radius"_F/100.f*24,"Trapper.Ability 3.Trap Auto Detonate Time"_F,trapDamage,0.2f,0.5f,"Trapper.Ability 3.Trap Activation Time"_F,p->OnUpperLevel(),false,INFINITE,FRIENDLY,WHITE,{1.f,1.f})EndBullet;
|
||||
|
||||
@ -39,7 +39,7 @@ All rights reserved.
|
||||
#define VERSION_MAJOR 1
|
||||
#define VERSION_MINOR 3
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_BUILD 13243
|
||||
#define VERSION_BUILD 13266
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
||||
63
Adventures in Lestoria/Warrior+Thief.cpp
Normal file
63
Adventures in Lestoria/Warrior+Thief.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma region License
|
||||
/*
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2026 Amy 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 "Monster.h"
|
||||
#include"AdventuresInLestoria.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
DEFINE_STRATEGY(WARRIORTHIEF)
|
||||
enum Phase{
|
||||
INIT,
|
||||
RUN,
|
||||
};
|
||||
switch(PHASE()){
|
||||
case INIT:{
|
||||
m.F(A::DEFENSIVE_COOLDOWN)=0.f;
|
||||
}break;
|
||||
case RUN:{
|
||||
m.F(A::DEFENSIVE_COOLDOWN)-=game->GetElapsedTime();
|
||||
|
||||
const float distToPlayer{util::distance(m.GetPos(),game->GetPlayer()->GetPos())};
|
||||
if(distToPlayer<100.f&&m.F(A::DEFENSIVE_COOLDOWN)<=0.f){
|
||||
m.F(A::DEFENSIVE_COOLDOWN)=ConfigFloat("Warrior.Right Click Ability.Cooldown");
|
||||
Warrior::rightClickAbility.action(&m,m.GetPos());
|
||||
}
|
||||
}break;
|
||||
}
|
||||
END_STRATEGY
|
||||
@ -104,26 +104,32 @@ bool Warrior::AutoAttack(){
|
||||
void Warrior::InitializeClassAbilities(){
|
||||
#pragma region Warrior Right-click Ability (Block)
|
||||
Warrior::rightClickAbility.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
if(p->GetState()==State::NORMAL||p->GetState()==State::CASTING){
|
||||
rightClickAbility.cooldown=rightClickAbility.COOLDOWN_TIME;
|
||||
float blockTime{"Warrior.Right Click Ability.Duration"_F};
|
||||
if(p->HasEnchant("Heavy Guard"))blockTime*="Heavy Guard"_ENC["BLOCK DURATION MULT"];
|
||||
if(p->HasEnchant("Advance Shield")){
|
||||
p->AddShield(p->GetMaxHealth()*"Advance Shield"_ENC["SHIELD AMOUNT"]/100.f,"Advance Shield"_ENC["SHIELD DURATION"],PlayerTimerType::ADVANCE_SHIELD_TIMER);
|
||||
}else{
|
||||
p->blockTimer=blockTime;
|
||||
p->SetState(State::BLOCK);
|
||||
p->AddBuff(BuffType::BLOCK_SLOWDOWN,p->blockTimer,"Warrior.Right Click Ability.SlowAmt"_F);
|
||||
[](Entity e,vf2d pos={}){
|
||||
auto GetFloat{[](const char*key,std::size_t len){
|
||||
AiL::OutputDebugInfo(key,len);
|
||||
return float(DATA.GetProperty(std::string(key,len)).GetReal());
|
||||
}};
|
||||
if(e.IsFriendly()&&(e.ToPlayer()->GetState()==State::NORMAL||e.ToPlayer()->GetState()==State::CASTING)||
|
||||
!e.IsFriendly()&&e.CanMove()){
|
||||
rightClickAbility.cooldown=rightClickAbility.COOLDOWN_TIME;
|
||||
float blockTime{e.IsFriendly() ? GetFloat(std::string{"Warrior.Right Click Ability.Duration"}.data(),std::string{"Warrior.Right Click Ability.Duration"}.length()) : Monster::STRATEGY::_GetFloat(*e.ToMonster(),"Warrior.Right Click Ability.Duration",(*e.ToMonster()).GetStrategyName())};
|
||||
if(e.HasEnchant("Heavy Guard"))blockTime*="Heavy Guard"_ENC["BLOCK DURATION MULT"];
|
||||
if(e.HasEnchant("Advance Shield")){
|
||||
if(e.IsFriendly())e.ToPlayer()->AddShield(e.ToPlayer()->GetMaxHealth()*"Advance Shield"_ENC["SHIELD AMOUNT"]/100.f,"Advance Shield"_ENC["SHIELD DURATION"],PlayerTimerType::ADVANCE_SHIELD_TIMER);
|
||||
}else{
|
||||
float&blockTimer{e.GetBlockTimer()};
|
||||
blockTimer=blockTime;
|
||||
if(e.IsFriendly())e.ToPlayer()->SetState(State::BLOCK);
|
||||
e.AddBuff(BuffType::BLOCK_SLOWDOWN,blockTimer,e.IsFriendly() ? GetFloat(std::string{"Warrior.Right Click Ability.SlowAmt"}.data(),std::string{"Warrior.Right Click Ability.SlowAmt"}.length()) : Monster::STRATEGY::_GetFloat(*e.ToMonster(),"Warrior.Right Click Ability.SlowAmt",(*e.ToMonster()).GetStrategyName()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
#pragma endregion
|
||||
#pragma region Warrior Ability 1 (Battlecry)
|
||||
Warrior::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
game->AddEffect(Effect{p->GetPos(),"Warrior.Ability 1.EffectLifetime"_F,"battlecry_effect.png",p->upperLevel,"Warrior.Ability 1.Range"_F/350,"Warrior.Ability 1.EffectFadetime"_F});
|
||||
p->AddBuff(BuffType::STAT_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F,{"Attack %"});
|
||||
p->AddBuff(BuffType::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
|
||||
@ -139,7 +145,7 @@ void Warrior::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Warrior Ability 2 (Ground Slam)
|
||||
Warrior::ability2.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
p->Spin(GROUND_SLAM_SPIN_TIME,"Warrior.Ability 2.SpinSpd"_F*PI);
|
||||
p->iframe_time="Warrior.Ability 2.IframeTime"_F;
|
||||
return true;
|
||||
@ -147,7 +153,7 @@ void Warrior::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Warrior Ability 3 (Sonic Slash)
|
||||
Warrior::ability3.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
p->SetState(State::SWING_SONIC_SWORD);
|
||||
p->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 3.StuckTime"_F,1);
|
||||
vf2d bulletVel={};
|
||||
|
||||
@ -116,7 +116,7 @@ bool Witch::AutoAttack(){
|
||||
void Witch::InitializeClassAbilities(){
|
||||
#pragma region Witch Right-click Ability (Transform)
|
||||
Witch::rightClickAbility.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
p->SetupAfterImage();
|
||||
p->afterImagePos=p->leapStartingPos=p->GetPos();
|
||||
geom2d::line<float>targetLine{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)};
|
||||
@ -136,7 +136,7 @@ void Witch::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Witch Ability 1 (Curse of Pain)
|
||||
Witch::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
|
||||
if(curseTarget.has_value()&&!curseTarget.value().expired()){
|
||||
//NOTE: If we have to change/modify Curse of Pain, we must also modify it in Monster::OnDeath (Monster.cpp)
|
||||
@ -165,7 +165,7 @@ void Witch::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Witch Ability 2 (Throw Poison)
|
||||
Witch::ability2.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
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))};
|
||||
@ -175,7 +175,7 @@ void Witch::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Witch Ability 3 (Curse of Death)
|
||||
Witch::ability3.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 3.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
|
||||
if(curseTarget.has_value()&&!curseTarget.value().expired()){
|
||||
const float curseDuration{"Witch.Ability 3.Curse Duration"_F};
|
||||
|
||||
@ -113,7 +113,7 @@ bool Wizard::AutoAttack(){
|
||||
void Wizard::InitializeClassAbilities(){
|
||||
#pragma region Wizard Right-click Ability (Teleport)
|
||||
Wizard::rightClickAbility.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
float pointMouseDirection=atan2(p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED).y-p->GetPos().y,p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED).x-p->GetPos().x);
|
||||
vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)};
|
||||
float dist=std::clamp(geom2d::line<float>{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)}.length(),0.f,"Wizard.Right Click Ability.TeleportRange"_F/100*24);
|
||||
@ -180,7 +180,7 @@ void Wizard::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Wizard Ability 1 (Fire Bolt)
|
||||
Wizard::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
float angleToCursor=atan2(p->GetWorldAimingLocation().y-p->GetPos().y,p->GetWorldAimingLocation().x-p->GetPos().x);
|
||||
CreateBullet(FireBolt)(p->GetPos(),{cos(angleToCursor)*"Wizard.Ability 1.BulletSpeed"_F,sin(angleToCursor)*"Wizard.Ability 1.BulletSpeed"_F},"Wizard.Ability 1.Radius"_F/100*12,int(p->GetAttack()*"Wizard.Ability 1.InitialDamageMult"_F),p->upperLevel,FRIENDLY,"Wizard.Ability 1.BulletColor"_Pixel)EndBullet;
|
||||
return true;
|
||||
@ -188,7 +188,7 @@ void Wizard::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Wizard Ability 2 (Lightning Bolt)
|
||||
Wizard::ability2.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
float angleToCursor=atan2(p->GetWorldAimingLocation().y-p->GetPos().y,p->GetWorldAimingLocation().x-p->GetPos().x);
|
||||
CreateBullet(LightningBolt)(p->GetPos(),{cos(angleToCursor)*"Wizard.Ability 2.BulletSpeed"_F,sin(angleToCursor)*"Wizard.Ability 2.BulletSpeed"_F},"Wizard.Ability 2.Radius"_F/100*12,int(p->GetAttack()*"Wizard.Ability 2.DamageMult"_F),p->upperLevel,FRIENDLY,"Wizard.Ability 2.BulletColor"_Pixel)EndBullet;
|
||||
return true;
|
||||
@ -196,7 +196,7 @@ void Wizard::InitializeClassAbilities(){
|
||||
#pragma endregion
|
||||
#pragma region Wizard Ability 3 (Meteor)
|
||||
Wizard::ability3.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
[](Entity e,vf2d pos={}){Player*p=e.ToPlayer(); //TODO
|
||||
Meteor::MeteorSetting meteorType{Meteor::METEOR};
|
||||
if(p->HasEnchant("Summon Comet")&&p->HasEnchant("Solar Flare"))meteorType=Meteor::COMET_FLARE;
|
||||
else if(p->HasEnchant("Summon Comet"))meteorType=Meteor::COMET;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="262" height="167" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="72">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="262" height="167" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="72">
|
||||
<properties>
|
||||
<property name="Backdrop" propertytype="Backdrop" value="mountain_day"/>
|
||||
<property name="Background Music" propertytype="BGM" value="mountain"/>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="76" height="85" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="33">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="76" height="85" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="34">
|
||||
<properties>
|
||||
<property name="Background Music" propertytype="BGM" value="beach_boss"/>
|
||||
<property name="Level Type" type="int" propertytype="LevelType" value="1"/>
|
||||
@ -570,12 +570,12 @@
|
||||
</properties>
|
||||
<ellipse/>
|
||||
</object>
|
||||
<object id="31" template="../maps/Monsters/Ghost of Pirate Captain.tx" x="774" y="1212">
|
||||
<object id="32" template="../maps/Monsters/Pirate's Treasure.tx" x="834" y="1506">
|
||||
<properties>
|
||||
<property name="spawner" type="object" value="30"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="32" template="../maps/Monsters/Pirate's Treasure.tx" x="834" y="1506">
|
||||
<object id="33" template="../maps/Monsters/Warrior+Thief.tx" x="906" y="1116">
|
||||
<properties>
|
||||
<property name="spawner" type="object" value="30"/>
|
||||
</properties>
|
||||
|
||||
@ -1476,4 +1476,10 @@ MonsterStrategy
|
||||
Hide Range = 500
|
||||
Hide Fade Speed = 0.5s
|
||||
}
|
||||
Warrior+Thief
|
||||
{
|
||||
Warrior.Right Click Ability.Duration = 3s
|
||||
Warrior.Right Click Ability.SlowAmt = 0.3
|
||||
Warrior.Right Click Ability.Cooldown = 15s
|
||||
}
|
||||
}
|
||||
@ -2499,4 +2499,46 @@ Monsters
|
||||
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
|
||||
# DROP[0] = Frog Skin,50%,1,1
|
||||
}
|
||||
Warrior+Thief
|
||||
{
|
||||
Health = 14000
|
||||
Attack = 40
|
||||
|
||||
CollisionDmg = 40
|
||||
|
||||
MoveSpd = 100%
|
||||
Size = 100%
|
||||
|
||||
XP = 0
|
||||
|
||||
# A flag to show an arrow indicator when the boss is off-screen.
|
||||
ShowBossIndicator = True
|
||||
|
||||
Strategy = Warrior+Thief
|
||||
|
||||
#Size of each animation frame
|
||||
SheetFrameSize = 24,24
|
||||
|
||||
# Setting this to true means every four rows indicates one animation, the ordering of the directions is: NORTH, EAST, SOUTH, WEST
|
||||
4-Way Spritesheet = True
|
||||
|
||||
Animations
|
||||
{
|
||||
# Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse,ReverseOneShot)
|
||||
# Animations must be defined in the same order as they are in their sprite sheets
|
||||
# The First Four animations must represent a standing, walking, attack, and death animation. Their names are up to the creator.
|
||||
IDLE = 4, 0.3, Repeat
|
||||
HURT = 4, 0.25, Repeat
|
||||
ATTACKING = 4, 0.15, PingPong
|
||||
DEATH = 4, 0.15, OneShot
|
||||
}
|
||||
|
||||
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
|
||||
DROP[0] = Octopus Ring,100%,1,1
|
||||
DROP[1] = Takoyaki,100%,1,1
|
||||
|
||||
Hurt Sound = Monster Hurt
|
||||
Death Sound = Slime Dead
|
||||
Walk Sound = Slime Walk
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="24" tileheight="24" infinite="1" nextlayerid="3" nextobjectid="58">
|
||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="24" tileheight="24" infinite="1" nextlayerid="3" nextobjectid="59">
|
||||
<tileset firstgid="1" source="Monsters.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="30" height="20">
|
||||
<data encoding="csv"/>
|
||||
@ -47,5 +47,6 @@
|
||||
<object id="52" name="Spider" type="Monster" gid="42" x="498" y="642" width="33.6" height="33.6"/>
|
||||
<object id="54" template="Monsters/Purple Slime.tx" type="Monster" x="486" y="714"/>
|
||||
<object id="56" template="Monsters/Giant Purple Slime.tx" type="Monster" x="522" y="684"/>
|
||||
<object id="58" template="Monsters/Warrior+Thief.tx" type="Monster" x="420" y="816"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<tileset firstgid="1" source="../Monsters.tsx"/>
|
||||
<object name="Warrior+Thief" type="Monster" gid="45" width="48" height="48"/>
|
||||
</template>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user