mirror of https://github.com/sm64pc/sm64pc.git
Added achievements system prototype
This commit is contained in:
parent
e3f11cbc43
commit
40eedb435b
7
Makefile
7
Makefile
|
@ -591,9 +591,10 @@ endif
|
|||
CXXFLAGS := -std=c++17
|
||||
LDFLAGS += -lstdc++fs
|
||||
|
||||
# ifeq ($(TOGGLE_GAME_DEBUG),1)
|
||||
# VERSION_CFLAGS += -DTOGGLE_GAME_DEBUG
|
||||
# endif
|
||||
ifeq ($(DEBUG),1)
|
||||
CC_CHECK += -DGAME_DEBUG
|
||||
CFLAGS += -DGAME_DEBUG
|
||||
endif
|
||||
|
||||
################################
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ const LevelScript level_intro_n64[] = {
|
|||
// SLEEP(/*frames*/ 500),
|
||||
LOAD_AREA(/*area*/ 1),
|
||||
TRANSITION(/*transType*/ WARP_TRANSITION_FADE_FROM_COLOR, /*time*/ 20, /*color*/ 0x00, 0x00, 0x00),
|
||||
SLEEP(/*frames*/ 90),
|
||||
SLEEP(/*frames*/ 90),
|
||||
TRANSITION(/*transType*/ WARP_TRANSITION_FADE_INTO_COLOR, /*time*/ 20, /*color*/ 0x00, 0x00, 0x00),
|
||||
SLEEP(/*frames*/ 21),
|
||||
CMD2A(/*unk2*/ 1),
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mario_step.h"
|
||||
#include "save_file.h"
|
||||
#include "thread6.h"
|
||||
#include "moon/achievements/achievements.h"
|
||||
#ifdef BETTERCAMERA
|
||||
#include "bettercamera.h"
|
||||
#endif
|
||||
|
@ -490,6 +491,8 @@ s32 act_triple_jump(struct MarioState *m) {
|
|||
play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAHOO);
|
||||
#endif
|
||||
|
||||
show_achievement("achievement.doATripleJump");
|
||||
|
||||
common_air_action_step(m, ACT_TRIPLE_JUMP_LAND, MARIO_ANIM_TRIPLE_JUMP, 0);
|
||||
if (m->action == ACT_TRIPLE_JUMP_LAND) {
|
||||
queue_rumble_data(5, 40);
|
||||
|
@ -985,7 +988,7 @@ s32 act_burning_jump(struct MarioState *m) {
|
|||
if (m->health < 0x100) {
|
||||
m->health = 0xFF;
|
||||
}
|
||||
|
||||
|
||||
reset_rumble_timers();
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
@ -2,82 +2,106 @@
|
|||
#include "moon/mod-engine/hooks/hook.h"
|
||||
#include "moon/ui/utils/moon-draw-utils.h"
|
||||
#include "moon/ui/interfaces/moon-screen.h"
|
||||
|
||||
#include "moon/utils/umath.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
std::map<Achievement*, bool> entries;
|
||||
extern "C" {
|
||||
#include "pc/cheats.h"
|
||||
#include "moon/utils/moon-gfx.h"
|
||||
#include "pc/platform.h"
|
||||
}
|
||||
|
||||
namespace AchievementList {
|
||||
Achievement* TRIPLE_JUMP = new Achievement("achievement.doATripleJump", "test", "Getting higher", "Do a triple jump", 0, nullptr);
|
||||
struct AchievementEntry {
|
||||
long long launchTime;
|
||||
int state = 0;
|
||||
int width = 0;
|
||||
int height = 32;
|
||||
bool dead = false;
|
||||
};
|
||||
|
||||
namespace Moon {
|
||||
void showAchievement(Achievement* achievement){
|
||||
if(entries.find(achievement) == entries.end())
|
||||
entries[achievement] = false;
|
||||
}
|
||||
std::map<std::string, Achievement*> registeredAchievements;
|
||||
std::map<Achievement*, AchievementEntry*> entries;
|
||||
|
||||
void showAchievementById(Achievement* achievement){
|
||||
namespace AchievementList {
|
||||
Achievement* TRIPLE_JUMP = MoonAchievements::bind(new Achievement("achievement.doATripleJump", "textures/segment2/segment2.05C00.rgba16", "Getting higher", "Do a triple jump", 0, 5, nullptr));
|
||||
Achievement* CHEATER = MoonAchievements::bind(new Achievement("", "textures/segment2/segment2.05C00.rgba16", "What a loser!", "Turn on the cheats", 0, 5, nullptr));
|
||||
};
|
||||
|
||||
namespace MoonAchievements {
|
||||
Achievement* bind(Achievement* achievement){
|
||||
registeredAchievements[achievement->id] = achievement;
|
||||
return achievement;
|
||||
}
|
||||
}
|
||||
int w = 0;
|
||||
int h = 32;
|
||||
int aId = -1;
|
||||
|
||||
bool launched = false;
|
||||
int delay = 35;
|
||||
int cgid = 0;
|
||||
bool cheatsGotEnabled = false;
|
||||
|
||||
namespace MoonInternal{
|
||||
|
||||
void setupAchievementEngine(std::string status){
|
||||
entries[AchievementList::TRIPLE_JUMP] = false;
|
||||
|
||||
if(status == "Update"){
|
||||
if(Cheats.EnableCheats) {
|
||||
Moon::showAchievement(AchievementList::CHEATER);
|
||||
cheatsGotEnabled = true;
|
||||
}
|
||||
}
|
||||
if(status == "Init"){
|
||||
|
||||
Moon::registerHookListener({.hookName = HUD_DRAW, .callback = [](HookCall call){
|
||||
cgid++;
|
||||
int id = 0;
|
||||
if(cgid > 90){
|
||||
launched = true;
|
||||
}
|
||||
|
||||
if(!launched) return;
|
||||
|
||||
for(auto &achievement : entries){
|
||||
if(!achievement.second){
|
||||
int achievementWidth = 128;
|
||||
long long millis = moon_get_milliseconds();
|
||||
AchievementEntry* aEntry = achievement.second;
|
||||
if(aEntry->dead || achievement.second->launchTime >= millis) continue;
|
||||
bool shouldClose = millis >= achievement.second->launchTime + achievement.first->duration;
|
||||
|
||||
if(!(gGlobalTimer % delay)){
|
||||
aId++;
|
||||
int aX = GetScreenWidth(false) / 2 - aEntry->width / 2;
|
||||
int aY = GetScreenHeight() - aEntry->height - 20;
|
||||
|
||||
MoonDrawRectangle(aX, aY, aEntry->width, aEntry->height, {0, 0, 0, 150}, false);
|
||||
if(!shouldClose){
|
||||
if(aEntry->width >= 32)
|
||||
MoonDrawTexture(GFX_DIMENSIONS_FROM_LEFT_EDGE(aX), aY, 31, 31, sys_strdup(achievement.first->icon.c_str()));
|
||||
if(aEntry->width >= achievementWidth / 2){
|
||||
MoonDrawText(aX + 32 + 5, aY + 2, achievement.first->title, 0.8, {255,255,255,255}, true, false);
|
||||
MoonDrawText(aX + 32 + 5, aY + 16, achievement.first->description, 0.8, {255,255,255,255}, true, false);
|
||||
}
|
||||
|
||||
switch(aId){
|
||||
case 0:
|
||||
if(w < 32){
|
||||
w += 4;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(w < 128){
|
||||
w += 16;
|
||||
}
|
||||
delay = 60;
|
||||
break;
|
||||
case 2:
|
||||
if(w > 0){
|
||||
w -= 36;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
MoonDrawRectangle(GetScreenWidth(false) / 2 - w / 2, GetScreenHeight() - h - 20, w, h, {255, 255, 255, 100}, false);
|
||||
|
||||
if(w >= 32)
|
||||
MoonDrawRectangle(GetScreenWidth(false) / 2 - w / 2, GetScreenHeight() - h - 20, 32, 32, {0, 255, 200, 100}, false);
|
||||
id++;
|
||||
}
|
||||
|
||||
aEntry->width = MathUtil::Lerp(aEntry->width, !shouldClose ? achievementWidth : 0, !shouldClose ? 0.1f : 0.35f);
|
||||
aEntry->dead = shouldClose && aEntry->width <= 0;
|
||||
break;
|
||||
id++;
|
||||
}
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Moon {
|
||||
void showAchievement(Achievement* achievement){
|
||||
if(cheatsGotEnabled) return;
|
||||
if(entries.find(achievement) != entries.end()) return;
|
||||
long long time = 0;
|
||||
if(entries.size() > 0)
|
||||
for(auto &achievement : entries){
|
||||
if(achievement.second->dead) continue;
|
||||
long long now = moon_get_milliseconds();
|
||||
time += (now - achievement.second->launchTime) + achievement.first->duration;
|
||||
}
|
||||
entries[achievement] = new AchievementEntry({.launchTime = time == 0 ? moon_get_milliseconds() + 100 : time});
|
||||
}
|
||||
|
||||
void showAchievementById(std::string id){
|
||||
if(registeredAchievements.find(id) == registeredAchievements.end()) return;
|
||||
Moon::showAchievement(registeredAchievements[id]);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void show_achievement(char* id){
|
||||
Moon::showAchievementById(std::string(id));
|
||||
}
|
||||
}
|
|
@ -1,22 +1,24 @@
|
|||
#ifndef MoonAchievements
|
||||
#define MoonAchievements
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
|
||||
class Achievement {
|
||||
protected:
|
||||
public:
|
||||
std::string id;
|
||||
std::string icon;
|
||||
std::string title;
|
||||
std::string description;
|
||||
Achievement* parent;
|
||||
int points;
|
||||
public:
|
||||
Achievement(std::string id, std::string icon, std::string title, std::string description, int points, Achievement* parent){
|
||||
long long duration;
|
||||
Achievement(std::string id, std::string icon, std::string title, std::string description, int points, float duration, Achievement* parent){
|
||||
this->id = id;
|
||||
this->icon = icon;
|
||||
this->title = title;
|
||||
this->description = description;
|
||||
this->duration = (long long)(duration * 1000LL);
|
||||
this->parent = parent;
|
||||
this->points = points;
|
||||
}
|
||||
|
@ -30,8 +32,15 @@ namespace MoonInternal{
|
|||
void setupAchievementEngine(std::string status);
|
||||
}
|
||||
|
||||
namespace MoonAchievements {
|
||||
Achievement* bind(Achievement* achievement);
|
||||
}
|
||||
|
||||
namespace AchievementList {
|
||||
extern Achievement* TRIPLE_JUMP;
|
||||
}
|
||||
|
||||
#else
|
||||
void show_achievement(char* id);
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
#include "animated.h"
|
||||
#include "moon/mod-engine/hooks/hook.h"
|
||||
#include <sys/time.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
extern "C" {
|
||||
#include "moon/utils/moon-gfx.h"
|
||||
}
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace std;
|
||||
|
||||
map<string, AnimatedEntry*> textures;
|
||||
|
||||
long long getMilliseconds(){
|
||||
struct timeval te;
|
||||
gettimeofday(&te, NULL);
|
||||
return te.tv_sec * 1000LL + te.tv_usec / 1000;
|
||||
}
|
||||
|
||||
void AnimatedModifier::onInit(){
|
||||
Moon::registerHookListener({.hookName = TEXTURE_BIND, .callback = [](HookCall call){
|
||||
char* *hookTexture = reinterpret_cast<char**>(call.baseArgs["texture"]);
|
||||
|
@ -25,7 +22,7 @@ void AnimatedModifier::onInit(){
|
|||
AnimatedEntry* entry = textures[texName];
|
||||
Frame *frame = entry->frames[entry->lastFrame];
|
||||
|
||||
if(getMilliseconds() >= entry->lastTime + frame->delay){
|
||||
if(moon_get_milliseconds() >= entry->lastTime + frame->delay){
|
||||
int maxFrames = entry->frames.size() - 1;
|
||||
bool reachMax = (entry->lastFrame < maxFrames);
|
||||
if(entry->bounce){
|
||||
|
@ -36,7 +33,7 @@ void AnimatedModifier::onInit(){
|
|||
}
|
||||
|
||||
entry->lastFrame += entry->bounce ? entry->lastBounce ? -1 : 1 : (reachMax ? 1 : -entry->lastFrame);
|
||||
entry->lastTime = getMilliseconds();
|
||||
entry->lastTime = moon_get_milliseconds();
|
||||
frame = entry->frames[entry->lastFrame];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#include "mdebug.h"
|
||||
|
||||
#include "moon/achievements/achievements.h"
|
||||
#include "moon/ui/widgets/mw-value.h"
|
||||
#include "moon/ui/moon-ui-manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern "C" {
|
||||
#include "pc/configfile.h"
|
||||
#include "pc/pc_main.h"
|
||||
}
|
||||
|
||||
MDebugCategory::MDebugCategory() : MoonCategory("Moon Debug"){
|
||||
this->titleKey = false;
|
||||
this->catOptions.push_back(new MWValue(22, 57 + (0 * 17), "Trigger Notification", { .btn = [](){
|
||||
Moon::showAchievement(AchievementList::TRIPLE_JUMP);
|
||||
}}, false));
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef MoonDebugCategory
|
||||
#define MoonDebugCategory
|
||||
|
||||
#include "mcategory.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class MDebugCategory : public MoonCategory {
|
||||
public:
|
||||
MDebugCategory();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -7,6 +7,7 @@
|
|||
#include "moon/ui/screens/options/categories/mcategory.h"
|
||||
|
||||
#include "moon/ui/screens/options/categories/mgame.h"
|
||||
#include "moon/ui/screens/options/categories/mdebug.h"
|
||||
#include "moon/ui/screens/options/categories/mvideo.h"
|
||||
#include "moon/ui/screens/options/categories/maudio.h"
|
||||
#include "moon/ui/screens/options/categories/mcheats.h"
|
||||
|
@ -47,6 +48,9 @@ void MoonOptMain::Init(){
|
|||
void MoonOptMain::Mount(){
|
||||
this->widgets.clear();
|
||||
categories.push_back(new MGameCategory());
|
||||
#ifdef GAME_DEBUG
|
||||
categories.push_back(new MDebugCategory());
|
||||
#endif
|
||||
#ifdef BETTERCAMERA
|
||||
categories.push_back(new MCameraCategory());
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,13 @@
|
|||
#include "gfx_dimensions.h"
|
||||
#include "config.h"
|
||||
#include "game/geo_misc.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
long long moon_get_milliseconds(){
|
||||
struct timeval te;
|
||||
gettimeofday(&te, NULL);
|
||||
return te.tv_sec * 1000LL + te.tv_usec / 1000;
|
||||
}
|
||||
|
||||
f32 moon_get_text_width(u8* text, float scale, u8 colored) {
|
||||
f32 size = 0;
|
||||
|
|
|
@ -10,6 +10,7 @@ struct Color {
|
|||
int a;
|
||||
};
|
||||
|
||||
long long moon_get_milliseconds();
|
||||
f32 moon_get_text_width(u8* text, float scale, u8 colored);
|
||||
void moon_draw_colored_text(f32 x, f32 y, const u8 *str, float scale, struct Color c);
|
||||
void moon_draw_text(f32 x, f32 y, const u8 *str, float scale);
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
#ifndef UMATH_H
|
||||
#define UMATH_H
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
class MathUtil{
|
||||
public:
|
||||
inline static float Sin(float f) { return (float)sin(f); }
|
||||
|
||||
// Returns the cosine of angle /f/ in radians.
|
||||
inline static float Cos(float f) { return (float)cos(f); }
|
||||
|
||||
// Returns the tangent of angle /f/ in radians.
|
||||
inline static float Tan(float f) { return (float)tan(f); }
|
||||
|
||||
// Returns the arc-sine of /f/ - the angle in radians whose sine is /f/.
|
||||
inline static float Asin(float f) { return (float)asin(f); }
|
||||
|
||||
// Returns the arc-cosine of /f/ - the angle in radians whose cosine is /f/.
|
||||
inline static float Acos(float f) { return (float)acos(f); }
|
||||
|
||||
// Returns the arc-tangent of /f/ - the angle in radians whose tangent is /f/.
|
||||
inline static float Atan(float f) { return (float)atan(f); }
|
||||
|
||||
// Returns the angle in radians whose ::ref::Tan is @@y/x@@.
|
||||
inline static float Atan2(float y, float x) { return (float)atan2(y, x); }
|
||||
|
||||
// Returns square root of /f/.
|
||||
inline static float Sqrt(float f) { return (float)sqrt(f); }
|
||||
|
||||
// Returns the absolute value of /f/.
|
||||
inline static float Abs(float f) { return (float)abs(f); }
|
||||
|
||||
// Returns the absolute value of /value/.
|
||||
inline static int Abs(int value) { return abs(value); }
|
||||
|
||||
/// *listonly*
|
||||
inline static float Min(float a, float b) { return a < b ? a : b; }
|
||||
// Returns the smallest of two or more values.
|
||||
inline static float Min(float values[]){
|
||||
int len = sizeof(values);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
float m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] < m)
|
||||
m = values[i];
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// *listonly*
|
||||
inline static int Min(int a, int b) { return a < b ? a : b; }
|
||||
// Returns the smallest of two or more values.
|
||||
inline static int Min(int values[])
|
||||
{
|
||||
int len = sizeof(values);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
int m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] < m)
|
||||
m = values[i];
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// *listonly*
|
||||
inline static float Max(float a, float b) { return a > b ? a : b; }
|
||||
// Returns largest of two or more values.
|
||||
inline static float Max(float values[]){
|
||||
int len = sizeof(values);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
float m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] > m)
|
||||
m = values[i];
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// *listonly*
|
||||
inline static int Max(int a, int b) { return a > b ? a : b; }
|
||||
// Returns the largest of two or more values.
|
||||
inline static int Max(int values[])
|
||||
{
|
||||
int len = sizeof(values);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
int m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] > m)
|
||||
m = values[i];
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
// Returns /f/ raised to power /p/.
|
||||
inline static float Pow(float f, float p) { return (float)pow(f, p); }
|
||||
|
||||
// Returns e raised to the specified power.
|
||||
inline static float Exp(float power) { return (float)exp(power); }
|
||||
|
||||
// Returns the natural (base e) logarithm of a specified number.
|
||||
inline static float Log(float f) { return (float)log(f); }
|
||||
|
||||
// Returns the base 10 logarithm of a specified number.
|
||||
inline static float Log10(float f) { return (float)log10(f); }
|
||||
|
||||
// Returns the smallest integer greater to or equal to /f/.
|
||||
inline static float Ceil(float f) { return (float)ceil(f); }
|
||||
|
||||
// Returns the largest integer smaller to or equal to /f/.
|
||||
inline static float Floor(float f) { return (float)floor(f); }
|
||||
|
||||
// Returns /f/ rounded to the nearest integer.
|
||||
inline static float Round(float f) { return (float)round(f); }
|
||||
|
||||
// Returns the smallest integer greater to or equal to /f/.
|
||||
inline static int CeilToInt(float f) { return (int)ceil(f); }
|
||||
|
||||
// Returns the largest integer smaller to or equal to /f/.
|
||||
inline static int FloorToInt(float f) { return (int)floor(f); }
|
||||
|
||||
// Returns /f/ rounded to the nearest integer.
|
||||
inline static int RoundToInt(float f) { return (int)round(f); }
|
||||
|
||||
// Returns the sign of /f/.
|
||||
inline static float Sign(float f) { return f >= 0.0 ? 1.0 : -1.0; }
|
||||
|
||||
// The infamous ''3.14159265358979...'' value (RO).
|
||||
const float PI = (float)PI;
|
||||
|
||||
// Degrees-to-radians conversion constant (RO).
|
||||
const float Deg2Rad = PI * 2.0 / 360.0;
|
||||
|
||||
// Radians-to-degrees conversion constant (RO).
|
||||
const float Rad2Deg = 1.0 / Deg2Rad;
|
||||
|
||||
// Clamps a value between a minimum float and maximum float value.
|
||||
inline static float Clamp(float value, float min, float max)
|
||||
{
|
||||
if (value < min)
|
||||
value = min;
|
||||
else if (value > max)
|
||||
value = max;
|
||||
return value;
|
||||
}
|
||||
|
||||
// Clamps value between min and max and returns value.
|
||||
// Set the position of the transform to be that of the time
|
||||
// but never less than 1 or more than 3
|
||||
//
|
||||
inline static int Clamp(int value, int min, int max)
|
||||
{
|
||||
if (value < min)
|
||||
value = min;
|
||||
else if (value > max)
|
||||
value = max;
|
||||
return value;
|
||||
}
|
||||
|
||||
// Clamps value between 0 and 1 and returns value
|
||||
inline static float Clamp01(float value)
|
||||
{
|
||||
if (value < 0.0)
|
||||
return 0.0;
|
||||
else if (value > 1.0)
|
||||
return 1.0;
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
// Interpolates between /a/ and /b/ by /t/. /t/ is clamped between 0 and 1.
|
||||
inline static float Lerp(float a, float b, float t)
|
||||
{
|
||||
return a + (b - a) * Clamp01(t);
|
||||
}
|
||||
|
||||
// Interpolates between /a/ and /b/ by /t/ without clamping the interpolant.
|
||||
inline static float LerpUnclamped(float a, float b, float t)
|
||||
{
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
// Same as ::ref::Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.
|
||||
inline static float LerpAngle(float a, float b, float t)
|
||||
{
|
||||
float delta = Repeat((b - a), 360);
|
||||
if (delta > 180)
|
||||
delta -= 360;
|
||||
return a + delta * Clamp01(t);
|
||||
}
|
||||
|
||||
// Moves a value /current/ towards /target/.
|
||||
inline static float MoveTowards(float current, float target, float maxDelta)
|
||||
{
|
||||
if (abs(target - current) <= maxDelta)
|
||||
return target;
|
||||
return current + Sign(target - current) * maxDelta;
|
||||
}
|
||||
|
||||
// Same as ::ref::MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.
|
||||
inline static float MoveTowardsAngle(float current, float target, float maxDelta)
|
||||
{
|
||||
float deltaAngle = DeltaAngle(current, target);
|
||||
if (-maxDelta < deltaAngle && deltaAngle < maxDelta)
|
||||
return target;
|
||||
target = current + deltaAngle;
|
||||
return MoveTowards(current, target, maxDelta);
|
||||
}
|
||||
|
||||
// Interpolates between /min/ and /max/ with smoothing at the limits.
|
||||
inline static float SmoothStep(float from, float to, float t)
|
||||
{
|
||||
t = Clamp01(t);
|
||||
t = -2.0F * t * t * t + 3.0F * t * t;
|
||||
return to * t + from * (1.0 - t);
|
||||
}
|
||||
|
||||
//*undocumented
|
||||
inline static float Gamma(float value, float absmax, float gamma)
|
||||
{
|
||||
bool negative = value < 0.0;
|
||||
float absval = Abs(value);
|
||||
if (absval > absmax)
|
||||
return negative ? -absval : absval;
|
||||
|
||||
float result = Pow(absval / absmax, gamma) * absmax;
|
||||
return negative ? -result : result;
|
||||
}
|
||||
|
||||
// Gradually changes a value towards a desired goal over time.
|
||||
inline static float SmoothDamp(float current, float target, float currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
|
||||
{
|
||||
// Based on Game Programming Gems 4 Chapter 1.10
|
||||
smoothTime = Max(0.0001F, smoothTime);
|
||||
float omega = 2.0 / smoothTime;
|
||||
|
||||
float x = omega * deltaTime;
|
||||
float exp = 1.0 / (1.0 + x + 0.48F * x * x + 0.235F * x * x * x);
|
||||
float change = current - target;
|
||||
float originalTo = target;
|
||||
|
||||
// Clamp maximum speed
|
||||
float maxChange = maxSpeed * smoothTime;
|
||||
change = Clamp(change, -maxChange, maxChange);
|
||||
target = current - change;
|
||||
|
||||
float temp = (currentVelocity + omega * change) * deltaTime;
|
||||
currentVelocity = (currentVelocity - omega * temp) * exp;
|
||||
float output = target + (change + temp) * exp;
|
||||
|
||||
// Prevent overshooting
|
||||
if (originalTo - current > 0.0F == output > originalTo)
|
||||
{
|
||||
output = originalTo;
|
||||
currentVelocity = (output - originalTo) / deltaTime;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
inline static float SmoothDampAngle(float current, float target, float currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
|
||||
{
|
||||
return SmoothDampAngle2(current, target, currentVelocity, smoothTime, maxSpeed, deltaTime);
|
||||
}
|
||||
|
||||
|
||||
inline static float SmoothDampAngle1(float current, float target, float currentVelocity, float smoothTime, float deltaTime)
|
||||
{
|
||||
float maxSpeed = (float) std::numeric_limits<double>::infinity();
|
||||
return SmoothDampAngle(current, target, currentVelocity, smoothTime, maxSpeed, deltaTime);
|
||||
}
|
||||
|
||||
// Gradually changes an angle given in degrees towards a desired goal angle over time.
|
||||
inline static float SmoothDampAngle2(float current, float target, float currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
|
||||
{
|
||||
target = current + DeltaAngle(current, target);
|
||||
return SmoothDamp(current, target, currentVelocity, smoothTime, maxSpeed, deltaTime);
|
||||
}
|
||||
|
||||
// Loops the value t, so that it is never larger than length and never smaller than 0.
|
||||
inline static float Repeat(float t, float length)
|
||||
{
|
||||
return Clamp((float) (t - floor(t / length) * length), 0.0f, length);
|
||||
}
|
||||
|
||||
// PingPongs the value t, so that it is never larger than length and never smaller than 0.
|
||||
inline static float PingPong(float t, float length)
|
||||
{
|
||||
t = Repeat(t, length * 2.0);
|
||||
return length - Abs(t - length);
|
||||
}
|
||||
|
||||
// Calculates the ::ref::Lerp parameter between of two values.
|
||||
inline static float InverseLerp(float a, float b, float value)
|
||||
{
|
||||
if (a != b)
|
||||
return Clamp01((value - a) / (b - a));
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Calculates the shortest difference between two given angles.
|
||||
inline static float DeltaAngle(float current, float target)
|
||||
{
|
||||
float delta = Repeat((target - current), 360.0F);
|
||||
if (delta > 180.0F)
|
||||
delta -= 360.0F;
|
||||
return delta;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue