Updated hamster images with profile icons. Added live placement tracking.

sigonasr2 3 months ago
parent 92f2b4dd85
commit 8551c9bacd
  1. BIN
      assets/hamster.xcf
  2. BIN
      assets/hamster1.png
  3. BIN
      assets/hamster2.png
  4. BIN
      assets/hamster3.png
  5. BIN
      assets/hamster4.png
  6. BIN
      assets/hamster5.png
  7. BIN
      assets/hamster6.png
  8. BIN
      assets/hamster7.png
  9. BIN
      assets/hamster8.png
  10. BIN
      assets/raceprogress.png
  11. 5
      src/Checkpoint.cpp
  12. 15
      src/Hamster.cpp
  13. 7
      src/Hamster.h
  14. 15
      src/HamsterGame.cpp
  15. 3
      src/HamsterGame.h
  16. 114
      src/HamsterLeaderboard.cpp
  17. 13
      src/HamsterLeaderboard.h
  18. 5
      src/TODO.txt

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

@ -86,7 +86,10 @@ void Checkpoint::DrawCheckpoints(TransformedView&tv){
if(screenDistance>226){
const vf2d dirVec{playerToCheckpointLine.vector().norm()};
const float dir{dirVec.polar().y};
std::optional<vf2d>projCircle{geom2d::project(geom2d::circle<float>({},16),HamsterGame::SCREEN_FRAME,geom2d::ray<float>(HamsterGame::SCREEN_FRAME.middle(),dirVec))};
geom2d::rect<float>screenBounds{HamsterGame::SCREEN_FRAME};
screenBounds.pos.x+=16.f;
screenBounds.size.x-=16.f;
std::optional<vf2d>projCircle{geom2d::project(geom2d::circle<float>({},16),screenBounds,geom2d::ray<float>(HamsterGame::SCREEN_FRAME.middle(),dirVec))};
if(projCircle.has_value()){
Pixel arrowCol{PixelLerp(GREEN,BLACK,std::clamp((screenDistance-226)/1000.f,0.f,1.f))};
uint8_t iconAlpha{uint8_t(util::lerp(255.f,0.f,std::clamp((screenDistance-226)/1000.f,0.f,1.f)))};

@ -422,8 +422,8 @@ void Hamster::DrawOverlay(){
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);
if(GetPlayer().HasPowerup(Powerup::WHEEL)){
for(int i:std::ranges::iota_view(0,3)){
if(fmod(HamsterGame::Game().GetRuntime(),2.f)<1.f&&GetPlayer().boostCounter>i)HamsterGame::Game().DrawDecal(HamsterGame::SCREEN_FRAME.pos+vf2d{i*16.f+4.f,HamsterGame::SCREEN_FRAME.size.y-20.f},HamsterGame::GetGFX("boost_outline.png").Decal(),{0.125f,0.125f},GetPlayer().boostCounter>i?WHITE:BLACK);
else HamsterGame::Game().DrawDecal(HamsterGame::SCREEN_FRAME.pos+vf2d{i*16.f+4.f,HamsterGame::SCREEN_FRAME.size.y-20.f},HamsterGame::GetGFX("boost.png").Decal(),{0.125f,0.125f},GetPlayer().boostCounter>i?WHITE:BLACK);
if(fmod(HamsterGame::Game().GetRuntime(),2.f)<1.f&&GetPlayer().boostCounter>i)HamsterGame::Game().DrawDecal(HamsterGame::SCREEN_FRAME.pos+vf2d{i*16.f+4.f,HamsterGame::SCREEN_FRAME.size.y-18.f},HamsterGame::GetGFX("boost_outline.png").Decal(),{0.125f,0.125f},GetPlayer().boostCounter>i?WHITE:BLACK);
else HamsterGame::Game().DrawDecal(HamsterGame::SCREEN_FRAME.pos+vf2d{i*16.f+4.f,HamsterGame::SCREEN_FRAME.size.y-18.f},HamsterGame::GetGFX("boost.png").Decal(),{0.125f,0.125f},GetPlayer().boostCounter>i?WHITE:BLACK);
}
for(int y:std::ranges::iota_view(-1,2)){
for(int x:std::ranges::iota_view(-1,2)){
@ -561,6 +561,7 @@ void Hamster::HandleCollision(){
if(IsPlayerControlled)HamsterAI::OnCheckpointCollected(this->pos);
if(IsPlayerControlled)checkpoint.OnCheckpointCollect();
if(CollectedAllCheckpoints()){finishedRaceTime=HamsterGame::Game().GetRaceTime();}
lastObtainedCheckpointPos=checkpoint.GetPos();
}
}
if(GetState()==NORMAL){
@ -842,7 +843,7 @@ const bool Hamster::CollectedAllCheckpoints()const{
const bool Hamster::HasCollectedCheckpoint(const Checkpoint&cp)const{
return checkpointsCollected.contains(cp.GetPos());
}
const std::vector<Hamster>&Hamster::GetHamsters(){
std::vector<Hamster>&Hamster::GetHamsters(){
return HAMSTER_LIST;
}
const Hamster::HamsterState&Hamster::GetState()const{
@ -1030,3 +1031,11 @@ const vf2d Hamster::GetAINodePositionVariance()const{
}
return finalOffset;
}
const size_t Hamster::GetCheckpointsCollectedCount()const{
return checkpointsCollected.size();
}
const std::optional<vf2d>Hamster::GetLastCollectedCheckpoint()const{
return lastObtainedCheckpointPos;
}

@ -104,7 +104,6 @@ class Hamster{
std::string img;
Animate2D::Animation<AnimationState::AnimationState>animations;
Animate2D::AnimationState internalAnimState;
PlayerControlled IsPlayerControlled;
static std::optional<Hamster*>playerHamster;
HamsterState state{NORMAL};
std::unordered_set<Powerup::PowerupType>powerups;
@ -132,6 +131,7 @@ class Hamster{
std::string colorFilename;
int points{};
std::optional<int>finishedRaceTime;
std::optional<vf2d>lastObtainedCheckpointPos;
HamsterAI::AIType aiLevel{HamsterAI::AIType::NORMAL};
public:
Hamster(const vf2d spawnPos,const std::string&img,const PlayerControlled IsPlayerControlled=NPC);
@ -178,7 +178,7 @@ public:
void SetState(const HamsterState state);
const bool CollectedAllCheckpoints()const;
const bool HasCollectedCheckpoint(const Checkpoint&cp)const;
static const std::vector<Hamster>&GetHamsters();
static std::vector<Hamster>&GetHamsters();
const HamsterState&GetState()const;
const bool BurnedOrDrowned()const;
const bool CanMove()const;
@ -188,4 +188,7 @@ public:
const float GetAINodeDistanceVariance()const;
const vf2d GetAINodePositionVariance()const;
const bool IsBurrowed()const;
const size_t GetCheckpointsCollectedCount()const;
const std::optional<vf2d>GetLastCollectedCheckpoint()const;
PlayerControlled IsPlayerControlled;
};

@ -101,6 +101,7 @@ void HamsterGame::LoadGraphics(){
_LoadImage("background3.png");
_LoadImage("background4.png");
_LoadImage("background5.png");
_LoadImage("raceprogress.png");
}
void HamsterGame::LoadAnimations(){
@ -203,7 +204,13 @@ void HamsterGame::LoadLevel(const std::string&mapName){
}
void HamsterGame::UpdateGame(const float fElapsedTime){
countdownTimer=std::max(0.f,countdownTimer-fElapsedTime);
if(countdownTimer>0.f){
countdownTimer-=fElapsedTime;
if(countdownTimer<=0.f){
countdownTimer=0.f;
leaderboard.OnRaceStart();
}
}
vEye.z+=(Hamster::GetPlayer().GetZ()+zoom-vEye.z)*fLazyFollowRate*fElapsedTime;
speedometerDisplayAmt+=(Hamster::GetPlayer().GetSpeed()-speedometerDisplayAmt)*fLazyFollowRate*fElapsedTime;
@ -222,6 +229,7 @@ void HamsterGame::UpdateGame(const float fElapsedTime){
Powerup::UpdatePowerups(fElapsedTime);
Checkpoint::UpdateCheckpoints(fElapsedTime);
FloatingText::UpdateFloatingText(fElapsedTime);
leaderboard.Update();
border.Update(fElapsedTime);
}
@ -310,6 +318,7 @@ void HamsterGame::DrawGame(){
DrawStringDecal(SCREEN_FRAME.pos+SCREEN_FRAME.size-speedometerStrSize-vf2d{4.f,4.f},speedometerStr,speedometerCol);
DrawDecal({2.f,4.f},GetGFX("radar.png").Decal());
DrawRadar();
leaderboard.Draw(*this);
DrawStringDecal({0,8.f},std::to_string(GetFPS()));
if(countdownTimer>0.f){
Pixel timerColor{fmod(countdownTimer,1.f)<0.5f?GREEN:WHITE};
@ -677,6 +686,10 @@ const bool HamsterGame::RaceCountdownCompleted(){
return countdownTimer==0.f;
}
const geom2d::rect<int>HamsterGame::GetMapSpawnRect()const{
return currentMap.value().GetData().GetSpawnZone();
}
int main()
{
HamsterGame game("Project Hamster");

@ -52,6 +52,7 @@ All rights reserved.
#include "olcPGEX_MiniAudio.h"
#include "HamsterNet.h"
#include "olcPGEX_SplashScreen.h"
#include "HamsterLeaderboard.h"
struct Letter{
vf2d pos;
@ -103,6 +104,7 @@ public:
static void LoadPBs();
const int GetRaceTime();
const bool RaceCountdownCompleted();
const geom2d::rect<int>GetMapSpawnRect()const;
private:
void UpdateGame(const float fElapsedTime);
void DrawGame();
@ -177,4 +179,5 @@ private:
"Red",
"Blue",
};
HamsterLeaderboard leaderboard;
};

@ -0,0 +1,114 @@
#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 "HamsterLeaderboard.h"
#include "Checkpoint.h"
#include "Hamster.h"
#include <ranges>
void HamsterLeaderboard::OnRaceStart(){
hamsterRanking.clear();
for(Hamster&hamster:Hamster::GetHamsters())hamsterRanking.emplace_back(hamster);
}
HamsterLeaderboard::HamsterRanking::HamsterRanking(Hamster&hamsterRef)
:hamster(hamsterRef){}
void HamsterLeaderboard::Update(){
for(HamsterRanking&hamsterRank:hamsterRanking){
Hamster&hamster{hamsterRank.hamster.get()};
float finalRanking{float(hamster.GetCheckpointsCollectedCount())};
std::optional<std::reference_wrapper<Checkpoint>>closestCheckpoint;
for(Checkpoint&cp:Checkpoint::GetCheckpoints()){
float distToHamster{geom2d::line<float>(hamster.GetPos(),cp.GetPos()).length()};
if(hamster.HasCollectedCheckpoint(cp))continue;
if(!closestCheckpoint.has_value())closestCheckpoint=cp;
else if(distToHamster<geom2d::line<float>(hamster.GetPos(),closestCheckpoint.value().get().GetPos()).length())closestCheckpoint=cp;
}
if(closestCheckpoint.has_value()){
vf2d lastCollectedPos{};
if(hamster.GetLastCollectedCheckpoint().has_value())lastCollectedPos=hamster.GetLastCollectedCheckpoint().value();
else lastCollectedPos=HamsterGame::Game().GetMapSpawnRect().middle();
vf2d closestCheckpointPos{closestCheckpoint.value().get().GetPos()};
float totalDist{geom2d::line<float>(lastCollectedPos,closestCheckpointPos).length()};
float distFromHamsterToClosestCheckpoint{geom2d::line<float>(hamster.GetPos(),closestCheckpointPos).length()};
float additionalRanking{std::clamp((totalDist-distFromHamsterToClosestCheckpoint)/totalDist,0.0001f,0.9999f)};
finalRanking+=additionalRanking;
}
hamsterRank.ranking=std::min(finalRanking,float(Checkpoint::GetCheckpoints().size()));
}
std::sort(hamsterRanking.begin(),hamsterRanking.end(),[](const HamsterRanking&rank1,const HamsterRanking&rank2){return rank1.ranking<rank2.ranking;});
}
void HamsterLeaderboard::Draw(HamsterGame&game){
game.DrawRotatedDecal(HamsterGame::SCREEN_FRAME.pos+vf2d{8.f,HamsterGame::SCREEN_FRAME.size.y/2.f},game.GetGFX("raceprogress.png").Decal(),0.f,game.GetGFX("raceprogress.png").Sprite()->Size()/2,{1.f,1.f},WHITE);
vf2d progressBarBottomPos{HamsterGame::SCREEN_FRAME.pos+vf2d{8.f,HamsterGame::SCREEN_FRAME.size.y/2.f+game.GetGFX("raceprogress.png").Sprite()->height/2.f}};
for(int i:std::ranges::iota_view(0U,Checkpoint::GetCheckpoints().size()-1)){
game.FillRectDecal(progressBarBottomPos+vf2d{-5.f,-float((game.GetGFX("raceprogress.png").Sprite()->height/(Checkpoint::GetCheckpoints().size()))*(i+1))},{10.f,1.f},WHITE);
}
int playerPlacement{};
std::optional<std::reference_wrapper<HamsterRanking>>playerHamsterRanking{};
for(int placement{};HamsterRanking&ranking:hamsterRanking){
if(ranking.hamster.get().IsPlayerControlled){
playerHamsterRanking=ranking;
playerPlacement=hamsterRanking.size()-placement;
game.DrawPartialRotatedDecal(progressBarBottomPos+vf2d{0.f,-(float(ranking.ranking)/Checkpoint::GetCheckpoints().size())*game.GetGFX("raceprogress.png").Sprite()->height},ranking.hamster.get().GetCurrentAnimation().GetSourceImage()->Decal(),0.f,{8.f,6.f},{64.f,64.f},{16.f,12.f});
}else game.DrawPartialRotatedDecal(progressBarBottomPos+vf2d{0.f,-(float(ranking.ranking)/Checkpoint::GetCheckpoints().size())*game.GetGFX("raceprogress.png").Sprite()->height},ranking.hamster.get().GetCurrentAnimation().GetSourceImage()->Decal(),0.f,{8.f,6.f},{64.f,64.f},{16.f,12.f});
placement++;
}
if(playerHamsterRanking.has_value()){
std::string addonStr{"th"};
if(playerPlacement==1)addonStr="st";
else if(playerPlacement==2)addonStr="nd";
std::string placementStr{std::format("{}{}",playerPlacement,addonStr)};
vi2d placementStrSize{game.GetTextSizeProp(placementStr)};
Pixel blinkCol{DARK_RED};
if(playerPlacement==1)blinkCol=CYAN;
else if(playerPlacement<=3)blinkCol=DARK_GREEN;
for(int y:std::ranges::iota_view(-1,2)){
for(int x:std::ranges::iota_view(-1,2)){
game.DrawRotatedStringPropDecal(progressBarBottomPos+vf2d{-4.f,8.f}+vi2d{x,y},placementStr,0.f,{},BLACK,{3.f,3.f});
}
}
game.DrawRotatedStringPropDecal(progressBarBottomPos+vf2d{-4.f,8.f},placementStr,0.f,{},blinkCol,{3.f,3.f});
}
}
const float HamsterLeaderboard::HamsterRanking::GetRanking()const{
return ranking;
}

@ -36,15 +36,22 @@ All rights reserved.
*/
#pragma endregion
#pragma once
#include "Hamster.h"
#include <functional>
class HamsterGame;
class Hamster;
class HamsterLeaderboard{
class HamsterRanking{
friend class HamsterLeaderboard;
std::reference_wrapper<Hamster>hamster;
float ranking; //The higher the ranking, the higher the hamster is placed.
float ranking{}; //The higher the ranking, the higher the hamster is placed.
public:
HamsterRanking(Hamster&hamsterRef);
const float GetRanking()const;
};
std::vector<HamsterRanking>hamsterRanking;
public:
void OnRaceStart();
void OnRaceFinished();
void Update();
void Draw(HamsterGame&game);
};

@ -155,9 +155,9 @@ Stage 12: Boss Battle #5 V2 - nene
Live Placement Tracking
Pulsating Animation based on terrain walking across
Pulsating Animation based on terrain walking across (30 min)
Sound Effects
Sound Effects (1 hour)
Footstep sounds(Grass, Sand, Rock, Shore (Shared with Lava and Swamp), Ocean)
Hamster Wheel sounds (Rolling)
@ -184,6 +184,7 @@ Menu Navigations (2 hours)
Grand Prix Management (1 hour)
Unlocks (1 hour)
Leaderboard Management (1 hour)
Tutorial Stuff (3 hours)
Menus
===========

Loading…
Cancel
Save