Keep persistent data about hamsters between map loads. Add menu backgrounds.

sigonasr2 3 months ago
parent e9c7fa0842
commit 363ad229bb
  1. BIN
      assets/background1.png
  2. BIN
      assets/background2.png
  3. BIN
      assets/background3.png
  4. BIN
      assets/background4.png
  5. BIN
      assets/background5.png
  6. 0
      assets/hamster1.png
  7. 29
      src/Hamster.cpp
  8. 3
      src/Hamster.h
  9. 31
      src/HamsterGame.cpp
  10. 15
      src/HamsterGame.h
  11. 4
      src/HamsterJet.cpp
  12. 50
      src/HamsterLeaderboard.h
  13. 2
      src/HamsterNet.cpp
  14. 2
      src/HamsterNet.h
  15. 79
      src/TODO.txt

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

@ -48,7 +48,7 @@ std::vector<Hamster>Hamster::HAMSTER_LIST;
const uint8_t Hamster::MAX_HAMSTER_COUNT{100U}; const uint8_t Hamster::MAX_HAMSTER_COUNT{100U};
const uint8_t Hamster::NPC_HAMSTER_COUNT{5U}; const uint8_t Hamster::NPC_HAMSTER_COUNT{5U};
const std::vector<std::string>Hamster::NPC_HAMSTER_IMAGES{ const std::vector<std::string>Hamster::NPC_HAMSTER_IMAGES{
"hamster.png", "hamster1.png",
"hamster2.png", "hamster2.png",
"hamster3.png", "hamster3.png",
"hamster4.png", "hamster4.png",
@ -57,11 +57,11 @@ const std::vector<std::string>Hamster::NPC_HAMSTER_IMAGES{
"hamster7.png", "hamster7.png",
"hamster8.png", "hamster8.png",
}; };
std::string Hamster::PLAYER_HAMSTER_IMAGE{"hamster.png"}; std::string Hamster::PLAYER_HAMSTER_IMAGE{"hamster1.png"};
std::optional<Hamster*>Hamster::playerHamster; std::optional<Hamster*>Hamster::playerHamster;
Hamster::Hamster(const vf2d spawnPos,const std::string&img,const PlayerControlled IsPlayerControlled) Hamster::Hamster(const vf2d spawnPos,const std::string&img,const PlayerControlled IsPlayerControlled)
:pos(spawnPos),IsPlayerControlled(IsPlayerControlled),randomId(util::random()){ :pos(spawnPos),IsPlayerControlled(IsPlayerControlled),randomId(util::random()),colorFilename(img){
animations=HamsterGame::GetAnimations(img); animations=HamsterGame::GetAnimations(img);
animations.ChangeState(internalAnimState,AnimationState::DEFAULT); animations.ChangeState(internalAnimState,AnimationState::DEFAULT);
} }
@ -297,6 +297,28 @@ void Hamster::MoveHamstersToSpawn(const geom2d::rect<int>startingLoc){
aiFileCount++; aiFileCount++;
MAX_AI_FILES--; MAX_AI_FILES--;
} }
struct HamsterPersistentData{
HamsterAI::AIType aiLevel;
std::string colorFilename;
Hamster::PlayerControlled IsPlayerControlled;
int points;
};
std::vector<HamsterPersistentData>persistentData;
size_t previousHamsterCount{HAMSTER_LIST.size()};
for(int i:std::ranges::iota_view(0U,HAMSTER_LIST.size())){
Hamster&hamster{HAMSTER_LIST[i]};
//Keep persistent data available and reset Hamster.
persistentData.emplace_back(hamster.aiLevel,hamster.colorFilename,hamster.IsPlayerControlled,hamster.points);
}
HAMSTER_LIST.clear();
for(HamsterPersistentData&data:persistentData){
Hamster&newHamster{HAMSTER_LIST.emplace_back(vf2d{},data.colorFilename,data.IsPlayerControlled)};
newHamster.points=data.points;
}
for(Hamster&hamster:HAMSTER_LIST){ for(Hamster&hamster:HAMSTER_LIST){
hamster.SetPos(vf2d{util::random_range(startingLoc.pos.x,startingLoc.pos.x+startingLoc.size.x),util::random_range(startingLoc.pos.y,startingLoc.pos.y+startingLoc.size.y)}); hamster.SetPos(vf2d{util::random_range(startingLoc.pos.x,startingLoc.pos.x+startingLoc.size.x),util::random_range(startingLoc.pos.y,startingLoc.pos.y+startingLoc.size.y)});
@ -538,6 +560,7 @@ void Hamster::HandleCollision(){
FloatingText::CreateFloatingText(pos,std::format("{} / {}",checkpointsCollected.size(),Checkpoint::GetCheckpoints().size()),{WHITE,GREEN},{1.5f,2.f}); FloatingText::CreateFloatingText(pos,std::format("{} / {}",checkpointsCollected.size(),Checkpoint::GetCheckpoints().size()),{WHITE,GREEN},{1.5f,2.f});
if(IsPlayerControlled)HamsterAI::OnCheckpointCollected(this->pos); if(IsPlayerControlled)HamsterAI::OnCheckpointCollected(this->pos);
if(IsPlayerControlled)checkpoint.OnCheckpointCollect(); if(IsPlayerControlled)checkpoint.OnCheckpointCollect();
if(CollectedAllCheckpoints()){finishedRaceTime=HamsterGame::Game().GetRaceTime();}
} }
} }
if(GetState()==NORMAL){ if(GetState()==NORMAL){

@ -129,6 +129,9 @@ class Hamster{
float boostTimer{}; float boostTimer{};
float canCollectWheelPowerupTimer{}; float canCollectWheelPowerupTimer{};
float SEARCH_RANGE{1.f}; float SEARCH_RANGE{1.f};
std::string colorFilename;
int points{};
std::optional<int>finishedRaceTime;
HamsterAI::AIType aiLevel{HamsterAI::AIType::NORMAL}; HamsterAI::AIType aiLevel{HamsterAI::AIType::NORMAL};
public: public:
Hamster(const vf2d spawnPos,const std::string&img,const PlayerControlled IsPlayerControlled=NPC); Hamster(const vf2d spawnPos,const std::string&img,const PlayerControlled IsPlayerControlled=NPC);

@ -96,6 +96,11 @@ void HamsterGame::LoadGraphics(){
_LoadImage("radaricons.png"); _LoadImage("radaricons.png");
_LoadImage("boost.png"); _LoadImage("boost.png");
_LoadImage("boost_outline.png"); _LoadImage("boost_outline.png");
_LoadImage("background1.png");
_LoadImage("background2.png");
_LoadImage("background3.png");
_LoadImage("background4.png");
_LoadImage("background5.png");
} }
void HamsterGame::LoadAnimations(){ void HamsterGame::LoadAnimations(){
@ -114,13 +119,7 @@ void HamsterGame::LoadAnimations(){
} }
ANIMATIONS[std::string(img)].AddState(state,newAnimation); ANIMATIONS[std::string(img)].AddState(state,newAnimation);
}; };
for(int i:std::ranges::iota_view(1,9)){
LoadAnimation(AnimationState::DEFAULT,"hamster.png",{{0,32},{32,32}},0.3f);
LoadAnimation(AnimationState::WHEEL_TOP,"hamster.png",{{0,96},{32,96}},0.1f);
LoadAnimation(AnimationState::WHEEL_BOTTOM,"hamster.png",{{64,96},{96,96}},0.1f);
LoadAnimation(AnimationState::KNOCKOUT,"hamster.png",{{64,32},{96,32}},0.2f);
LoadAnimation(AnimationState::SIDE_VIEW,"hamster.png",{{0,0},{32,0}},0.3f);
for(int i:std::ranges::iota_view(2,9)){
LoadAnimation(AnimationState::DEFAULT,std::format("hamster{}.png",i),{{0,32},{32,32}},0.3f); LoadAnimation(AnimationState::DEFAULT,std::format("hamster{}.png",i),{{0,32},{32,32}},0.3f);
LoadAnimation(AnimationState::WHEEL_TOP,std::format("hamster{}.png",i),{{0,96},{32,96}},0.1f); LoadAnimation(AnimationState::WHEEL_TOP,std::format("hamster{}.png",i),{{0,96},{32,96}},0.1f);
LoadAnimation(AnimationState::WHEEL_BOTTOM,std::format("hamster{}.png",i),{{64,96},{96,96}},0.1f); LoadAnimation(AnimationState::WHEEL_BOTTOM,std::format("hamster{}.png",i),{{64,96},{96,96}},0.1f);
@ -369,11 +368,9 @@ bool HamsterGame::OnUserUpdate(float fElapsedTime){
net.InitSession(); net.InitSession();
netInitialized=true; netInitialized=true;
net.SetName("Sig"); net.SetName("OneLoneHamster");
net.SetColor("Yellow"); net.SetColor(hamsterColorNames[0]);
LoadLevel("StageV.tmx"); //THIS IS TEMPORARY. SetupAndStartRace();
camera.SetTarget(Hamster::GetPlayer().GetPos());
net.StartRace(currentMapName);
} }
runTime+=fElapsedTime; runTime+=fElapsedTime;
@ -658,6 +655,16 @@ const HamsterGame::GameMode HamsterGame::GetGameMode(){
return mode; return mode;
} }
void HamsterGame::SetupAndStartRace(){
LoadLevel("StageV.tmx"); //THIS IS TEMPORARY.
camera.SetTarget(Hamster::GetPlayer().GetPos());
net.StartRace(currentMapName);
}
const int HamsterGame::GetRaceTime(){
return net.GetCurrentRaceTime();
}
int main() int main()
{ {
HamsterGame game("Project Hamster"); HamsterGame game("Project Hamster");

@ -101,6 +101,7 @@ public:
const GameMode GetGameMode(); const GameMode GetGameMode();
static void SavePB(const std::string&mapName,int ms); static void SavePB(const std::string&mapName,int ms);
static void LoadPBs(); static void LoadPBs();
const int GetRaceTime();
private: private:
void UpdateGame(const float fElapsedTime); void UpdateGame(const float fElapsedTime);
void DrawGame(); void DrawGame();
@ -113,6 +114,7 @@ private:
static HamsterGame*self; static HamsterGame*self;
Border border; Border border;
void DrawLevelTiles(); void DrawLevelTiles();
void SetupAndStartRace();
std::optional<TMXParser>currentMap; std::optional<TMXParser>currentMap;
std::optional<TSXParser>currentTileset; std::optional<TSXParser>currentTileset;
double runTime{}; double runTime{};
@ -158,6 +160,19 @@ private:
"StageX.tmx", "StageX.tmx",
"StageXI.tmx", "StageXI.tmx",
"StageXII.tmx", "StageXII.tmx",
"Grand Prix I",
"Grand Prix II",
"Grand Prix III",
}; };
std::string emscripten_temp_val{"123456"}; std::string emscripten_temp_val{"123456"};
std::vector<std::string>hamsterColorNames{
"Yellow",
"Pink",
"Cyan",
"Black",
"Green",
"Purple"
"Red",
"Blue",
};
}; };

@ -192,10 +192,10 @@ void HamsterJet::HandlePlayerControls(){
hamster.vel=vf2d{std::min(hamster.GetMaxSpeed(),hamster.vel.polar().x),hamster.vel.polar().y}.cart(); hamster.vel=vf2d{std::min(hamster.GetMaxSpeed(),hamster.vel.polar().x),hamster.vel.polar().y}.cart();
hamster.frictionEnabled=false; hamster.frictionEnabled=false;
} }
if(HamsterGame::Game().GetKey(UP).bHeld){ if(GetState()==LANDING&&HamsterGame::Game().GetKey(UP).bHeld){
fallSpd=std::min(5.f,fallSpd+5.f*HamsterGame::Game().GetElapsedTime()); fallSpd=std::min(5.f,fallSpd+5.f*HamsterGame::Game().GetElapsedTime());
} }
if(HamsterGame::Game().GetKey(DOWN).bHeld){ if(GetState()==LANDING&&HamsterGame::Game().GetKey(DOWN).bHeld){
fallSpd=std::max(1.f,fallSpd-5.f*HamsterGame::Game().GetElapsedTime()); fallSpd=std::max(1.f,fallSpd-5.f*HamsterGame::Game().GetElapsedTime());
} }
if(HamsterGame::Game().GetKey(SPACE).bPressed){ if(HamsterGame::Game().GetKey(SPACE).bPressed){

@ -0,0 +1,50 @@
#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 "Hamster.h"
class HamsterLeaderboard{
class HamsterRanking{
std::reference_wrapper<Hamster>hamster;
float ranking; //The higher the ranking, the higher the hamster is placed.
};
std::vector<HamsterRanking>hamsterRanking;
public:
void OnRaceStart();
void OnRaceFinished();
};

@ -227,7 +227,7 @@ bool HamsterNet::StartRace(const std::string& map)
return (hamsterNet__startRace() == 1); return (hamsterNet__startRace() == 1);
} }
int HamsterNet::GetCurrentRaceTime(const std::string& map){ int HamsterNet::GetCurrentRaceTime(){
std::chrono::duration<double, std::milli> duration = std::chrono::system_clock::now() - m_tp1; std::chrono::duration<double, std::milli> duration = std::chrono::system_clock::now() - m_tp1;
return static_cast<int>(duration.count()); return static_cast<int>(duration.count());
} }

@ -29,7 +29,7 @@ public:
bool StartRace(const std::string& map); bool StartRace(const std::string& map);
using FinishTime=int; using FinishTime=int;
int GetCurrentRaceTime(const std::string& map); int GetCurrentRaceTime();
std::pair<FinishTime,bool> FinishRace(); std::pair<FinishTime,bool> FinishRace();
std::vector<LeaderboardEntry> GetLeaderboard(const std::string& map, const int offset = 0, const int limit = 100, const std::string& sortBy = "time", bool ascending = true); std::vector<LeaderboardEntry> GetLeaderboard(const std::string& map, const int offset = 0, const int limit = 100, const std::string& sortBy = "time", bool ascending = true);

@ -154,6 +154,85 @@ Stage 11: Boss Battle #3 Alternate - nene
Stage 12: Boss Battle #5 V2 - nene Stage 12: Boss Battle #5 V2 - nene
Live Placement Tracking
Pulsating Animation based on terrain walking across
Sound Effects
Footstep sounds(Grass, Sand, Rock, Shore (Shared with Lava and Swamp), Ocean)
Hamster Wheel sounds (Rolling)
Hamster Jet Idle Sound
Hamster Jet Rockets Sound
Hamster Jet Landing Sound
Powerup Collection Sound
Hamster Wheel Boost Sound
Checkpoint Collection Sound
Hamster Jet Launch Command Sound
Fail to Boost/Jet Launch command sound
Race Finish sound
Race Start sound
Race Countdown timer sound
Menu Select/Back Sound
==========================
Stages VI - XII: 7 Stages (1 hour each, 7 hours total)
Menu Navigations (2 hours)
Grand Prix Management (1 hour)
Unlocks (1 hour)
Leaderboard Management (1 hour)
Menus
===========
Title Screen
Main Menu
Grand Prix
Single Race (Locked behind Grand Prix I)
Options
Quit
(Bottom of Main Menu shows Research Progress)
Races:
1st - 10
2nd - 7
3rd - 5
4th - 3
5th - 2
6th - 1
Research Progress: Each Grand Prix has up to 40 points to earn, thus you need 120 Research points to beat the game.
Grand Prix
Grand Prix I
Grand Prix II (Locked behind Grand Prix I)
Grand Prix III (Locked behind Grand Prix II)
Marathon (Locked behind Game Completion)
Single Race
Track I
Track II
Track III
Track IV
Track V
Track VI
Track VII
Track VIII
Track IX
Track X
Track XI
Track XII
Options
Volume Control
Remap R / Space to different keys.
Racing
Settings Settings
======== ========
Keybind Rebinds Keybind Rebinds
Loading…
Cancel
Save