The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'!
https://forums.lestoria.net
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
6.3 KiB
174 lines
6.3 KiB
#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 "ItemDrop.h"
|
|
#include "olcUTIL_Geometry2D.h"
|
|
#include "AdventuresInLestoria.h"
|
|
#include "SoundEffect.h"
|
|
|
|
INCLUDE_game
|
|
INCLUDE_GFX
|
|
|
|
float ItemDrop::gravity;
|
|
std::vector<ItemDrop>ItemDrop::drops;
|
|
|
|
void ItemDrop::Initialize(){
|
|
drops.clear();
|
|
gravity="ItemDrop.Item Drop Gravity"_F;
|
|
}
|
|
|
|
ItemDrop::ItemDrop(const ItemInfo*item,const vf2d pos,const bool isUpper)
|
|
:item(item),pos(pos),upperLevel(isUpper){
|
|
const bool HasBossArenaBounds=game->GetCurrentMap().GetMapType()==Map::MapType::BOSS;
|
|
if(HasBossArenaBounds){
|
|
const geom2d::rect<int>arenaBounds=game->GetZones().at("BossArena")[0].zone;
|
|
this->pos.x=std::clamp(this->pos.x,float(arenaBounds.pos.x),float(arenaBounds.pos.x+arenaBounds.size.x));
|
|
this->pos.y=std::clamp(this->pos.y,float(arenaBounds.pos.y),float(arenaBounds.pos.y+arenaBounds.size.y));
|
|
}
|
|
speed.x=util::random_range("ItemDrop.Item Drop Horizontal Speed"_f[0],"ItemDrop.Item Drop Horizontal Speed"_f[1]);
|
|
speed.y=util::random_range("ItemDrop.Item Drop Vertical Speed"_f[0],"ItemDrop.Item Drop Vertical Speed"_f[1]);
|
|
zSpeed="ItemDrop.Item Drop Initial Rise Speed"_F;
|
|
randomSpinOffset=util::random(PI/2);
|
|
}
|
|
|
|
vf2d ItemDrop::GetPos()const{
|
|
return pos;
|
|
}
|
|
|
|
bool ItemDrop::OnUpperLevel(){
|
|
return upperLevel;
|
|
}
|
|
|
|
void ItemDrop::Draw()const{
|
|
#pragma region Item Drop Shadow Rendering
|
|
if(GetZ()>0){
|
|
vf2d shadowScale=vf2d{8*"ItemDrop.Item Drop Scale"_F/3.f,1}/std::max(1.f,GetZ()/24);
|
|
game->view.DrawDecal(GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*"ItemDrop.Item Drop Scale"_F},GFX["circle.png"].Decal(),shadowScale,BLACK);
|
|
}
|
|
#pragma endregion
|
|
|
|
float yOffset=0;
|
|
if(GetZ()==0){
|
|
yOffset=sin((game->levelTime+randomSpinOffset)*3)*0.5f;
|
|
}
|
|
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},GFX["skill_overlay_icon_overlay.png"].Decal(),0,GFX["skill_overlay_icon_overlay.png"].Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},YELLOW);
|
|
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},const_cast<Decal*>(item->Icon().Decal()),0,item->Icon().Sprite()->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{255,255,255,128});
|
|
game->SetDecalMode(DecalMode::ADDITIVE);
|
|
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},const_cast<Decal*>(item->Icon().Decal()),0,item->Icon().Sprite()->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),128});
|
|
game->SetDecalMode(DecalMode::NORMAL);
|
|
}
|
|
|
|
void ItemDrop::UpdateDrops(float fElapsedTime){
|
|
for(ItemDrop&drop:drops){
|
|
#pragma region Handle Z Speed
|
|
drop.z+=drop.zSpeed*fElapsedTime;
|
|
if(drop.z<=0){
|
|
drop.zSpeed=0;
|
|
drop.z=0;
|
|
}
|
|
else{
|
|
drop.zSpeed+=gravity*fElapsedTime;
|
|
drop.pos+=drop.speed*fElapsedTime;
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region Check for Suction / Player pull-in
|
|
if(drop.zSpeed==0&&drop.OnUpperLevel()==game->GetPlayer()->OnUpperLevel()){
|
|
geom2d::line<float>lineTo=geom2d::line<float>(drop.pos,game->GetPlayer()->GetPos());
|
|
float dist=lineTo.length();
|
|
if(dist<="ItemDrop.Item Drop Suction Range"_F){
|
|
vf2d pointVel=lineTo.vector().norm();
|
|
float moveDistance=(1.f/std::min(48.f,dist))*"ItemDrop.Item Drop Suction Strength"_F*fElapsedTime;
|
|
if(moveDistance>dist){
|
|
drop.pos=game->GetPlayer()->GetPos();
|
|
drop.collected=true;
|
|
}else{
|
|
drop.pos+=pointVel*moveDistance;
|
|
}
|
|
}
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region Handle Upper/Lower Level Zone Intersecting
|
|
if(drop.speed.mag()>0){
|
|
const std::map<std::string,std::vector<ZoneData>>&zoneData=game->GetZones(game->GetCurrentLevel());
|
|
for(const ZoneData&upperLevelZone:zoneData.at("UpperZone")){
|
|
if(geom2d::overlaps(upperLevelZone.zone,drop.pos)){
|
|
drop.upperLevel=true;
|
|
}
|
|
}
|
|
for(const ZoneData&lowerLevelZone:zoneData.at("LowerZone")){
|
|
if(geom2d::overlaps(lowerLevelZone.zone,drop.pos)){
|
|
drop.upperLevel=false;
|
|
}
|
|
}
|
|
}
|
|
#pragma endregion
|
|
}
|
|
|
|
std::erase_if(drops,[](ItemDrop&drop){
|
|
if(drop.collected){
|
|
Inventory::AddItem(drop.GetItem()->Name(),1,true);
|
|
ItemOverlay::AddToItemOverlay(*drop.GetItem());
|
|
SoundEffect::PlaySFX("Collect Item",SoundEffect::CENTERED);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
ItemOverlay::Update();
|
|
}
|
|
|
|
float ItemDrop::GetZ()const{
|
|
return z;
|
|
}
|
|
|
|
void ItemDrop::SpawnItem(const ItemInfo*item,vf2d pos,bool isUpper){
|
|
drops.push_back(ItemDrop{item,pos,isUpper});
|
|
}
|
|
|
|
const ItemInfo*ItemDrop::GetItem()const{
|
|
return item;
|
|
}
|
|
|
|
const std::vector<ItemDrop>&ItemDrop::GetDrops(){
|
|
return ItemDrop::drops;
|
|
}
|
|
|
|
void ItemDrop::ClearDrops(){
|
|
ItemDrop::drops.clear();
|
|
} |