mirror of
https://github.com/sigonasr2/hamster.git
synced 2025-04-17 22:29:40 -05:00
Added Fall-o-meter HUD elements, added crash landing mechanics, and fuel.
This commit is contained in:
parent
5b188afc85
commit
002b00f018
BIN
assets/fuelbar_outline.png
Normal file
BIN
assets/fuelbar_outline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 778 B |
Binary file not shown.
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 17 KiB |
194
src/Hamster.cpp
194
src/Hamster.cpp
@ -92,61 +92,7 @@ void Hamster::UpdateHamsters(const float fElapsedTime){
|
|||||||
h.imgScale=1.f;
|
h.imgScale=1.f;
|
||||||
h.drownTimer=0.f;
|
h.drownTimer=0.f;
|
||||||
if(!h.lastSafeLocation.has_value()){
|
if(!h.lastSafeLocation.has_value()){
|
||||||
#pragma region Safe Terrain Outline Search Algorithm
|
h.lastSafeLocation=h.GetNearestSafeLocation();
|
||||||
{
|
|
||||||
using TilePos=vi2d;
|
|
||||||
using TileDistance=int;
|
|
||||||
|
|
||||||
const vi2d playerTile{h.GetPos()/16};
|
|
||||||
geom2d::rect<int>searchRect{{-1,-1},{3,3}};
|
|
||||||
std::optional<std::pair<TilePos,TileDistance>>closestTile;
|
|
||||||
|
|
||||||
const auto DetermineAndUpdateClosestTile=[&h,&playerTile,&closestTile](const vi2d&tile){
|
|
||||||
if(!h.IsLethalTerrain(tile*16)){
|
|
||||||
std::pair<TilePos,TileDistance>closest{closestTile.value_or(std::pair<TilePos,TileDistance>{{},std::numeric_limits<int>::max()})};
|
|
||||||
int tileDist{abs(playerTile.x-tile.x)+abs(playerTile.y-tile.y)};
|
|
||||||
if(tileDist<=closest.second)closestTile.emplace(std::pair<TilePos,TileDistance>{tile,tileDist});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
while(!closestTile.has_value()){
|
|
||||||
#pragma region Top Outline Check
|
|
||||||
{
|
|
||||||
for(int offsetX:std::ranges::iota_view(searchRect.pos.x,searchRect.size.x)){
|
|
||||||
const vi2d checkTile{playerTile+vi2d{offsetX,searchRect.top().end.y}};
|
|
||||||
DetermineAndUpdateClosestTile(checkTile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma endregion
|
|
||||||
#pragma region Bottom Outline Check
|
|
||||||
{
|
|
||||||
for(int offsetX:std::ranges::iota_view(searchRect.pos.x,searchRect.size.x)){
|
|
||||||
const vi2d checkTile{playerTile+vi2d{offsetX,searchRect.bottom().end.y}};
|
|
||||||
DetermineAndUpdateClosestTile(checkTile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma endregion
|
|
||||||
#pragma region Right Outline Check
|
|
||||||
{
|
|
||||||
for(int offsetY:std::ranges::iota_view(searchRect.pos.y+1,searchRect.size.y-2+1)){
|
|
||||||
const vi2d checkTile{playerTile+vi2d{searchRect.right().end.x,offsetY}};
|
|
||||||
DetermineAndUpdateClosestTile(checkTile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma endregion
|
|
||||||
#pragma region Left Outline Check
|
|
||||||
{
|
|
||||||
for(int offsetY:std::ranges::iota_view(searchRect.pos.y+1,searchRect.size.y-2+1)){
|
|
||||||
const vi2d checkTile{playerTile+vi2d{searchRect.left().end.x,offsetY}};
|
|
||||||
DetermineAndUpdateClosestTile(checkTile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma endregion
|
|
||||||
searchRect.pos-=1;
|
|
||||||
searchRect.size+=2;
|
|
||||||
}
|
|
||||||
h.lastSafeLocation=closestTile.value().first*16+8;
|
|
||||||
}
|
|
||||||
#pragma endregion
|
|
||||||
}
|
}
|
||||||
h.SetPos(h.lastSafeLocation.value());
|
h.SetPos(h.lastSafeLocation.value());
|
||||||
h.state=NORMAL;
|
h.state=NORMAL;
|
||||||
@ -162,6 +108,13 @@ void Hamster::UpdateHamsters(const float fElapsedTime){
|
|||||||
h.state=WAIT;
|
h.state=WAIT;
|
||||||
}
|
}
|
||||||
}break;
|
}break;
|
||||||
|
case KNOCKOUT:{
|
||||||
|
h.knockoutTimer-=fElapsedTime;
|
||||||
|
if(h.knockoutTimer<=0.f){
|
||||||
|
h.state=NORMAL;
|
||||||
|
h.animations.ChangeState(h.internalAnimState,HamsterGame::DEFAULT);
|
||||||
|
}
|
||||||
|
}break;
|
||||||
}
|
}
|
||||||
if(h.hamsterJet.has_value())h.hamsterJet.value().Update(fElapsedTime);
|
if(h.hamsterJet.has_value())h.hamsterJet.value().Update(fElapsedTime);
|
||||||
if(h.state!=FLYING){
|
if(h.state!=FLYING){
|
||||||
@ -182,6 +135,12 @@ void Hamster::UpdateHamsters(const float fElapsedTime){
|
|||||||
}
|
}
|
||||||
h.TurnTowardsTargetDirection();
|
h.TurnTowardsTargetDirection();
|
||||||
h.MoveHamster();
|
h.MoveHamster();
|
||||||
|
if(h.IsPlayerControlled){
|
||||||
|
h.hamsterJetDisplay.Update(fElapsedTime);
|
||||||
|
h.hamsterJetLightsDisplay.Update(fElapsedTime);
|
||||||
|
h.readyFlashTimer+=fElapsedTime;
|
||||||
|
h.jetFuelDisplayAmt+=(h.jetFuel-h.jetFuelDisplayAmt)*4.f*fElapsedTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +153,8 @@ void Hamster::LoadHamsters(const vf2d startingLoc){
|
|||||||
for(int i:std::ranges::iota_view(0U,NPC_HAMSTER_COUNT)){
|
for(int i:std::ranges::iota_view(0U,NPC_HAMSTER_COUNT)){
|
||||||
HAMSTER_LIST.emplace_back(startingLoc,NPC_HAMSTER_IMAGES.at(util::random()%NPC_HAMSTER_IMAGES.size()),NPC);
|
HAMSTER_LIST.emplace_back(startingLoc,NPC_HAMSTER_IMAGES.at(util::random()%NPC_HAMSTER_IMAGES.size()),NPC);
|
||||||
}
|
}
|
||||||
|
playerHamster.value()->hamsterJetDisplay.Initialize("hamster_jet.png",{78,223,208},{79,81,128});
|
||||||
|
playerHamster.value()->hamsterJetLightsDisplay.Initialize("hamster_jet.png",{245,233,130},{245,233,130});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hamster::DrawHamsters(TransformedView&tv){
|
void Hamster::DrawHamsters(TransformedView&tv){
|
||||||
@ -216,10 +177,44 @@ void Hamster::DrawHamsters(TransformedView&tv){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Hamster::DrawOverlay(){
|
void Hamster::DrawOverlay(){
|
||||||
if(GetPlayer().hamsterJet.has_value()){
|
if(GetPlayer().hamsterJet.has_value())GetPlayer().hamsterJet.value().DrawOverlay();
|
||||||
GetPlayer().hamsterJet.value().DrawOverlay();
|
|
||||||
HamsterGame::Game().DrawDecal(vf2d{96.f,0.f}+HamsterGame::SCREEN_FRAME.size,HamsterGame::GetGFX("fuelmeter.png").Decal());
|
const vf2d jetDisplayOffset{HamsterGame::SCREEN_FRAME.pos+vf2d{HamsterGame::SCREEN_FRAME.size.x,0.f}};
|
||||||
|
Pixel jetDisplayCol{VERY_DARK_GREY};
|
||||||
|
if(!GetPlayer().hamsterJet.has_value()){
|
||||||
|
if(GetPlayer().HasPowerup(Powerup::JET))jetDisplayCol=WHITE;
|
||||||
|
const Animate2D::FrameSequence&lightAnim{HamsterGame::Game().GetAnimation("hamster_jet.png",HamsterGame::AnimationState::JET_LIGHTS)};
|
||||||
|
const Animate2D::Frame&lightFrame{lightAnim.GetFrame(HamsterGame::Game().GetRuntime())};
|
||||||
|
HamsterGame::Game().DrawPartialRotatedDecal(jetDisplayOffset+vf2d{48.f,80.f},GetPlayer().hamsterJetDisplay.Decal(),0.f,{24.f,24.f},{0.f,0.f},{48.f,48.f},{2.f,2.f},jetDisplayCol);
|
||||||
|
HamsterGame::Game().DrawPartialRotatedDecal(jetDisplayOffset+vf2d{48.f,80.f},GetPlayer().hamsterJetLightsDisplay.Decal(),0.f,{24.f,24.f},lightFrame.GetSourceRect().pos,lightFrame.GetSourceRect().size,{2.f,2.f},jetDisplayCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(GetPlayer().HasPowerup(Powerup::JET)&&!GetPlayer().hamsterJet.has_value()){
|
||||||
|
const std::string readyText{"READY!"};
|
||||||
|
const vi2d textSize{HamsterGame::Game().GetTextSize(readyText)};
|
||||||
|
for(int y:std::ranges::iota_view(-1,2)){
|
||||||
|
for(int x:std::ranges::iota_view(-1,2)){
|
||||||
|
if(x==0&&y==0)continue;
|
||||||
|
HamsterGame::Game().DrawRotatedStringDecal(jetDisplayOffset+vf2d{48.f,116.f}+vi2d{x,y},readyText,0.f,textSize/2,fmod(GetPlayer().readyFlashTimer,1.5f)<=0.75f?DARK_RED:BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HamsterGame::Game().DrawRotatedStringDecal(jetDisplayOffset+vf2d{48.f,116.f},readyText,0.f,textSize/2,GREEN);
|
||||||
|
HamsterGame::Game().DrawDecal(HamsterGame::SCREEN_FRAME.pos+vf2d{HamsterGame::SCREEN_FRAME.size.x,0.f},HamsterGame::GetGFX("fuelmeter.png").Decal());
|
||||||
|
const std::string launchText{"(SPACE)x2\nto Launch!"};
|
||||||
|
const vi2d launchTextSize{HamsterGame::Game().GetTextSize(launchText)};
|
||||||
|
for(int y:std::ranges::iota_view(-1,2)){
|
||||||
|
for(int x:std::ranges::iota_view(-1,2)){
|
||||||
|
if(x==0&&y==0)continue;
|
||||||
|
HamsterGame::Game().DrawRotatedStringDecal(jetDisplayOffset+vf2d{48.f,224.f}+vi2d{x,y},launchText,0.f,launchTextSize/2,BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HamsterGame::Game().DrawRotatedStringDecal(jetDisplayOffset+vf2d{48.f,224.f},launchText,0.f,launchTextSize/2,WHITE);
|
||||||
|
}else{
|
||||||
|
HamsterGame::Game().DrawPartialDecal(HamsterGame::SCREEN_FRAME.pos+vf2d{HamsterGame::SCREEN_FRAME.size.x,0.f},HamsterGame::GetGFX("fuelmeter.png").Decal(),{0,0},{96,200});
|
||||||
|
}
|
||||||
|
const float jetFuelBarHeight{float(HamsterGame::GetGFX("fuelbar.png").Sprite()->height)};
|
||||||
|
HamsterGame::Game().DrawPartialDecal(jetDisplayOffset+vf2d{24.f,139.f}+vf2d{0.f,jetFuelBarHeight*(1.f-GetPlayer().jetFuelDisplayAmt)},HamsterGame::GetGFX("fuelbar.png").Decal(),{0.f,jetFuelBarHeight*(1.f-GetPlayer().jetFuelDisplayAmt)},{float(HamsterGame::GetGFX("fuelbar.png").Sprite()->width),jetFuelBarHeight*(GetPlayer().jetFuelDisplayAmt)});
|
||||||
|
if(GetPlayer().HasPowerup(Powerup::JET))HamsterGame::Game().DrawDecal(jetDisplayOffset+vf2d{22.f,137.f},HamsterGame::GetGFX("fuelbar_outline.png").Decal(),{1.f,1.f},GetPlayer().jetFuel<=0.2f?(fmod(GetPlayer().readyFlashTimer,1.f)<=0.5f?RED:BLACK):BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Animate2D::Frame&Hamster::GetCurrentAnimation()const{
|
const Animate2D::Frame&Hamster::GetCurrentAnimation()const{
|
||||||
@ -265,7 +260,11 @@ void Hamster::HandlePlayerControls(){
|
|||||||
}
|
}
|
||||||
lastTappedSpace=0.f;
|
lastTappedSpace=0.f;
|
||||||
}
|
}
|
||||||
if(HamsterGame::Game().GetKey(P).bPressed)ObtainPowerup(Powerup::JET);
|
if(HamsterGame::Game().GetKey(P).bPressed){
|
||||||
|
ObtainPowerup(Powerup::JET);
|
||||||
|
Powerup tempJetPowerup{{},Powerup::JET};
|
||||||
|
tempJetPowerup.OnPowerupObtain(*this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hamster::TurnTowardsTargetDirection(){
|
void Hamster::TurnTowardsTargetDirection(){
|
||||||
@ -276,6 +275,9 @@ void Hamster::MoveHamster(){
|
|||||||
SetPos(GetPos()+vel*HamsterGame::Game().GetElapsedTime());
|
SetPos(GetPos()+vel*HamsterGame::Game().GetElapsedTime());
|
||||||
|
|
||||||
distanceTravelled+=vel.mag()*HamsterGame::Game().GetElapsedTime();
|
distanceTravelled+=vel.mag()*HamsterGame::Game().GetElapsedTime();
|
||||||
|
if(state==FLYING){
|
||||||
|
jetFuel=std::max(0.f,jetFuel-vel.mag()*HamsterGame::Game().GetElapsedTime()/100000.f);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma region Handle Friction
|
#pragma region Handle Friction
|
||||||
if(frictionEnabled){
|
if(frictionEnabled){
|
||||||
@ -314,9 +316,11 @@ void Hamster::HandleCollision(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(Powerup&powerup:Powerup::GetPowerups()){
|
for(Powerup&powerup:Powerup::GetPowerups()){
|
||||||
if(z<=0.1f&&!HasPowerup(powerup.GetType())&&geom2d::overlaps(geom2d::circle<float>(GetPos(),collisionRadius),geom2d::circle<float>(powerup.GetPos(),20.f))){
|
if(z<=0.1f&&
|
||||||
|
(!HasPowerup(powerup.GetType())||HasPowerup(Powerup::JET)&&powerup.GetType()==Powerup::JET&&jetFuel!=1.f)
|
||||||
|
&&geom2d::overlaps(geom2d::circle<float>(GetPos(),collisionRadius),geom2d::circle<float>(powerup.GetPos(),20.f))){
|
||||||
ObtainPowerup(powerup.GetType());
|
ObtainPowerup(powerup.GetType());
|
||||||
powerup.OnPowerupObtain();
|
powerup.OnPowerupObtain(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,4 +454,72 @@ void Hamster::OnUserDestroy(){
|
|||||||
|
|
||||||
void Hamster::SetDrawingOffsetY(const float offsetY){
|
void Hamster::SetDrawingOffsetY(const float offsetY){
|
||||||
drawingOffsetY=offsetY;
|
drawingOffsetY=offsetY;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vf2d Hamster::GetNearestSafeLocation()const{
|
||||||
|
using TilePos=vi2d;
|
||||||
|
using TileDistance=int;
|
||||||
|
|
||||||
|
const vi2d playerTile{GetPos()/16};
|
||||||
|
geom2d::rect<int>searchRect{{-1,-1},{3,3}};
|
||||||
|
std::optional<std::pair<TilePos,TileDistance>>closestTile;
|
||||||
|
|
||||||
|
const auto DetermineAndUpdateClosestTile=[this,&playerTile,&closestTile](const vi2d&tile){
|
||||||
|
if(!IsLethalTerrain(tile*16)&&!IsSolidTerrain(tile*16)){
|
||||||
|
std::pair<TilePos,TileDistance>closest{closestTile.value_or(std::pair<TilePos,TileDistance>{{},std::numeric_limits<int>::max()})};
|
||||||
|
int tileDist{abs(playerTile.x-tile.x)+abs(playerTile.y-tile.y)};
|
||||||
|
if(tileDist<=closest.second)closestTile.emplace(std::pair<TilePos,TileDistance>{tile,tileDist});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
while(!closestTile.has_value()){
|
||||||
|
#pragma region Top Outline Check
|
||||||
|
{
|
||||||
|
for(int offsetX:std::ranges::iota_view(searchRect.pos.x,searchRect.size.x)){
|
||||||
|
const vi2d checkTile{playerTile+vi2d{offsetX,searchRect.top().end.y}};
|
||||||
|
DetermineAndUpdateClosestTile(checkTile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
#pragma region Bottom Outline Check
|
||||||
|
{
|
||||||
|
for(int offsetX:std::ranges::iota_view(searchRect.pos.x,searchRect.size.x)){
|
||||||
|
const vi2d checkTile{playerTile+vi2d{offsetX,searchRect.bottom().end.y}};
|
||||||
|
DetermineAndUpdateClosestTile(checkTile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
#pragma region Right Outline Check
|
||||||
|
{
|
||||||
|
for(int offsetY:std::ranges::iota_view(searchRect.pos.y+1,searchRect.size.y-2+1)){
|
||||||
|
const vi2d checkTile{playerTile+vi2d{searchRect.right().end.x,offsetY}};
|
||||||
|
DetermineAndUpdateClosestTile(checkTile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
#pragma region Left Outline Check
|
||||||
|
{
|
||||||
|
for(int offsetY:std::ranges::iota_view(searchRect.pos.y+1,searchRect.size.y-2+1)){
|
||||||
|
const vi2d checkTile{playerTile+vi2d{searchRect.left().end.x,offsetY}};
|
||||||
|
DetermineAndUpdateClosestTile(checkTile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
searchRect.pos-=1;
|
||||||
|
searchRect.size+=2;
|
||||||
|
}
|
||||||
|
return closestTile.value().first*16+8;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool Hamster::IsSolidTerrain(const vf2d pos)const{
|
||||||
|
return HamsterGame::Game().IsTerrainSolid(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hamster::SetJetFuel(const float amt){
|
||||||
|
jetFuel=amt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hamster::Knockout(){
|
||||||
|
state=KNOCKOUT;
|
||||||
|
knockoutTimer=4.f;
|
||||||
|
animations.ChangeState(internalAnimState,HamsterGame::KNOCKOUT);
|
||||||
}
|
}
|
@ -59,6 +59,7 @@ class Hamster{
|
|||||||
FLYING,
|
FLYING,
|
||||||
WAIT,
|
WAIT,
|
||||||
BURNING,
|
BURNING,
|
||||||
|
KNOCKOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<Hamster>HAMSTER_LIST;
|
static std::vector<Hamster>HAMSTER_LIST;
|
||||||
@ -103,6 +104,12 @@ class Hamster{
|
|||||||
std::optional<HamsterJet>hamsterJet;
|
std::optional<HamsterJet>hamsterJet;
|
||||||
float lastTappedSpace{0.f};
|
float lastTappedSpace{0.f};
|
||||||
float drawingOffsetY{0.f};
|
float drawingOffsetY{0.f};
|
||||||
|
SpecialRenderable hamsterJetDisplay;
|
||||||
|
SpecialRenderable hamsterJetLightsDisplay;
|
||||||
|
float readyFlashTimer{};
|
||||||
|
float jetFuel{0.f};
|
||||||
|
float jetFuelDisplayAmt{0.f};
|
||||||
|
float knockoutTimer{0.f};
|
||||||
public:
|
public:
|
||||||
Hamster(const vf2d spawnPos,const std::string_view img,const PlayerControlled IsPlayerControlled=NPC);
|
Hamster(const vf2d spawnPos,const std::string_view img,const PlayerControlled IsPlayerControlled=NPC);
|
||||||
static const Hamster&GetPlayer();
|
static const Hamster&GetPlayer();
|
||||||
@ -129,6 +136,7 @@ public:
|
|||||||
const bool HasPowerup(const Powerup::PowerupType powerup)const;
|
const bool HasPowerup(const Powerup::PowerupType powerup)const;
|
||||||
void RemoveAllPowerups();
|
void RemoveAllPowerups();
|
||||||
const bool IsLethalTerrain(const vf2d pos)const;
|
const bool IsLethalTerrain(const vf2d pos)const;
|
||||||
|
const bool IsSolidTerrain(const vf2d pos)const;
|
||||||
const bool IsDrowning()const;
|
const bool IsDrowning()const;
|
||||||
const bool IsBurning()const;
|
const bool IsBurning()const;
|
||||||
const float GetDrownRatio()const;
|
const float GetDrownRatio()const;
|
||||||
@ -137,4 +145,7 @@ public:
|
|||||||
void SetZ(const float z);
|
void SetZ(const float z);
|
||||||
static void OnUserDestroy();
|
static void OnUserDestroy();
|
||||||
void SetDrawingOffsetY(const float offsetY);
|
void SetDrawingOffsetY(const float offsetY);
|
||||||
|
const vf2d GetNearestSafeLocation()const;
|
||||||
|
void SetJetFuel(const float amt);
|
||||||
|
void Knockout();
|
||||||
};
|
};
|
@ -52,6 +52,7 @@ void HamsterGame::LoadGraphics(){
|
|||||||
_LoadImage("fallometer_outline.png");
|
_LoadImage("fallometer_outline.png");
|
||||||
_LoadImage("fuelmeter.png");
|
_LoadImage("fuelmeter.png");
|
||||||
_LoadImage("fuelbar.png");
|
_LoadImage("fuelbar.png");
|
||||||
|
_LoadImage("fuelbar_outline.png");
|
||||||
UpdateMatrixTexture();
|
UpdateMatrixTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +76,7 @@ void HamsterGame::LoadAnimations(){
|
|||||||
LoadAnimation(DEFAULT,"hamster.png",{{0,32},{32,32}},0.3f);
|
LoadAnimation(DEFAULT,"hamster.png",{{0,32},{32,32}},0.3f);
|
||||||
LoadAnimation(WHEEL_TOP,"hamster.png",{{0,96},{32,96}},0.1f);
|
LoadAnimation(WHEEL_TOP,"hamster.png",{{0,96},{32,96}},0.1f);
|
||||||
LoadAnimation(WHEEL_BOTTOM,"hamster.png",{{64,96},{96,96}},0.1f);
|
LoadAnimation(WHEEL_BOTTOM,"hamster.png",{{64,96},{96,96}},0.1f);
|
||||||
|
LoadAnimation(KNOCKOUT,"hamster.png",{{64,32},{96,32}},0.2f);
|
||||||
Animate2D::FrameSequence&waterAnimFrames{(*ANIMATED_TILE_IDS.insert({1384,Animate2D::FrameSequence{0.2f}}).first).second};
|
Animate2D::FrameSequence&waterAnimFrames{(*ANIMATED_TILE_IDS.insert({1384,Animate2D::FrameSequence{0.2f}}).first).second};
|
||||||
for(vf2d&sourcePos:std::vector<vf2d>{{192+16*0,784},{192+16*1,784},{192+16*2,784},{192+16*3,784},{192+16*4,784},{192+16*5,784},{192+16*6,784},{192+16*7,784}}){
|
for(vf2d&sourcePos:std::vector<vf2d>{{192+16*0,784},{192+16*1,784},{192+16*2,784},{192+16*3,784},{192+16*4,784},{192+16*5,784},{192+16*6,784},{192+16*7,784}}){
|
||||||
waterAnimFrames.AddFrame(Animate2D::Frame{&GetGFX("gametiles.png"),{sourcePos,{16,16}}});
|
waterAnimFrames.AddFrame(Animate2D::Frame{&GetGFX("gametiles.png"),{sourcePos,{16,16}}});
|
||||||
|
@ -64,6 +64,7 @@ public:
|
|||||||
WHEEL_BOTTOM,
|
WHEEL_BOTTOM,
|
||||||
JET_LIGHTS,
|
JET_LIGHTS,
|
||||||
JET_FLAMES,
|
JET_FLAMES,
|
||||||
|
KNOCKOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
HamsterGame();
|
HamsterGame();
|
||||||
|
@ -80,19 +80,22 @@ void HamsterJet::Update(const float fElapsedTime){
|
|||||||
hamster.SetPos(pos);
|
hamster.SetPos(pos);
|
||||||
hamster.SetZ(z+0.03f);
|
hamster.SetZ(z+0.03f);
|
||||||
if(timer<=0.f){
|
if(timer<=0.f){
|
||||||
state=PLAYER_CONTROL;
|
state=HAMSTER_CONTROL;
|
||||||
HamsterGame::Game().SetZoom(0.6f);
|
HamsterGame::Game().SetZoom(0.6f);
|
||||||
easeInTimer=0.6f;
|
easeInTimer=0.6f;
|
||||||
}
|
}
|
||||||
}break;
|
}break;
|
||||||
case PLAYER_CONTROL:{
|
case HAMSTER_CONTROL:{
|
||||||
jetState[TOP_LEFT]=jetState[BOTTOM_LEFT]=jetState[BOTTOM_RIGHT]=jetState[TOP_RIGHT]=OFF;
|
jetState[TOP_LEFT]=jetState[BOTTOM_LEFT]=jetState[BOTTOM_RIGHT]=jetState[TOP_RIGHT]=OFF;
|
||||||
HandleJetControls();
|
HandleJetControls();
|
||||||
pos=hamster.GetPos();
|
pos=hamster.GetPos();
|
||||||
}break;
|
}break;
|
||||||
case LANDING:{
|
case LANDING:{
|
||||||
jetState[TOP_LEFT]=jetState[BOTTOM_LEFT]=jetState[BOTTOM_RIGHT]=jetState[TOP_RIGHT]=OFF;
|
jetState[TOP_LEFT]=jetState[BOTTOM_LEFT]=jetState[BOTTOM_RIGHT]=jetState[TOP_RIGHT]=OFF;
|
||||||
HandleJetControls();
|
if(hamster.IsPlayerControlled)HandleJetControls();
|
||||||
|
else{
|
||||||
|
//TODO: AI controls here!
|
||||||
|
}
|
||||||
pos=hamster.GetPos();
|
pos=hamster.GetPos();
|
||||||
hamster.SetZ(hamster.GetZ()-fallSpd*fElapsedTime);
|
hamster.SetZ(hamster.GetZ()-fallSpd*fElapsedTime);
|
||||||
z=hamster.GetZ();
|
z=hamster.GetZ();
|
||||||
@ -104,6 +107,14 @@ void HamsterJet::Update(const float fElapsedTime){
|
|||||||
timer=3.f;
|
timer=3.f;
|
||||||
originalPos=hamster.GetPos();
|
originalPos=hamster.GetPos();
|
||||||
targetPos={hamster.GetPos().x+128.f,hamster.GetPos().y+32.f};
|
targetPos={hamster.GetPos().x+128.f,hamster.GetPos().y+32.f};
|
||||||
|
Terrain::CrashSpeed crashSpd{Terrain::LIGHT};
|
||||||
|
if(fallSpd>4.f)crashSpd=Terrain::MAX;
|
||||||
|
else if(fallSpd>2.f)crashSpd=Terrain::MEDIUM;
|
||||||
|
std::pair<Terrain::FuelDamage,Terrain::KnockoutOccurs>landingResult{Terrain::GetFuelDamageTakenAndKnockoutEffect(hamster.GetTerrainStandingOn(),crashSpd)};
|
||||||
|
hamster.jetFuel=std::max(0.f,hamster.jetFuel-landingResult.first);
|
||||||
|
if(landingResult.second)hamster.Knockout();
|
||||||
|
if(hamster.IsTerrainStandingOnSolid())hamster.SetPos(hamster.GetNearestSafeLocation());
|
||||||
|
if(hamster.jetFuel<=0.f)hamster.powerups.erase(Powerup::JET);
|
||||||
}
|
}
|
||||||
}break;
|
}break;
|
||||||
case COMPLETE_LANDING:{
|
case COMPLETE_LANDING:{
|
||||||
@ -121,11 +132,11 @@ void HamsterJet::Update(const float fElapsedTime){
|
|||||||
void HamsterJet::Draw(){
|
void HamsterJet::Draw(){
|
||||||
float drawingOffsetY{0.f};
|
float drawingOffsetY{0.f};
|
||||||
hamster.SetDrawingOffsetY(0.f);
|
hamster.SetDrawingOffsetY(0.f);
|
||||||
if((state==PLAYER_CONTROL||state==LANDING)&&z>2.f){
|
if((state==HAMSTER_CONTROL||state==LANDING)&&z>2.f){
|
||||||
HamsterGame::Game().SetZ(z/2.f);
|
HamsterGame::Game().SetZ(z/2.f);
|
||||||
HamsterGame::Game().tv.DrawRotatedDecal(pos,HamsterGame::GetGFX("aimingTarget.png").Decal(),0.f,HamsterGame::GetGFX("aimingTarget.png").Sprite()->Size()/2);
|
HamsterGame::Game().tv.DrawRotatedDecal(pos,HamsterGame::GetGFX("aimingTarget.png").Decal(),0.f,HamsterGame::GetGFX("aimingTarget.png").Sprite()->Size()/2);
|
||||||
}
|
}
|
||||||
if(state==PLAYER_CONTROL){
|
if(state==HAMSTER_CONTROL){
|
||||||
drawingOffsetY=util::lerp(48.f,0.f,easeInTimer/0.6f);
|
drawingOffsetY=util::lerp(48.f,0.f,easeInTimer/0.6f);
|
||||||
hamster.SetDrawingOffsetY(util::lerp(48.f,0.f,easeInTimer/0.6f));
|
hamster.SetDrawingOffsetY(util::lerp(48.f,0.f,easeInTimer/0.6f));
|
||||||
}
|
}
|
||||||
@ -168,7 +179,7 @@ void HamsterJet::HandleJetControls(){
|
|||||||
jetState[BOTTOM_RIGHT]=ON;
|
jetState[BOTTOM_RIGHT]=ON;
|
||||||
jetState[TOP_RIGHT]=ON;
|
jetState[TOP_RIGHT]=ON;
|
||||||
}
|
}
|
||||||
if(aimingDir!=vf2d{}){
|
if(aimingDir!=vf2d{}&&hamster.jetFuel>0.f){
|
||||||
hamster.targetRot=aimingDir.norm().polar().y;
|
hamster.targetRot=aimingDir.norm().polar().y;
|
||||||
const vf2d currentVel{hamster.vel};
|
const vf2d currentVel{hamster.vel};
|
||||||
hamster.vel+=vf2d{currentVel.polar().x+(hamster.GetMaxSpeed()*HamsterGame::Game().GetElapsedTime())/hamster.GetTimeToMaxSpeed(),hamster.rot}.cart();
|
hamster.vel+=vf2d{currentVel.polar().x+(hamster.GetMaxSpeed()*HamsterGame::Game().GetElapsedTime())/hamster.GetTimeToMaxSpeed(),hamster.rot}.cart();
|
||||||
|
@ -47,7 +47,7 @@ public:
|
|||||||
enum State{
|
enum State{
|
||||||
SWOOP_DOWN,
|
SWOOP_DOWN,
|
||||||
RISE_UP,
|
RISE_UP,
|
||||||
PLAYER_CONTROL,
|
HAMSTER_CONTROL,
|
||||||
LANDING,
|
LANDING,
|
||||||
COMPLETE_LANDING,
|
COMPLETE_LANDING,
|
||||||
};
|
};
|
||||||
|
@ -38,6 +38,7 @@ All rights reserved.
|
|||||||
|
|
||||||
#include "HamsterGame.h"
|
#include "HamsterGame.h"
|
||||||
#include "Powerup.h"
|
#include "Powerup.h"
|
||||||
|
#include "Hamster.h"
|
||||||
|
|
||||||
std::vector<Powerup>Powerup::powerupList;
|
std::vector<Powerup>Powerup::powerupList;
|
||||||
std::unordered_map<int,std::pair<Powerup::PowerupType,Powerup::TileType>>Powerup::powerupIds;
|
std::unordered_map<int,std::pair<Powerup::PowerupType,Powerup::TileType>>Powerup::powerupIds;
|
||||||
@ -112,6 +113,7 @@ const geom2d::rect<float>Powerup::GetPowerupSubimageRect(const PowerupType power
|
|||||||
return {POWERUP_TILESET_STARTING_POS+vf2d{int(powerupType)*32.f,0.f},{32,32}};
|
return {POWERUP_TILESET_STARTING_POS+vf2d{int(powerupType)*32.f,0.f},{32,32}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Powerup::OnPowerupObtain(){
|
void Powerup::OnPowerupObtain(Hamster&pickupHamster){
|
||||||
spinSpd=0.3f;
|
spinSpd=0.3f;
|
||||||
|
if(type==JET)pickupHamster.SetJetFuel(1.f);
|
||||||
}
|
}
|
@ -41,6 +41,8 @@ All rights reserved.
|
|||||||
#include "olcUTIL_Geometry2D.h"
|
#include "olcUTIL_Geometry2D.h"
|
||||||
#include "olcPGEX_TransformedView.h"
|
#include "olcPGEX_TransformedView.h"
|
||||||
|
|
||||||
|
class Hamster;
|
||||||
|
|
||||||
class Powerup{
|
class Powerup{
|
||||||
public:
|
public:
|
||||||
enum PowerupType{
|
enum PowerupType{
|
||||||
@ -79,5 +81,5 @@ public:
|
|||||||
static void UpdatePowerups(const float fElapsedTime);
|
static void UpdatePowerups(const float fElapsedTime);
|
||||||
static void DrawPowerups(TransformedView&tv);
|
static void DrawPowerups(TransformedView&tv);
|
||||||
static const geom2d::rect<float>GetPowerupSubimageRect(const PowerupType powerupType);
|
static const geom2d::rect<float>GetPowerupSubimageRect(const PowerupType powerupType);
|
||||||
void OnPowerupObtain();
|
void OnPowerupObtain(Hamster&pickupHamster);
|
||||||
};
|
};
|
@ -73,4 +73,82 @@ const std::string Terrain::TerrainToString(const TerrainType type){
|
|||||||
return "Void";
|
return "Void";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const std::pair<Terrain::FuelDamage,Terrain::KnockoutOccurs>Terrain::GetFuelDamageTakenAndKnockoutEffect(const TerrainType type,const CrashSpeed crashSpeed){
|
||||||
|
|
||||||
|
const auto GetHardSurfaceCrashResult=[&crashSpeed](){
|
||||||
|
switch(crashSpeed){
|
||||||
|
case MAX:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{1.f,true};
|
||||||
|
}break;
|
||||||
|
case MEDIUM:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.8f,false};
|
||||||
|
}break;
|
||||||
|
case LIGHT:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.5f,false};
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto GetMediumSurfaceCrashResult=[&crashSpeed](){
|
||||||
|
switch(crashSpeed){
|
||||||
|
case MAX:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.8f,true};
|
||||||
|
}break;
|
||||||
|
case MEDIUM:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.5f,false};
|
||||||
|
}break;
|
||||||
|
case LIGHT:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.25f,false};
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto GetSoftSurfaceCrashResult=[&crashSpeed](){
|
||||||
|
switch(crashSpeed){
|
||||||
|
case MAX:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.5f,false};
|
||||||
|
}break;
|
||||||
|
case MEDIUM:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.25f,false};
|
||||||
|
}break;
|
||||||
|
case LIGHT:{
|
||||||
|
return std::pair<FuelDamage,KnockoutOccurs>{0.25f,false};
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(type){
|
||||||
|
case ROCK:{
|
||||||
|
return GetHardSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case GRASS:{
|
||||||
|
return GetMediumSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case SAND:{
|
||||||
|
return GetSoftSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case SWAMP:{
|
||||||
|
return GetSoftSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case LAVA:{
|
||||||
|
return GetSoftSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case SHORE:{
|
||||||
|
return GetMediumSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case OCEAN:{
|
||||||
|
return GetMediumSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case FOREST:{
|
||||||
|
return GetMediumSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case TUNNEL:{
|
||||||
|
return GetHardSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
case ICE:{
|
||||||
|
return GetHardSurfaceCrashResult();
|
||||||
|
}break;
|
||||||
|
default:{
|
||||||
|
return GetMediumSurfaceCrashResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -57,5 +57,13 @@ namespace Terrain{
|
|||||||
TUNNEL,
|
TUNNEL,
|
||||||
ICE,
|
ICE,
|
||||||
};
|
};
|
||||||
|
enum CrashSpeed{
|
||||||
|
MAX,
|
||||||
|
MEDIUM,
|
||||||
|
LIGHT,
|
||||||
|
};
|
||||||
|
using FuelDamage=float;
|
||||||
|
using KnockoutOccurs=bool;
|
||||||
const std::string TerrainToString(const TerrainType type);
|
const std::string TerrainToString(const TerrainType type);
|
||||||
|
const std::pair<FuelDamage,KnockoutOccurs>GetFuelDamageTakenAndKnockoutEffect(const TerrainType type,const CrashSpeed crashSpeed);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user