Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Game submission: Coin Rush #27

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ filesystem/
# Blender backup files
*.blend[0-9]

# IDE files
cmake-*
.idea
CMakeLists.txt
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ $(FILESYSTEM_DIR)/%.xm64: $(ASSETS_DIR)/%.xm
define MINIGAME_template
SRC_$(1) = \
$$(wildcard $$(MINIGAME_DIR)/$(1)/*.c) \
$$(wildcard $$(MINIGAME_DIR)/$(1)/*/*.c) \
$$(wildcard $$(MINIGAME_DIR)/$(1)/**/*.c) \
$$(wildcard $$(MINIGAME_DIR)/$(1)/*.cpp) \
$$(wildcard $$(MINIGAME_DIR)/$(1)/*/*.cpp)
$$(wildcard $$(MINIGAME_DIR)/$(1)/**/*.cpp) \
$$(wildcard $$(MINIGAME_DIR)/$(1)/**/**/*.cpp)
$$(MINIGAMEDSO_DIR)/$(1).dso: $$(SRC_$(1):%.cpp=$$(BUILD_DIR)/%.o)
$$(MINIGAMEDSO_DIR)/$(1).dso: $$(SRC_$(1):%.c=$$(BUILD_DIR)/%.o)
-include $$(MINIGAME_DIR)/$(1)/$(1).mk
Expand Down
1 change: 1 addition & 0 deletions assets/boss_fight/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.blend1
Binary file added assets/boss_fight/09.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/bg00.rgba16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/bgm/Boss.mp3
Binary file not shown.
Binary file added assets/boss_fight/bgm/Main.mp3
Binary file not shown.
Binary file added assets/boss_fight/card00.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/card01.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/card02.i8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/card03.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/dirt05_64.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/editor/boxWood.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/editor/coin.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/editor/guide.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/editor/vase.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/editor/void.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/envMetal.rgba16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/font.ia4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/grass/0.rgba32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/grass/1.rgba32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/grass/2.rgba32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/grass/blade.i8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/grass03.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/grassBorder06.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/hdr.blend
Binary file not shown.
Binary file added assets/boss_fight/knight.ci4.xcf
Binary file not shown.
Binary file added assets/boss_fight/knight0.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/knight1.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/knight2.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/knight3.ci4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/map.blend
Binary file not shown.
Binary file added assets/boss_fight/map.coll
Binary file not shown.
Binary file added assets/boss_fight/map.glb
Binary file not shown.
Binary file added assets/boss_fight/map.scene
Binary file not shown.
Binary file added assets/boss_fight/metal_soft.i8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/boss_fight/obj/barcode.i4.png
Binary file added assets/boss_fight/obj/boss.blend
Binary file not shown.
Binary file added assets/boss_fight/obj/boss.glb
Binary file not shown.
Binary file added assets/boss_fight/obj/box.blend
Binary file not shown.
Binary file added assets/boss_fight/obj/box.glb
Binary file not shown.
Binary file added assets/boss_fight/obj/brick25_64.ci4.png
Binary file added assets/boss_fight/obj/can.blend
Binary file not shown.
Binary file added assets/boss_fight/obj/can.glb
Binary file not shown.
Binary file added assets/boss_fight/obj/can_top.i4.png
Binary file added assets/boss_fight/obj/crate00.ci8.png
Binary file added assets/boss_fight/obj/env1.rgba16.png
Binary file added assets/boss_fight/obj/env_gold.rgba16.png
Binary file added assets/boss_fight/obj/food_label.i4.png
Binary file added assets/boss_fight/obj/glass.i8.png
Binary file added assets/boss_fight/obj/gridColor.rgba16.png
Binary file added assets/boss_fight/obj/gridMain.i4.png
Binary file added assets/boss_fight/obj/htd_text.i4.png
Binary file added assets/boss_fight/obj/noise00.i8.png
Binary file added assets/boss_fight/obj/noise01.i8.png
Binary file added assets/boss_fight/obj/sky.i8.png
Binary file added assets/boss_fight/obj/sky2.i8.png
Binary file added assets/boss_fight/obj/t3d_icon.i4.png
Binary file added assets/boss_fight/obj/t3d_text.i4.png
Binary file added assets/boss_fight/obj/vase.blend
Binary file not shown.
Binary file added assets/boss_fight/obj/vase.glb
Binary file not shown.
Binary file added assets/boss_fight/obj/void.blend
Binary file not shown.
Binary file added assets/boss_fight/obj/void.glb
Binary file not shown.
Binary file added assets/boss_fight/paper02.i4.png
Binary file added assets/boss_fight/paper03.i8.png
Binary file added assets/boss_fight/player.blend
Binary file not shown.
Binary file added assets/boss_fight/player.glb
Binary file not shown.
Binary file added assets/boss_fight/ptx/coin.i4.png
Binary file added assets/boss_fight/ptx/spark.ia8.png
Binary file added assets/boss_fight/ptx/swirl.i4.png
Binary file added assets/boss_fight/sfx/BoxBreak.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/BoxHit.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/CoinGet.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/CoinHit.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/FadeIn.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/FadeOut.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/GrassCut.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/MenuOpen.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/Notice.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/PlImpact.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/PlSpin.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/PotBreak.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/SwordHit.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/UiOk.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/UiSelect.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/VoidOff.wav
Binary file not shown.
Binary file added assets/boss_fight/sfx/VoidOn.wav
Binary file not shown.
Binary file added assets/boss_fight/shadow.i8.png
Binary file added assets/boss_fight/snowIce_012.i8.png
Binary file added assets/boss_fight/spark.i8.png
Binary file added assets/boss_fight/swordFX.i8.png
Binary file added assets/boss_fight/ui/bannerPause.rgba16.png
Binary file added assets/boss_fight/ui/head.rgba32.png
Binary file added assets/boss_fight/ui/marker.ia8.png
Binary file added assets/boss_fight/ui/numbers.ia8.png
Binary file added assets/boss_fight/ui/progPoint.ia8.png
Binary file added assets/boss_fight/ui/txtExit.ia8.png
Binary file added assets/boss_fight/ui/txtGo.ia8.png
Binary file added assets/boss_fight/ui/txtPlayers.ia8.png
Binary file added assets/boss_fight/ui/txtPlayers.xcf
Binary file not shown.
Binary file added assets/boss_fight/ui/txtResume.ia8.png
Binary file added assets/boss_fight/ui/txtWin.ia8.png
Binary file added assets/boss_fight/wood00.i4.png
3 changes: 3 additions & 0 deletions code/boss_fight/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
tools/build
tools/gltf_to_coll
tools/gltf_to_scene
6 changes: 6 additions & 0 deletions code/boss_fight/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Assets
Most assets are CC0 or self made.<br>
I do want to highlight a few:
- BGM-Main: https://johnoestmannmusic.com/0x2a725-orions-geology-workshop/
- BGM-Boss: https://johnoestmannmusic.com/feylon_reactor/
- Knight-Sprites: https://pixelfrog-assets.itch.io/tiny-swords
218 changes: 218 additions & 0 deletions code/boss_fight/audio/audioManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/**
* @copyright 2024 - Max Bebök
* @license MIT
*/
#include "audioManager.h"
#include "../main.h"
#include <libdragon.h>
#include <string>
#include <unistd.h>

namespace {
constexpr int CHANNEL_COUNT = 32;

constexpr int CHANNEL_BGM = 1;
constexpr int CHANNEL_INFO = 3;
constexpr int CHANNEL_SFX = 4;
constexpr int CHANNEL_SFX_COUNT = 8;

constexpr float BGM_FADE_TIME = 2.0f;
uint32_t lastIdx{};

int findFreeChannel() {
lastIdx += 1; // Note: for some reason not cycling through sometimes causes SFX to not play
for(int i=0; i<CHANNEL_SFX_COUNT; ++i) {
int idx = CHANNEL_SFX + ((i + lastIdx) % CHANNEL_SFX_COUNT);
if(!mixer_ch_playing(idx))return idx;
}
return -1;
}

uint32_t getWaveSize(wav64_t *wav) {
return wav->wave.len * wav->wave.channels * (wav->wave.bits / 8);
}

void constructPath(char *path, uint64_t name, std::size_t pathLen) {
const char* nameStr = reinterpret_cast<const char*>(&name);
char *p = path + pathLen - 15;
const char* nameStrEnd = nameStr + 8;
while(*nameStr != '\0' && nameStr != nameStrEnd) {
*(p++) = *(nameStr++);
}
*(p++) = '.'; *(p++) = 'w';
*(p++) = 'a'; *(p++) = 'v';
*(p++) = '6'; *(p++) = '4';
*p = '\0';
}
}

void AudioManager::waveformRead(void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) {
auto* inst = (SFXInstance*)ctx;
if (seeking) {
inst->sampleDataCurr = inst->sampleDataStart + (wpos << inst->bps);
}

uint8_t* ram_addr = (uint8_t*)samplebuffer_append(sbuf, wlen);
int bytes = wlen << inst->bps;
memcpy(ram_addr, inst->sampleDataCurr, bytes);
inst->sampleDataCurr += bytes;
}

AudioManager::AudioManager() {
lastIdx = CHANNEL_SFX;
char path[]{"core/01234567.wav64\0"};
constructPath(path, "Start"_u64, sizeof(path)-1);
wav64_open(&infoSFXStart, path);
constructPath(path, "Winner"_u64, sizeof(path)-1);
wav64_open(&infoSFXWin, path);
}

AudioManager::~AudioManager() {
for(auto &sfx : sfxMap) {
free_uncached(sfx.second.sampleData);
wav64_close(&sfx.second.source);
}
wav64_close(&bgm);
wav64_close(&infoSFXStart);
wav64_close(&infoSFXWin);
}

void AudioManager::update(const T3DVec3 &camPos, const T3DVec3 &camTarget, float deltaTime) {
currCamPos = camPos;
listenerDir = camTarget - camPos;
t3d_vec3_norm(listenerDir);

ticks = get_ticks();
bgmVolume.update(deltaTime);
float fadeNorm = bgmVolume.value / BGM_FADE_TIME;
fadeNorm *= volBGM;
if(fadeNorm <= 0.0) {
if(mixer_ch_playing(CHANNEL_BGM)) {
mixer_ch_stop(CHANNEL_BGM);
wav64_close(&bgm);
}
} else {
mixer_ch_set_vol(CHANNEL_BGM, fadeNorm, fadeNorm);
}

//mixer_try_play();
ticks = get_ticks() - ticks;
}

void AudioManager::playBGM(uint64_t name) {
char path[]{FS_BASE_PATH "bgm/01234567.wav64\0"};
constructPath(path, name, sizeof(path)-1);

wav64_open(&bgm, path);
wav64_set_loop(&bgm, true);
mixer_ch_set_vol(CHANNEL_BGM, 0.01f, 0.01f);
mixer_ch_set_limits(CHANNEL_BGM, 0, 48000, 0);
wav64_play(&bgm, CHANNEL_BGM);

bgmVolume.target = BGM_FADE_TIME;
bgmVolume.value = 0.1f;
}

void AudioManager::stopBGM() {
bgmVolume.target = 0;
}

void AudioManager::setBGMVolume(float vol) {
bgmVolume.target = vol;
}

void AudioManager::setVolume3D(int channel, const T3DVec3 &soundPos, float baseVolume)
{
auto listenerToSfx = soundPos - currCamPos;
float dist = t3d_vec3_len(listenerToSfx);
if(dist < 0.0001f)dist = 0.0001f;
float volume = (1.0f / dist) * baseVolume * 3.0f;
volume = fminf(volume, 1.0f);

listenerToSfx /= dist;
T3DVec3 cross;
t3d_vec3_cross(cross, listenerDir, {0.0f, 1.0f, 0.0f});
float pan = t3d_vec3_dot(listenerToSfx, cross);
pan = pan * 0.5f + 0.5f;
/*debugf("SFX-3D: %.2f %.2f %.2f -> %.2f %.2f %.2f: pan: %f, vol: %f\n",
soundPos.x, soundPos.y, soundPos.z,
currCamPos.x, currCamPos.y, currCamPos.z,
pan, volume);*/
mixer_ch_set_vol_pan(channel, volume, pan);
}

uint32_t AudioManager::playSFX(uint64_t name, const T3DVec3 &pos, SfxConf conf) {

auto it = sfxMap.find(name);
if(it == sfxMap.end()) {
wav64_t sfx;
char path[]{FS_BASE_PATH "sfx/01234567.wav64\0"};
constructPath(path, name, sizeof(path)-1);

wav64_open(&sfx, path);
it = sfxMap.insert({name, {sfx}}).first;

uint32_t dataSize = getWaveSize(&it->second.source);
it->second.source.wave.read = waveformRead;
it->second.source.wave.ctx = &it->second;
it->second.sampleData = (uint8_t*)malloc_uncached(dataSize);
read(it->second.source.current_fd, CachedAddr(it->second.sampleData), dataSize);

for(auto & instance : it->second.instances) {
instance.sampleDataStart = it->second.sampleData;
instance.sampleDataCurr = it->second.sampleData;
instance.bps = (it->second.source.wave.bits == 8 ? 0 : 1) + (it->second.source.wave.channels == 2 ? 1 : 0);
instance.wave = it->second.source;
instance.wave.wave.ctx = &instance;
}
//data_cache_hit_writeback(it->second.sampleData, dataSize);
}

// check if any channel is free
int ch = findFreeChannel();
if(ch < 0) {
//debugf("SFX: no free channels!\n");
return 0;
}

// find free instance in SFX
for(auto & instance : it->second.instances) {
if(instance.channel == 0 || !mixer_ch_playing(instance.channel)) {
instance.channel = ch;
float vol = conf.volume * volSFX;
if(conf.is2D) {
mixer_ch_set_vol(ch, vol, vol);
} else {
setVolume3D(ch, pos, vol);
}
mixer_ch_play(ch, &instance.wave.wave);
if(conf.variation) {
float var = (conf.variation / 255.0f) * Math::rand01() * 10000.0f;
mixer_ch_set_freq(ch, instance.wave.wave.frequency - var);
}
return 0;
}
}
//debugf("SFX: no free instance!\n");
return 0;
}

uint32_t AudioManager::getActiveChannelMask() {
uint32_t mask = 0;
uint32_t maskBit = 1;
for(int i=3; i<CHANNEL_COUNT; ++i) {
if(mixer_ch_playing(i)) {
mask |= maskBit;
}
maskBit <<= 1;
}
return mask;
}

void AudioManager::playInfoSFX(uint64_t name) {
if(name == "Winner"_u64) {
wav64_play(&infoSFXWin, CHANNEL_INFO);
} else {
wav64_play(&infoSFXStart, CHANNEL_INFO);
}
}
71 changes: 71 additions & 0 deletions code/boss_fight/audio/audioManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @copyright 2024 - Max Bebök
* @license MIT
*/
#pragma once

#include "../utils/math.h"
#include <array>
#include <t3d/t3dmath.h>
#include <unordered_map>

struct SfxConf {
float volume{1.0};
uint8_t loop{0};
uint8_t is2D{0};
uint8_t variation{0};
};

class AudioManager {
private:
struct SFXInstance {
wav64_t wave{};
uint8_t *sampleDataStart{};
uint8_t *sampleDataCurr{};
uint8_t channel{};
uint8_t bps{};
};

struct SFX {
wav64_t source{};
uint8_t *sampleData{nullptr};
std::array<SFXInstance, 4> instances{};
};

std::unordered_map<uint64_t, SFX> sfxMap;
wav64_t bgm{};
wav64_t infoSFXStart{};
wav64_t infoSFXWin{};
T3DVec3 currCamPos{0.0f, 0.0f, 0.0f};
T3DVec3 listenerDir{0.0f, 0.0f, -1.0f};

float volBGM{0.7f};
float volSFX{0.9f};
Math::Timer bgmVolume{};

void setVolume3D(int channel, const T3DVec3 &soundPos, float baseVolume = 1.0f);
static void waveformRead(void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking);

public:
uint64_t ticks{0};

AudioManager();
~AudioManager();

void update(const T3DVec3 &camPos, const T3DVec3 &camTarget, float deltaTime);

void playBGM(uint64_t name);
void stopBGM();
void setBGMVolume(float vol);

uint32_t playSFX(uint64_t name, const T3DVec3 &pos, SfxConf conf = {});

uint32_t playSFX(uint64_t name, SfxConf conf = {}) {
conf.is2D = 1;
return playSFX(name, T3DVec3{}, conf);
}

void playInfoSFX(uint64_t name);

uint32_t getActiveChannelMask();
};
63 changes: 63 additions & 0 deletions code/boss_fight/boss_fight.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @copyright 2024 - Max Bebök
* @license MIT
*/

#include <t3d/tpx.h>
#include "main.h"
#include "scene/scene.h"
#include "utils/memory.h"

namespace {
Scene *scene{};
}

extern "C" {
#include "../../core.h"
#include "../../minigame.h"

MinigameDef minigame_def = {
.gamename = "Coin Rush",
.developername = "HailToDodongo",
.description = "Collect as many coins as possible!",
.instructions = "Stick: Move, B: Attack, A: Jump",
};

void minigame_init()
{
Memory::dumpHeap("minigame_init");
constexpr resolution_t res{SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_HEIGHT > 240 ? INTERLACE_HALF : INTERLACE_OFF};
display_init(res, DEPTH_16_BPP, 3, GAMMA_NONE, FILTERS_RESAMPLE);

t3d_init((T3DInitParams){});
tpx_init((TPXInitParams){});

#if RSPQ_PROFILE
rspq_profile_start();
debugf("Profiling enabled\n");
#endif

scene = new Scene();
}

void minigame_fixedloop(float deltatime)
{
//debugf("Fixed loop\n");
}

void minigame_loop(float deltatime)
{
scene->update(deltatime);
scene->draw(deltatime);
}

void minigame_cleanup()
{
rspq_wait();
delete scene;
tpx_destroy();
t3d_destroy();
display_close();
Memory::dumpHeap("minigame_cleanup");
}
}
Loading