Fix collision bugs, added sky covers. Implemented basic hamster jet animations.

main
sigonasr2 3 months ago
parent 086e244866
commit 2a821cb70a
  1. 8
      assets/HamsterGame.tiled-session
  2. 7
      assets/Terrain.tsx
  3. 2
      assets/TestLevel.tmx
  4. BIN
      assets/clouds.png
  5. BIN
      assets/dot.png
  6. BIN
      assets/hamster_jet.png
  7. BIN
      border.png
  8. 40
      src/Hamster.cpp
  9. 6
      src/Hamster.h
  10. 42
      src/HamsterGame.cpp
  11. 6
      src/HamsterGame.h
  12. 89
      src/HamsterJet.cpp
  13. 68
      src/HamsterJet.h
  14. 18
      src/SpecialRenderable.cpp
  15. 3
      src/SpecialRenderable.h
  16. 1
      src/TSXParser.h
  17. 4
      src/util.cpp
  18. 1
      src/util.h

@ -8,11 +8,11 @@
], ],
"fileStates": { "fileStates": {
"TestLevel.tmx": { "TestLevel.tmx": {
"scale": 3, "scale": 4,
"selectedLayer": 0, "selectedLayer": 2,
"viewCenter": { "viewCenter": {
"x": 761.1666666666666, "x": 222.625,
"y": 278.8333333333333 "y": 144.625
} }
}, },
"TreeTemplates.tmx": { "TreeTemplates.tmx": {

@ -597,7 +597,12 @@
<property name="Terrain Type" type="int" propertytype="TerrainType" value="1"/> <property name="Terrain Type" type="int" propertytype="TerrainType" value="1"/>
</properties> </properties>
</tile> </tile>
<tile id="101" type="TileProps" probability="20"/> <tile id="101" type="TileProps" probability="20">
<properties>
<property name="Solid" type="bool" value="false"/>
<property name="Terrain Type" type="int" propertytype="TerrainType" value="1"/>
</properties>
</tile>
<tile id="102" type="TileProps"> <tile id="102" type="TileProps">
<properties> <properties>
<property name="Solid" type="bool" value="true"/> <property name="Solid" type="bool" value="true"/>

@ -613,7 +613,7 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data> </data>
</layer> </layer>
<layer id="4" name="Tile Layer 4" width="200" height="200"> <layer id="4" name="Tile Layer 4" width="200" height="200" visible="0" locked="1">
<data encoding="csv"> <data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

@ -104,7 +104,11 @@ void Hamster::UpdateHamsters(const float fElapsedTime){
h.state=WAIT; h.state=WAIT;
} }
}break; }break;
case FLYING:{
h.hamsterJet.value().Update(fElapsedTime);
};
} }
if(h.state!=FLYING){
if((h.GetTerrainStandingOn()==Terrain::OCEAN||!h.HasPowerup(Powerup::SWAMP)&&h.GetTerrainStandingOn()==Terrain::SWAMP)&&h.state!=DROWNING&&h.state!=WAIT)h.drownTimer+=fElapsedTime; if((h.GetTerrainStandingOn()==Terrain::OCEAN||!h.HasPowerup(Powerup::SWAMP)&&h.GetTerrainStandingOn()==Terrain::SWAMP)&&h.state!=DROWNING&&h.state!=WAIT)h.drownTimer+=fElapsedTime;
else if((!h.HasPowerup(Powerup::LAVA)&&h.GetTerrainStandingOn()==Terrain::LAVA)&&h.state!=BURNING&&h.state!=WAIT)h.burnTimer+=fElapsedTime; else if((!h.HasPowerup(Powerup::LAVA)&&h.GetTerrainStandingOn()==Terrain::LAVA)&&h.state!=BURNING&&h.state!=WAIT)h.burnTimer+=fElapsedTime;
else if(h.lastSafeLocationTimer<=0.f&&h.state==NORMAL&&!h.StandingOnLethalTerrain()){ else if(h.lastSafeLocationTimer<=0.f&&h.state==NORMAL&&!h.StandingOnLethalTerrain()){
@ -119,6 +123,7 @@ void Hamster::UpdateHamsters(const float fElapsedTime){
if(h.burnTimer>=h.DEFAULT_BURN_TIME&&h.state!=BURNING&&h.state!=WAIT){ if(h.burnTimer>=h.DEFAULT_BURN_TIME&&h.state!=BURNING&&h.state!=WAIT){
h.state=BURNING; h.state=BURNING;
} }
}
h.TurnTowardsTargetDirection(); h.TurnTowardsTargetDirection();
h.MoveHamster(); h.MoveHamster();
} }
@ -143,6 +148,7 @@ void Hamster::DrawHamsters(TransformedView&tv){
const Animate2D::Frame&img{h.animations.GetState(h.internalAnimState)==HamsterGame::DEFAULT?anim.GetFrame(h.distanceTravelled/100.f):h.GetCurrentAnimation()}; const Animate2D::Frame&img{h.animations.GetState(h.internalAnimState)==HamsterGame::DEFAULT?anim.GetFrame(h.distanceTravelled/100.f):h.GetCurrentAnimation()};
const Animate2D::Frame&wheelTopImg{wheelTopAnim.GetFrame(h.distanceTravelled/80.f)}; const Animate2D::Frame&wheelTopImg{wheelTopAnim.GetFrame(h.distanceTravelled/80.f)};
const Animate2D::Frame&wheelBottomImg{wheelBottomAnim.GetFrame(h.distanceTravelled/80.f)}; const Animate2D::Frame&wheelBottomImg{wheelBottomAnim.GetFrame(h.distanceTravelled/80.f)};
if(h.state==FLYING)h.hamsterJet.value().Draw();
HamsterGame::Game().SetZ(h.z); HamsterGame::Game().SetZ(h.z);
if(h.HasPowerup(Powerup::WHEEL))tv.DrawPartialRotatedDecal(h.pos,wheelBottomImg.GetSourceImage()->Decal(),h.rot,wheelBottomImg.GetSourceRect().size/2,wheelBottomImg.GetSourceRect().pos,wheelBottomImg.GetSourceRect().size,vf2d{1.f,1.f}*h.imgScale,PixelLerp(h.shrinkEffectColor,WHITE,h.imgScale)); if(h.HasPowerup(Powerup::WHEEL))tv.DrawPartialRotatedDecal(h.pos,wheelBottomImg.GetSourceImage()->Decal(),h.rot,wheelBottomImg.GetSourceRect().size/2,wheelBottomImg.GetSourceRect().pos,wheelBottomImg.GetSourceRect().size,vf2d{1.f,1.f}*h.imgScale,PixelLerp(h.shrinkEffectColor,WHITE,h.imgScale));
tv.DrawPartialRotatedDecal(h.pos,img.GetSourceImage()->Decal(),h.rot,img.GetSourceRect().size/2,img.GetSourceRect().pos,img.GetSourceRect().size,vf2d{1.f,1.f}*h.imgScale,PixelLerp(h.shrinkEffectColor,WHITE,h.imgScale)); tv.DrawPartialRotatedDecal(h.pos,img.GetSourceImage()->Decal(),h.rot,img.GetSourceRect().size/2,img.GetSourceRect().pos,img.GetSourceRect().size,vf2d{1.f,1.f}*h.imgScale,PixelLerp(h.shrinkEffectColor,WHITE,h.imgScale));
@ -165,6 +171,7 @@ const vf2d&Hamster::GetPos()const{
} }
void Hamster::HandlePlayerControls(){ void Hamster::HandlePlayerControls(){
lastTappedSpace+=HamsterGame::Game().GetElapsedTime();
vf2d aimingDir{}; vf2d aimingDir{};
if(HamsterGame::Game().GetKey(W).bHeld){ if(HamsterGame::Game().GetKey(W).bHeld){
aimingDir+=vf2d{0,-1}; aimingDir+=vf2d{0,-1};
@ -178,12 +185,6 @@ void Hamster::HandlePlayerControls(){
if(HamsterGame::Game().GetKey(A).bHeld){ if(HamsterGame::Game().GetKey(A).bHeld){
aimingDir+=vf2d{-1,0}; aimingDir+=vf2d{-1,0};
} }
if(HamsterGame::Game().GetKey(PGUP).bHeld){
z+=0.25f*HamsterGame::Game().GetElapsedTime();
}
if(HamsterGame::Game().GetKey(PGDN).bHeld){
z=std::max(0.f,z-0.25f*HamsterGame::Game().GetElapsedTime());
}
if(aimingDir!=vf2d{}){ if(aimingDir!=vf2d{}){
targetRot=aimingDir.norm().polar().y; targetRot=aimingDir.norm().polar().y;
const vf2d currentVel{vel}; const vf2d currentVel{vel};
@ -191,6 +192,13 @@ void Hamster::HandlePlayerControls(){
vel=vf2d{std::min(GetMaxSpeed(),vel.polar().x),vel.polar().y}.cart(); vel=vf2d{std::min(GetMaxSpeed(),vel.polar().x),vel.polar().y}.cart();
frictionEnabled=false; frictionEnabled=false;
} }
if(HamsterGame::Game().GetKey(SPACE).bPressed){
if(lastTappedSpace<=0.6f&&HasPowerup(Powerup::JET)){
state=FLYING;
hamsterJet.emplace(*this);
}
lastTappedSpace=0.f;
}
} }
void Hamster::TurnTowardsTargetDirection(){ void Hamster::TurnTowardsTargetDirection(){
@ -213,7 +221,7 @@ void Hamster::MoveHamster(){
void Hamster::HandleCollision(){ void Hamster::HandleCollision(){
for(Hamster&h:HAMSTER_LIST){ for(Hamster&h:HAMSTER_LIST){
if(this==&h)continue; if(this==&h)continue;
if(geom2d::overlaps(geom2d::circle<float>(GetPos(),GetRadius()),geom2d::circle<float>(h.GetPos(),h.GetRadius()))){ if(abs(z-h.z)<=0.1f&&geom2d::overlaps(geom2d::circle<float>(GetPos(),GetRadius()),geom2d::circle<float>(h.GetPos(),h.GetRadius()))){
if(geom2d::line<float>(GetPos(),h.GetPos()).length()==0.f){ //Push these two in random directions, they are on top of each other! if(geom2d::line<float>(GetPos(),h.GetPos()).length()==0.f){ //Push these two in random directions, they are on top of each other!
float randDir{util::random(2*geom2d::pi)}; float randDir{util::random(2*geom2d::pi)};
vf2d collisionResolve1{GetPos()+vf2d{GetRadius(),randDir}.cart()}; vf2d collisionResolve1{GetPos()+vf2d{GetRadius(),randDir}.cart()};
@ -340,9 +348,19 @@ const float&Hamster::GetZ()const{
} }
void Hamster::SetPos(const vf2d pos){ void Hamster::SetPos(const vf2d pos){
if(state!=FLYING){ bool movedY{false};
if(!HamsterGame::Game().IsTerrainSolid(vf2d{this->pos.x,pos.y}))this->pos=vf2d{this->pos.x,pos.y}; if(state==FLYING||!HamsterGame::Game().IsTerrainSolid(vf2d{this->pos.x,pos.y})){
if(!HamsterGame::Game().IsTerrainSolid(vf2d{pos.x,this->pos.y}))this->pos=vf2d{pos.x,this->pos.y}; this->pos=vf2d{this->pos.x,pos.y};
if(!HamsterGame::Game().IsTerrainSolid(vf2d{this->pos.x,pos.y}))this->pos=vf2d{this->pos.x,pos.y}; movedY=true;
} }
if(state==FLYING||!HamsterGame::Game().IsTerrainSolid(vf2d{pos.x,this->pos.y}))this->pos=vf2d{pos.x,this->pos.y};
if (!movedY&&(state==FLYING||!HamsterGame::Game().IsTerrainSolid(vf2d{this->pos.x,pos.y})))this->pos=vf2d{this->pos.x,pos.y};
}
void Hamster::SetZ(const float z){
this->z=z;
}
void Hamster::OnUserDestroy(){
HAMSTER_LIST.clear();
} }

@ -42,6 +42,8 @@ All rights reserved.
#include "olcUTIL_Animate2D.h" #include "olcUTIL_Animate2D.h"
#include "Terrain.h" #include "Terrain.h"
#include <unordered_set> #include <unordered_set>
#include "HamsterJet.h"
#include "HamsterGame.h"
class Hamster{ class Hamster{
enum PlayerControlled{ enum PlayerControlled{
@ -97,6 +99,8 @@ class Hamster{
static std::optional<Hamster*>playerHamster; static std::optional<Hamster*>playerHamster;
HamsterState state{NORMAL}; HamsterState state{NORMAL};
std::unordered_set<Powerup::PowerupType>powerups; std::unordered_set<Powerup::PowerupType>powerups;
std::optional<HamsterJet>hamsterJet;
float lastTappedSpace{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();
@ -127,4 +131,6 @@ public:
const float GetDrownRatio()const; const float GetDrownRatio()const;
const float GetBurnRatio()const; const float GetBurnRatio()const;
void SetPos(const vf2d pos); void SetPos(const vf2d pos);
void SetZ(const float z);
static void OnUserDestroy();
}; };

@ -34,7 +34,7 @@ bool HamsterGame::OnUserCreate(){
void HamsterGame::_LoadImage(const std::string_view img){ void HamsterGame::_LoadImage(const std::string_view img){
GFX.insert({ASSETS_DIR+std::string(img),Renderable{}}); GFX.insert({ASSETS_DIR+std::string(img),Renderable{}});
rcode result{GFX[ASSETS_DIR+std::string(img)].Load(ASSETS_DIR+std::string(img))}; rcode result{GFX[ASSETS_DIR+std::string(img)].Load(ASSETS_DIR+std::string(img),nullptr,false,false)};
if(result!=OK)throw std::runtime_error{std::format("Failed to Load Image {}. OLC Rcode: {}",img,int(result))}; if(result!=OK)throw std::runtime_error{std::format("Failed to Load Image {}. OLC Rcode: {}",img,int(result))};
} }
@ -45,6 +45,8 @@ void HamsterGame::LoadGraphics(){
_LoadImage("drownmeter.png"); _LoadImage("drownmeter.png");
_LoadImage("burnmeter.png"); _LoadImage("burnmeter.png");
_LoadImage("hamster_jet.png"); _LoadImage("hamster_jet.png");
_LoadImage("dot.png");
_LoadImage("clouds.png");
UpdateMatrixTexture(); UpdateMatrixTexture();
} }
@ -76,7 +78,7 @@ void HamsterGame::LoadAnimations(){
for(vf2d&sourcePos:std::vector<vf2d>{{192+16*0,800},{192+16*1,800},{192+16*2,800},{192+16*3,800},{192+16*4,800},{192+16*5,800},{192+16*6,800},{192+16*7,800},{192+16*8,800}}){ for(vf2d&sourcePos:std::vector<vf2d>{{192+16*0,800},{192+16*1,800},{192+16*2,800},{192+16*3,800},{192+16*4,800},{192+16*5,800},{192+16*6,800},{192+16*7,800},{192+16*8,800}}){
lavaAnimFrames.AddFrame(Animate2D::Frame{&GetGFX("gametiles.png"),{sourcePos,{16,16}}}); lavaAnimFrames.AddFrame(Animate2D::Frame{&GetGFX("gametiles.png"),{sourcePos,{16,16}}});
} }
LoadAnimation(JET_LIGHTS,"hamster_jet.png",{{0,48},{48,48}},0.3f); LoadAnimation(JET_LIGHTS,"hamster_jet.png",{{0,48},{48,48}},0.3f,Animate2D::Style::Repeat,{48,48});
animatedWaterTile.Create(16,16,false,false); animatedWaterTile.Create(16,16,false,false);
@ -87,6 +89,10 @@ void HamsterGame::LoadLevel(const std::string_view mapName){
const vf2d levelSpawnLoc{50,50}; //TEMPORARY const vf2d levelSpawnLoc{50,50}; //TEMPORARY
currentMap=TMXParser{ASSETS_DIR+std::string(mapName)}; currentMap=TMXParser{ASSETS_DIR+std::string(mapName)};
cloudSpd.x=util::random_range(-12.f,12.f);
cloudSpd.y=util::random_range(-0.3f,0.3f);
cloudOffset.x=util::random();
cloudOffset.y=util::random();
Hamster::LoadHamsters(levelSpawnLoc); Hamster::LoadHamsters(levelSpawnLoc);
camera.SetTarget(Hamster::GetPlayer().GetPos()); camera.SetTarget(Hamster::GetPlayer().GetPos());
@ -126,6 +132,7 @@ void HamsterGame::LoadLevel(const std::string_view mapName){
void HamsterGame::UpdateGame(const float fElapsedTime){ void HamsterGame::UpdateGame(const float fElapsedTime){
UpdateMatrixTexture(); UpdateMatrixTexture();
UpdateWaterTexture(); UpdateWaterTexture();
cloudOffset+=cloudSpd*fElapsedTime;
camera.SetViewSize(tv.GetWorldVisibleArea()); camera.SetViewSize(tv.GetWorldVisibleArea());
camera.Update(fElapsedTime); camera.Update(fElapsedTime);
tv.SetWorldOffset(tv.ScaleToWorld(-SCREEN_FRAME.pos)+camera.GetViewPosition()); tv.SetWorldOffset(tv.ScaleToWorld(-SCREEN_FRAME.pos)+camera.GetViewPosition());
@ -135,10 +142,19 @@ void HamsterGame::UpdateGame(const float fElapsedTime){
} }
void HamsterGame::DrawGame(){ void HamsterGame::DrawGame(){
tv.DrawPartialDecal({-3200,-3200},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},animatedWaterTile.Decal(),{0,0},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{3200,3200}); tv.DrawPartialDecal({-3200,-3200},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},animatedWaterTile.Decal(),{0,0},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400});
DrawLevelTiles(); DrawLevelTiles();
Powerup::DrawPowerups(tv); Powerup::DrawPowerups(tv);
Hamster::DrawHamsters(tv); Hamster::DrawHamsters(tv);
SetZ(3.f);
tv.DrawPartialDecal({-3200,-3200},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},GetGFX("dot.png").Decal(),{0,0},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},{226,228,255,32});
SetZ(7.f);
tv.DrawPartialDecal({-3200,-3200},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},GetGFX("dot.png").Decal(),{0,0},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},{178,242,255,64});
SetZ(2.f);
tv.DrawPartialDecal({-3200,-3200},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},GetGFX("clouds.png").Decal(),cloudOffset,currentMap.value().GetData().GetMapData().MapSize*16/2.f,{255,255,255,64});
SetZ(6.f);
tv.DrawPartialDecal({-3200,-3200},currentMap.value().GetData().GetMapData().MapSize*16+vf2d{6400,6400},GetGFX("clouds.png").Decal(),cloudOffset*2,currentMap.value().GetData().GetMapData().MapSize*16/2.f,{255,255,255,72});
SetZ(0.f);
border.Draw(); border.Draw();
#pragma region Powerup Display #pragma region Powerup Display
@ -186,7 +202,9 @@ const Terrain::TerrainType HamsterGame::GetTerrainTypeAtPos(const vf2d pos)const
} }
const bool HamsterGame::IsTerrainSolid(const vf2d pos)const{ const bool HamsterGame::IsTerrainSolid(const vf2d pos)const{
if(pos.x<=0.f||pos.y<=0.f||pos.x>=currentMap.value().GetData().GetMapData().width*16||pos.y>=currentMap.value().GetData().GetMapData().height*16)return true; if(pos.x<=0.f||pos.y<=0.f||pos.x>=currentMap.value().GetData().GetMapData().width*16||pos.y>=currentMap.value().GetData().GetMapData().height*16){
return true;
}
bool tileIsBlank{true}; bool tileIsBlank{true};
for(const LayerTag&layer:currentMap.value().GetData().GetLayers()){ for(const LayerTag&layer:currentMap.value().GetData().GetLayers()){
int tileX{int(floor(pos.x)/16)}; int tileX{int(floor(pos.x)/16)};
@ -223,7 +241,7 @@ void HamsterGame::DrawLevelTiles(){
bool HamsterGame::OnUserUpdate(float fElapsedTime){ bool HamsterGame::OnUserUpdate(float fElapsedTime){
runTime+=fElapsedTime; runTime+=fElapsedTime;
vEye.z+=(Hamster::GetPlayer().GetZ()+1.f-vEye.z)*fLazyFollowRate*fElapsedTime; vEye.z+=(Hamster::GetPlayer().GetZ()+zoom-vEye.z)*fLazyFollowRate*fElapsedTime;
UpdateGame(fElapsedTime); UpdateGame(fElapsedTime);
DrawGame(); DrawGame();
@ -240,6 +258,7 @@ const Animate2D::Animation<HamsterGame::AnimationState>&HamsterGame::GetAnimatio
} }
bool HamsterGame::OnUserDestroy(){ bool HamsterGame::OnUserDestroy(){
Hamster::OnUserDestroy();
ANIMATIONS.clear(); ANIMATIONS.clear();
GFX.clear(); GFX.clear();
return true; return true;
@ -258,6 +277,7 @@ void HamsterGame::UpdateMatrixTexture(){
Renderable&texture{(*result.first).second}; Renderable&texture{(*result.first).second};
if(result.second){ if(result.second){
texture.Create(64,64,false,false); texture.Create(64,64,false,false);
texture.Sprite()->SetSampleMode(Sprite::PERIODIC);
} }
const std::array<char,10>matrixLetters{'0','1','2','3','4','5','6','7','8','9'}; const std::array<char,10>matrixLetters{'0','1','2','3','4','5','6','7','8','9'};
@ -367,6 +387,17 @@ void HamsterGame::Apply3DTransform(std::vector<DecalInstance>&decals){
std::copy(foregroundDecals.begin(),foregroundDecals.end(),std::back_inserter(decals)); std::copy(foregroundDecals.begin(),foregroundDecals.end(),std::back_inserter(decals));
} }
const Animate2D::FrameSequence&HamsterGame::GetAnimation(const std::string_view img,const AnimationState state){
return GetAnimations(img).GetFrames(state);
}
void HamsterGame::SetZoom(const float zoom){
this->zoom=zoom;
}
const float HamsterGame::GetZoom()const{
return zoom;
}
int main() int main()
{ {
HamsterGame game; HamsterGame game;
@ -375,4 +406,3 @@ int main()
return 0; return 0;
} }

@ -75,11 +75,14 @@ public:
static const Renderable&GetGFX(const std::string_view img); static const Renderable&GetGFX(const std::string_view img);
static const Animate2D::Animation<HamsterGame::AnimationState>&GetAnimations(const std::string_view img); static const Animate2D::Animation<HamsterGame::AnimationState>&GetAnimations(const std::string_view img);
static const Animate2D::FrameSequence&GetAnimation(const std::string_view img,const AnimationState state);
static HamsterGame&Game(); static HamsterGame&Game();
static std::unordered_map<uint32_t,Animate2D::FrameSequence>ANIMATED_TILE_IDS; static std::unordered_map<uint32_t,Animate2D::FrameSequence>ANIMATED_TILE_IDS;
const double GetRuntime()const; const double GetRuntime()const;
const Terrain::TerrainType GetTerrainTypeAtPos(const vf2d pos)const; const Terrain::TerrainType GetTerrainTypeAtPos(const vf2d pos)const;
const bool IsTerrainSolid(const vf2d pos)const; const bool IsTerrainSolid(const vf2d pos)const;
void SetZoom(const float zoom);
const float GetZoom()const;
private: private:
void UpdateGame(const float fElapsedTime); void UpdateGame(const float fElapsedTime);
void DrawGame(); void DrawGame();
@ -105,9 +108,12 @@ private:
void UpdateWaterTexture(); void UpdateWaterTexture();
GFX3D::PipeLine renderer; GFX3D::PipeLine renderer;
virtual void Apply3DTransform(std::vector<DecalInstance>&decals)override final; virtual void Apply3DTransform(std::vector<DecalInstance>&decals)override final;
float zoom{1.f}; //Increase to zoom out, decrease to zoom in (this is the overhead distance from the player).
GFX3D::vec3d vUp{0,-1,0}; GFX3D::vec3d vUp{0,-1,0};
GFX3D::vec3d vEye{0.f,0,1}; GFX3D::vec3d vEye{0.f,0,1};
GFX3D::vec3d vLookDir{0,0,-1}; GFX3D::vec3d vLookDir{0,0,-1};
const float fLazyFollowRate{4.0f}; const float fLazyFollowRate{4.0f};
vf2d cloudSpd{};
vf2d cloudOffset{};
}; };

@ -0,0 +1,89 @@
#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 "HamsterJet.h"
#include "Hamster.h"
#include "util.h"
HamsterJet::HamsterJet(Hamster&hamster)
:hamster(hamster),hamsterOriginalPos(hamster.GetPos()),pos({hamster.GetPos().x-128.f,hamster.GetPos().y+32.f}),z(3.f),state(SWOOP_DOWN),timer(3.f){
jet.Initialize("hamster_jet.png",{78,223,208},{79,81,128});
lights.Initialize("hamster_jet.png",{245,233,130},{245,233,130});
}
void HamsterJet::Update(const float fElapsedTime){
jet.Update(fElapsedTime);
lights.Update(fElapsedTime);
timer=std::max(0.f,timer-fElapsedTime);
switch(state){
case SWOOP_DOWN:{
HamsterGame::Game().SetZoom(1.5f);
z=util::lerp(0.f,3.f,std::pow(timer/3.f,2));
vf2d originalPos{hamster.GetPos().x-128.f,hamster.GetPos().y+32.f};
pos=hamster.GetPos().lerp(originalPos,std::pow(timer/3.f,4));
if(timer<=0.4f){
hamster.SetPos(hamsterOriginalPos-vf2d{0.f,sin(float(geom2d::pi)*timer/0.4f)*8.f});
}
if(timer<=0.f){
state=RISE_UP;
hamster.SetPos(pos);
this->originalPos=pos;
targetPos=pos+vf2d{128.f,32};
targetZ=8.f;
timer=3.f;
}
}break;
case RISE_UP:{
pos=targetPos.lerp(originalPos,std::sqrt(timer/3.f));
z=util::lerp(targetZ,0.f,timer/3.f);
hamster.SetPos(pos);
hamster.SetZ(z+0.01f);
if(timer<=0.f){
state=PLAYER_CONTROL;
HamsterGame::Game().SetZoom(1.f);
}
}break;
}
}
void HamsterJet::Draw(){
HamsterGame::Game().SetZ(z);
HamsterGame::Game().tv.DrawPartialRotatedDecal(pos,jet.Decal(),0.f,{24,24},{},{48,48});
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().tv.DrawPartialRotatedDecal(pos,lights.Decal(),0.f,lightFrame.GetSourceRect().size/2.f,lightFrame.GetSourceRect().pos,lightFrame.GetSourceRect().size);
HamsterGame::Game().SetZ(0.f);
}

@ -0,0 +1,68 @@
#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
#pragma once
#include "olcUTIL_Geometry2D.h"
#include "SpecialRenderable.h"
class Hamster;
class HamsterJet{
enum State{
SWOOP_DOWN,
RISE_UP,
PLAYER_CONTROL,
LANDING,
COMPLETE_LANDING,
};
Hamster&hamster;
vf2d pos;
vf2d hamsterOriginalPos;
vf2d originalPos;
vf2d targetPos;
float z;
float targetZ{};
State state;
SpecialRenderable jet;
SpecialRenderable lights;
float timer{};
public:
HamsterJet(Hamster&hamster);
void Update(const float fElapsedTime);
void Draw();
};

@ -63,19 +63,35 @@ void SpecialRenderable::Initialize(std::string_view imgName,const Pixel override
void SpecialRenderable::Update(const float fElapsedTime){ void SpecialRenderable::Update(const float fElapsedTime){
if(!IsInitialized)throw std::runtime_error{std::format("SpecialRenderable for {} is not properly initialized!",originalImgName)}; if(!IsInitialized)throw std::runtime_error{std::format("SpecialRenderable for {} is not properly initialized!",originalImgName)};
lastPixelsUpdateTimer-=fElapsedTime; lastPixelsUpdateTimer-=fElapsedTime;
HamsterGame::Game().SetDrawTarget(modifiedImg.Sprite());
HamsterGame::Game().SetPixelMode(Pixel::ALPHA);
HamsterGame::Game().SetPixelBlend(0.5f);
if(lastPixelsUpdateTimer<=0.f){ if(lastPixelsUpdateTimer<=0.f){
lastPixelsUpdateTimer+=0.1f; lastPixelsUpdateTimer+=0.1f;
for(int y:std::ranges::iota_view(0,modifiedImg.Sprite()->height)){ for(int y:std::ranges::iota_view(0,modifiedImg.Sprite()->height)){
for(int x:std::ranges::iota_view(0,modifiedImg.Sprite()->width)){ for(int x:std::ranges::iota_view(0,modifiedImg.Sprite()->width)){
if(HamsterGame::GetGFX(originalImgName).Sprite()->GetPixel(x,y)==overrideCol){ if(HamsterGame::GetGFX(originalImgName).Sprite()->GetPixel(x,y)==overrideCol){
modifiedImg.Sprite()->SetPixel(x,y,PixelLerp(matrixCol,HamsterGame::GetGFX("MATRIX_TEXTURE").Sprite()->GetPixel(x,y),0.5f)); modifiedImg.Sprite()->SetPixel(x,y,HamsterGame::GetGFX("MATRIX_TEXTURE").Sprite()->GetPixel(x,y));
HamsterGame::Game().Draw({x,y},matrixCol);
} }
} }
} }
} }
HamsterGame::Game().SetDrawTarget(nullptr);
HamsterGame::Game().SetPixelMode(Pixel::MASK);
HamsterGame::Game().SetPixelBlend(1.f);
modifiedImg.Decal()->Update(); modifiedImg.Decal()->Update();
} }
const Renderable&SpecialRenderable::Get()const{ const Renderable&SpecialRenderable::Get()const{
if(!IsInitialized)throw std::runtime_error{std::format("SpecialRenderable for {} is not properly initialized!",originalImgName)}; if(!IsInitialized)throw std::runtime_error{std::format("SpecialRenderable for {} is not properly initialized!",originalImgName)};
return modifiedImg; return modifiedImg;
} }
void SpecialRenderable::ChangeMatrixColor(const Pixel newMatrixCol){
matrixCol=newMatrixCol;
}
Decal*SpecialRenderable::Decal()const{
return modifiedImg.Decal();
}
Sprite*SpecialRenderable::Sprite()const{
return modifiedImg.Sprite();
}

@ -51,4 +51,7 @@ public:
void Initialize(std::string_view imgName,const Pixel overrideCol,const Pixel matrixCol); void Initialize(std::string_view imgName,const Pixel overrideCol,const Pixel matrixCol);
void Update(const float fElapsedTime); void Update(const float fElapsedTime);
const Renderable&Get()const; const Renderable&Get()const;
Decal*Decal()const;
Sprite*Sprite()const;
void ChangeMatrixColor(const Pixel newMatrixCol);
}; };

@ -147,6 +147,7 @@ class TSXParser{
} else } else
if (newTag.tag=="tile"){ if (newTag.tag=="tile"){
previousTag.emplace_back(newTag.tag); previousTag.emplace_back(newTag.tag);
previousTagID.clear();
previousTagID.emplace_back(newTag.GetInteger("id")); previousTagID.emplace_back(newTag.GetInteger("id"));
} else } else
if(newTag.tag=="frame"){ if(newTag.tag=="frame"){

@ -47,3 +47,7 @@ void olc::util::turn_towards_direction(float&angle,float target,float rate)
if(diff>0&&newAngleDiff<0|| if(diff>0&&newAngleDiff<0||
diff<0&&newAngleDiff>0)angle=fmod(target,2*geom2d::pi); //We have crossed the angle difference threshold and can safely say we reached it. diff<0&&newAngleDiff>0)angle=fmod(target,2*geom2d::pi); //We have crossed the angle difference threshold and can safely say we reached it.
} }
float olc::util::lerp(float n1,float n2,double t){
return float(n1*(1-t)+n2*t);
}

@ -47,4 +47,5 @@ namespace olc::util{
int random(); int random();
float angle_difference(float angle_1, float angle_2); float angle_difference(float angle_1, float angle_2);
void turn_towards_direction(float&angle,float target,float rate); void turn_towards_direction(float&angle,float target,float rate);
float lerp(float n1,float n2,double t);
}; };
Loading…
Cancel
Save