Updated hamster images with profile icons. Added live placement tracking.
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
BIN
assets/raceprogress.png
Normal file
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{
|
||||
@ -1029,4 +1030,12 @@ const vf2d Hamster::GetAINodePositionVariance()const{
|
||||
}break;
|
||||
}
|
||||
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;
|
||||
};
|
114
src/HamsterLeaderboard.cpp
Normal file
@ -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
|
||||
===========
|
||||
|