diff --git a/.gitignore b/.gitignore
index 7d6d598..8582554 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,7 +73,6 @@ x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
-[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
@@ -275,8 +274,6 @@ _pkginfo.txt
# Others
ClientBin/
-~$*
-*~
*.dbmdl
*.dbproj.schemaview
*.jfm
@@ -446,3 +443,5 @@ FodyWeavers.xsd
/CMakeFiles
/CMakeCache.txt
/assets/gfx/Cute RPG
+/bin/assets/gfx/Cute RPG
+/bin
diff --git a/Notes.txt b/Notes.txt
index c7efdad..4f31a1a 100644
--- a/Notes.txt
+++ b/Notes.txt
@@ -37,4 +37,69 @@ Playtesting / Tweaks / Bugfixes
Multiple Puzzle Variations
-Building 1: 8x6
\ No newline at end of file
+Building 1: 8x6
+
+Battle Mechanics:
+Moves have a Color. When the Shep uses a move, their border shifts
+into the color of the move they just used.
+
+If a move hits an opponent of matching color, the damage is doubled.
+
+Each move has a "Recharge time". Using the move before the recharge time
+has finished makes it deal normal damage (%-based on full charge).
+A move that is fully recharged deals 200% damage. Moves start with 1 charge.
+
+Moves that do apply status effects only work when fully charged.
+
+
+Capturing Mechanics:
+The lower the health of the Shep being attacked, the higher the chance of capture.
+From 5% at full health to 95% at lowest health.
+
+For legendaries the capturing rate is halved.
+
+For shinies the capturing rate is halved again.
+
+Chance of a legendary encounter is 1% (1/100)
+Each area has one legendary
+Chance of a shiny encounter is 0.5% (1/200)
+
+
+
+Hint Man
+Everytime the day/night cycle shifts (every 6 minutes) the hint man will move
+to one of a few predetermined spawn locations. If the player finds them they can
+ask for a hint to solving the puzzle (after first encountering it).
+
+First hint is free
+Second hint costs 100 geometry stars (GS)
+Every hint thereafter costs 200 GS
+
+Each time you are shown a picture of the puzzle with one more slot solved.
+When you go to solve it a faint overlay with the hint so far is shown.
+
+Each shape defeated awards you with some GS. Some NPCs will ask if you want to trade Sheps.
+Once traded, you are stuck with it, but may make it easier to unlock what you need.
+
+Each locked area is comprised of multiple Shep:
+First Area: 2 Shep
+Second Area: 3 Shep (2 from second area, 1 from first)
+Third Area: 4 Shep (3 from third area, 1 from second/first)
+Final Area: 6 Shep (4 from final area, 1 from third, 1 from second/first)
+
+In the final area, you will face legendary shep (6 in a row) at really high levels.
+Take them out or capture them and proceed to the higher tiers.
+
+
+
+Damage Formula:
+PWR = Move Base Power
+ATK = Shep Attack
+MAX(PWR+PWR*(ATK*0.01)-PWR*(DEF*0.01),1)
+
+
+Tackle 20 2
+Rush 30 2
+Flash Hit 20 3 Has priority
+Scratch 20 2
+Slash 40 3
\ No newline at end of file
diff --git a/Shep.vcxproj b/Shep.vcxproj
index c5bacd4..949605f 100644
--- a/Shep.vcxproj
+++ b/Shep.vcxproj
@@ -354,6 +354,10 @@ if %errorlevel% neq 0 goto :VCEnd
+
+
+
+
@@ -377,6 +381,10 @@ if %errorlevel% neq 0 goto :VCEnd
+
+
+
+
diff --git a/Shep.vcxproj.filters b/Shep.vcxproj.filters
index a8f98ca..785863e 100644
--- a/Shep.vcxproj.filters
+++ b/Shep.vcxproj.filters
@@ -68,5 +68,11 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
\ No newline at end of file
diff --git a/bin/assets/gfx/Shep.png b/bin/assets/gfx/Shep.png
new file mode 100644
index 0000000..397c31c
Binary files /dev/null and b/bin/assets/gfx/Shep.png differ
diff --git a/bin/assets/gfx/Town1.png b/bin/assets/gfx/Town1.png
new file mode 100644
index 0000000..e972c33
Binary files /dev/null and b/bin/assets/gfx/Town1.png differ
diff --git a/bin/assets/maps/Collision.tsx b/bin/assets/maps/Collision.tsx
new file mode 100644
index 0000000..7dec85d
--- /dev/null
+++ b/bin/assets/maps/Collision.tsx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/bin/assets/maps/Town1.png b/bin/assets/maps/Town1.png
new file mode 100644
index 0000000..f1dfe71
Binary files /dev/null and b/bin/assets/maps/Town1.png differ
diff --git a/bin/assets/maps/Town1.tmx b/bin/assets/maps/Town1.tmx
new file mode 100644
index 0000000..226d90a
--- /dev/null
+++ b/bin/assets/maps/Town1.tmx
@@ -0,0 +1,96 @@
+
+
diff --git a/bin/assets/maps/collision_tileset.png b/bin/assets/maps/collision_tileset.png
new file mode 100644
index 0000000..69dd27c
Binary files /dev/null and b/bin/assets/maps/collision_tileset.png differ
diff --git a/src/AnimationState.h b/src/AnimationState.h
new file mode 100644
index 0000000..6d7b5aa
--- /dev/null
+++ b/src/AnimationState.h
@@ -0,0 +1,46 @@
+#pragma region License
+/*
+License (OLC-3)
+~~~~~~~~~~~~~~~
+
+Copyright 2024 Joshua Sigona
+
+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
+
+enum class AnimationState{
+ STAND,
+ WALK_N,
+ WALK_E,
+ WALK_S,
+ WALK_W,
+};
\ No newline at end of file
diff --git a/src/GameObject.cpp b/src/GameObject.cpp
index 97718ba..24aea3b 100644
--- a/src/GameObject.cpp
+++ b/src/GameObject.cpp
@@ -36,12 +36,12 @@ All rights reserved.
*/
#pragma endregion
-
#include "ShepGame.h"
#include "GameObject.h"
+#include "GameSettings.h"
-GameObject::GameObject(const vf3d&pos,const vf3d&scale,const std::string&spriteMeshName,const MeshType&type)
- :pos(pos),scale(scale),spriteMeshName(spriteMeshName),type(type){};
+GameObject::GameObject(const vf3d&pos,const vf3d&scale,const ObjectID&id,const std::string&spriteMeshName,const MeshType&type)
+ :pos(pos),scale(scale),id(id),spriteMeshName(spriteMeshName),type(type){};
const MeshType GameObject::GetMeshType()const{
return type;
@@ -82,4 +82,18 @@ void GameObject::SetAutoScale(const vf2d&unitDivision){
const vf2d imgSize{spr.Sprite()->Size()};
scale.x=imgSize.x/unitDivision.x;
scale.z=imgSize.y/unitDivision.y;
+}
+
+void GameObject::Update(const float&fElapsedTime){
+ if(anim)ShepGame::Game().animation.UpdateState(*anim,fElapsedTime);
+ auto&game{ShepGame::Game()};
+
+ switch(id){
+ case ObjectID::PLAYER:{
+ if(game.GetKey(Key::W).bHeld){pos.z-=fElapsedTime*GameSettings::playerSpd;}
+ if(game.GetKey(Key::A).bHeld){pos.x-=fElapsedTime*GameSettings::playerSpd;}
+ if(game.GetKey(Key::S).bHeld){pos.z+=fElapsedTime*GameSettings::playerSpd;}
+ if(game.GetKey(Key::D).bHeld){pos.x+=fElapsedTime*GameSettings::playerSpd;}
+ }break;
+ }
}
\ No newline at end of file
diff --git a/src/GameObject.h b/src/GameObject.h
index fba1741..598239c 100644
--- a/src/GameObject.h
+++ b/src/GameObject.h
@@ -38,10 +38,18 @@ All rights reserved.
#pragma once
#include "MeshType.h"
+#include "olcUTIL_Animate2D.h"
+#include "AnimationState.h"
class GameObject{
public:
- GameObject(const vf3d&pos,const vf3d&scale,const std::string&spriteMeshName,const MeshType&type);
+ enum class ObjectID{
+ DEFAULT,
+ PLAYER,
+ };
+
+ GameObject(const vf3d&pos,const vf3d&scale,const ObjectID&id,const std::string&spriteMeshName,const MeshType&type);
+ void Update(const float&fElapsedTime);
const MeshType GetMeshType()const;
const hw3d::mesh&GetMesh()const;
const vf3d&GetPos()const;
@@ -51,6 +59,9 @@ public:
private:
vf3d pos;
vf3d scale;
+ ObjectID id;
std::string spriteMeshName;
MeshType type;
+ AnimationState state;
+ std::optionalanim;
};
\ No newline at end of file
diff --git a/src/GameSettings.h b/src/GameSettings.h
index 00c5876..d60f57d 100644
--- a/src/GameSettings.h
+++ b/src/GameSettings.h
@@ -39,6 +39,8 @@ All rights reserved.
class GameSettings{
public:
- inline static bool LIGHTS_VISIBLE{true};
- inline static bool DEBUG_CAMERA{true};
+ inline static constexpr bool LIGHTS_VISIBLE{true};
+ inline static constexpr bool DEBUG_CAMERA{true};
+
+ inline static constexpr float playerSpd{5.f};
};
\ No newline at end of file
diff --git a/src/ShepGame.cpp b/src/ShepGame.cpp
index 768a3b8..850ed0b 100644
--- a/src/ShepGame.cpp
+++ b/src/ShepGame.cpp
@@ -49,7 +49,7 @@ void ShepGame::LoadSprite(const std::string&filename){
assets[filename].Load("assets/gfx/"+filename);
}
-GameObject&ShepGame::AddGameObject(const vf3d&pos,const vf3d&scale,const std::string&spriteMeshName,const MeshType&type){
+GameObject&ShepGame::AddGameObject(const vf3d&pos,const vf3d&scale,const GameObject::ObjectID&id,const std::string&spriteMeshName,const MeshType&type){
switch(type){
case MeshType::FLOOR:
case MeshType::SPRITE:{
@@ -59,7 +59,7 @@ GameObject&ShepGame::AddGameObject(const vf3d&pos,const vf3d&scale,const std::st
if(!meshes.count(spriteMeshName.substr(0,spriteMeshName.length()-4)+".obj"))LoadObj("building1.obj");
}break;
}
- return objects.emplace_back(pos,scale,spriteMeshName,type);
+ return objects.emplace_back(pos,scale,id,spriteMeshName,type);
}
void ShepGame::AddLight(const Light&light){
@@ -99,10 +99,8 @@ bool ShepGame::OnUserCreate(){
LoadMap("Town1.tmx");
- for(int i:std::ranges::iota_view(0,3)){
- AddGameObject({float(i),0,0},{1,2,1},"nico-Trapper_512.png",MeshType::SPRITE);
- }
- auto&town{AddGameObject({0,0,0},{1,1,1},"Town1.png",MeshType::FLOOR)};
+ AddGameObject({0,0,0},{1,2,1},GameObject::ObjectID::PLAYER,"nico-Trapper_512.png",MeshType::SPRITE);
+ auto&town{AddGameObject({0,0,0},{1,1,1},GameObject::ObjectID::DEFAULT,"Town1.png",MeshType::FLOOR)};
town.SetAutoScale({16,16});
AddLight({{4,0,10},WHITE});
@@ -135,7 +133,7 @@ void ShepGame::LoadMap(const std::string&filename){
for(const XMLTag&tag:zone.properties){
const auto&data{tag.data};
if(data.at("name")=="Building Type"){
- AddGameObject({float(spawnCoords.x),0,float(spawnCoords.y)},{1,1,1},data.at("value")+".png",MeshType::OBJ);
+ AddGameObject({float(spawnCoords.x),0,float(spawnCoords.y)},{1,1,1},GameObject::ObjectID::DEFAULT,data.at("value")+".png",MeshType::OBJ);
}
}
@@ -218,6 +216,7 @@ bool ShepGame::OnUserUpdate(float fElapsedTime){
for(GameObject&obj:objects){
Render3DModel(obj);
+ obj.Update(fElapsedTime);
}
if(GameSettings::LIGHTS_VISIBLE){
diff --git a/src/ShepGame.h b/src/ShepGame.h
index 8cbcd67..58b05a1 100644
--- a/src/ShepGame.h
+++ b/src/ShepGame.h
@@ -49,7 +49,7 @@ class ShepGame : public olc::PixelGameEngine
{
public:
ShepGame();
- GameObject&AddGameObject(const vf3d&pos,const vf3d&scale,const std::string&spriteMeshName,const MeshType&type); //Auto handles resources management! You do not have to add the sprites yourself (via LoadSprite)
+ GameObject&AddGameObject(const vf3d&pos,const vf3d&scale,const GameObject::ObjectID&id,const std::string&spriteMeshName,const MeshType&type); //Auto handles resources management! You do not have to add the sprites yourself (via LoadSprite)
void AddLight(const Light&light);
const Renderable&GetSpr(const std::string&filename)const;
const hw3d::mesh&GetMesh(const std::string&filename)const;
@@ -57,6 +57,7 @@ public:
const hw3d::mesh&GetSpriteMesh()const;
static ShepGame&Game();
void LoadMap(const std::string&filename);
+ Animate2D::Animationanimation;
private:
static ShepGame*game;
diff --git a/src/olcUTIL_Animate2D.h b/src/olcUTIL_Animate2D.h
new file mode 100644
index 0000000..be18a0b
--- /dev/null
+++ b/src/olcUTIL_Animate2D.h
@@ -0,0 +1,226 @@
+/*
+ OneLoneCoder - Animate2D v1.01
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Handles animated Sprites efficiently
+
+
+ License (OLC-3)
+ ~~~~~~~~~~~~~~~
+
+ Copyright 2018 - 2024 OneLoneCoder.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.
+
+ Links
+ ~~~~~
+ YouTube: https://www.youtube.com/javidx9
+ Discord: https://discord.gg/WhwHUMV
+ Twitter: https://www.twitter.com/javidx9
+ Twitch: https://www.twitch.tv/javidx9
+ GitHub: https://www.github.com/onelonecoder
+ Homepage: https://www.onelonecoder.com
+
+ Author
+ ~~~~~~
+ David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022, 2023, 2024
+
+
+ Versions
+ ~~~~~~~~
+ 1.01 +PingPong Style Animation
+*/
+
+#pragma once
+
+#include "olcUTIL_Geometry2D.h"
+#include "olcPixelGameEngine.h"
+#include
+
+namespace olc::utils::Animate2D
+{
+ // This class rerpresents a valid "frame" of an animation. It could be from any image source, and
+ // any location withing that image source. Once it's constructed, it's advised not to change it, as
+ // this likely indicates a usage bug.
+ //
+ // "Sourceless" frames are valid too - this is useful if you have a particular animation set, but
+ // want to apply it to a variety of sources, for example sprite maps with common layouts.
+ class Frame
+ {
+ public:
+ inline Frame(const olc::Renderable* gfxSource, const geom2d::rect& rectSource = { {0,0},{0,0} })
+ : gfxImageSource(gfxSource), rectFrameSource(rectSource)
+ {
+ // If no source rectangle specified then use whole image source. Ignore in the event
+ // that a frame is set up as source-less
+ if(gfxSource && rectFrameSource.size.x == 0)
+ rectFrameSource.size = gfxSource->Sprite()->Size();
+ }
+
+ inline const olc::Renderable* GetSourceImage() const
+ {
+ return gfxImageSource;
+ }
+
+ inline const geom2d::rect& GetSourceRect() const
+ {
+ return rectFrameSource;
+ }
+
+ inline const geom2d::rect& GetSourceUV() const
+ {
+ return geom2d::rect{vf2d{rectFrameSource.pos}/gfxImageSource->Sprite()->Size(),vf2d{rectFrameSource.size}/gfxImageSource->Sprite()->Size()};
+ }
+
+ private:
+ const olc::Renderable* gfxImageSource;
+ geom2d::rect rectFrameSource;
+ };
+
+ // Animation styles decide how the frames should be traversed in time
+ enum class Style : uint8_t
+ {
+ Repeat, // Cycle through, go back to beginning
+ OneShot, // Play once and suspend on final frame
+ PingPong, // Traverse through forwards, then backwards
+ Reverse, // Cycle through sequence backwards
+ };
+
+ class FrameSequence
+ {
+ public:
+ // Constructs a sequence of frames with a duration and a traversal style
+ inline FrameSequence(const float fFrameDuration = 0.1f, const Style nStyle = Style::Repeat)
+ {
+ m_fFrameDuration = fFrameDuration;
+ m_fFrameRate = 1.0f / m_fFrameDuration;
+ m_nStyle = nStyle;
+ }
+
+ // Adds a frame to this sequence
+ inline void AddFrame(const Frame& frame)
+ {
+ m_vFrames.emplace_back(frame);
+ }
+
+ // Returns a Frame Object for a given time into an animation
+ inline const Frame& GetFrame(const float fTime) const
+ {
+ return m_vFrames[ConvertTimeToFrame(fTime)];
+ }
+
+ private:
+ Style m_nStyle;
+ std::vector m_vFrames;
+ float m_fFrameDuration = 0.1f;
+ float m_fFrameRate = 10.0f;
+
+ inline const size_t ConvertTimeToFrame(const float fTime) const
+ {
+ switch (m_nStyle)
+ {
+ case Style::Repeat:
+ return size_t(fTime * m_fFrameRate) % m_vFrames.size();
+ break;
+ case Style::OneShot:
+ return std::clamp(size_t(fTime * m_fFrameRate), size_t(0), m_vFrames.size() - 1);
+ break;
+ case Style::PingPong:
+ {
+ // Thanks @sigonasr2 (discord)
+ size_t frame = size_t(m_fFrameRate * fTime) % (m_vFrames.size() * 2 - 1);
+ return frame >= m_vFrames.size() ? m_vFrames.size() - frame % m_vFrames.size() - 1 : frame;
+ }
+ break;
+ case Style::Reverse:
+ return (m_vFrames.size() - 1) - (size_t(fTime * m_fFrameRate) % m_vFrames.size());
+ break;
+ }
+
+ return 0;
+ }
+ };
+
+ // A Animate2D::State is a lightweight token that can be attached to things
+ // that are animated. Under normal circumstances, it is never updated manually
+ struct AnimationState
+ {
+ private:
+ size_t nIndex = 0;
+ float fTime = 0.0f;
+ template
+ friend class Animation;
+ };
+
+ // Animation object holds a group of frame sequences and can mutate an AnimationState token
+ template
+ class Animation
+ {
+ public:
+ Animation() = default;
+
+ // Change an animation state token to a new state
+ inline bool ChangeState(AnimationState& state, const StatesEnum& sStateName) const
+ {
+ size_t idx = m_mapStateIndices.at(sStateName);
+ if (state.nIndex != idx)
+ {
+ state.fTime = 0.0f;
+ state.nIndex = idx;
+ return true;
+ }
+
+ return false;
+ }
+
+ // Update an animation state token
+ inline void UpdateState(AnimationState& state, const float fElapsedTime) const
+ {
+ state.fTime += fElapsedTime;
+ }
+
+ public:
+ // Retrieve the frame information for a given animation state
+ inline const Frame& GetFrame(const AnimationState& state) const
+ {
+ return m_vSequences[state.nIndex].GetFrame(state.fTime);
+ }
+
+ public:
+ // Add a named Frame sequence as a state
+ inline void AddState(const StatesEnum& sStateName, const FrameSequence& sequence)
+ {
+ m_vSequences.emplace_back(sequence);
+ m_mapStateIndices[sStateName] = m_vSequences.size() - 1;
+ }
+
+ private:
+ std::vector m_vSequences;
+ std::unordered_map m_mapStateIndices;
+ };
+}
\ No newline at end of file