Start refactor to turn around a bad design decision regarding challenges. Oops. Number of required trophies moved from scene.xml to .challenge file.
git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10885 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
4e0a3b826d
commit
c953803f13
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="city" laps="3"/>
|
<track id="city" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="4"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="farm" laps="3"/>
|
<track id="farm" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="0"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="fortmagma" laps="3"/>
|
<track id="fortmagma" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="23"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="2"/>
|
<karts number="2"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="hacienda" laps="3"/>
|
<track id="hacienda" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="3"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="islandtrack" laps="3"/>
|
<track id="islandtrack" laps="3"/>
|
||||||
<mode major="single" minor="followtheleader"/>
|
<mode major="single" minor="followtheleader"/>
|
||||||
|
<requirements trophies="6"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="jungle" laps="3"/>
|
<track id="jungle" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="1"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="lighthouse" laps="3"/>
|
<track id="lighthouse" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="16"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="mines" laps="3"/>
|
<track id="mines" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="22"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="4"/>
|
<karts number="4"/>
|
||||||
<requirements time="175"/>
|
<requirements time="175"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="minigolf" laps="3"/>
|
<track id="minigolf" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="20"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="olivermath" laps="3"/>
|
<track id="olivermath" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="0"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="1"/>
|
<karts number="1"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="sandtrack" laps="3"/>
|
<track id="sandtrack" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="0"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="1"/>
|
<karts number="1"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="scotland" laps="3"/>
|
<track id="scotland" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="5"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="snowmountain" laps="3"/>
|
<track id="snowmountain" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="0"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="snowtuxpeak" laps="3"/>
|
<track id="snowtuxpeak" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="8"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="startrack" laps="3"/>
|
<track id="startrack" laps="3"/>
|
||||||
<mode major="single" minor="followtheleader"/>
|
<mode major="single" minor="followtheleader"/>
|
||||||
|
<requirements trophies="5"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="6"/>
|
<karts number="6"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="subsea" laps="2"/>
|
<track id="subsea" laps="2"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="2"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="tuxtollway" laps="3"/>
|
<track id="tuxtollway" laps="3"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="19"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="5"/>
|
<karts number="5"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="xr591" laps="2"/>
|
<track id="xr591" laps="2"/>
|
||||||
<mode major="single" minor="quickrace"/>
|
<mode major="single" minor="quickrace"/>
|
||||||
|
<requirements trophies="20"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="1"/>
|
<karts number="1"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<challenge version="2">
|
<challenge version="2">
|
||||||
<track id="zengarden" laps="3"/>
|
<track id="zengarden" laps="3"/>
|
||||||
<mode major="single" minor="timetrial"/>
|
<mode major="single" minor="timetrial"/>
|
||||||
|
<requirements trophies="4"/>
|
||||||
|
|
||||||
<hard>
|
<hard>
|
||||||
<karts number="2"/>
|
<karts number="2"/>
|
||||||
|
@ -35,13 +35,14 @@ ChallengeData::ChallengeData(const std::string& filename)
|
|||||||
throw(std::runtime_error)
|
throw(std::runtime_error)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
m_filename = filename;
|
m_filename = filename;
|
||||||
m_major = RaceManager::MAJOR_MODE_SINGLE;
|
m_major = RaceManager::MAJOR_MODE_SINGLE;
|
||||||
m_minor = RaceManager::MINOR_MODE_NORMAL_RACE;
|
m_minor = RaceManager::MINOR_MODE_NORMAL_RACE;
|
||||||
m_num_laps = -1;
|
m_num_laps = -1;
|
||||||
m_track_id = "";
|
m_track_id = "";
|
||||||
m_gp_id = "";
|
m_gp_id = "";
|
||||||
m_version = 0;
|
m_version = 0;
|
||||||
|
m_num_trophies = 0;
|
||||||
|
|
||||||
for (int d=0; d<RaceManager::DIFFICULTY_COUNT; d++)
|
for (int d=0; d<RaceManager::DIFFICULTY_COUNT; d++)
|
||||||
{
|
{
|
||||||
@ -97,6 +98,15 @@ ChallengeData::ChallengeData(const std::string& filename)
|
|||||||
error("laps");
|
error("laps");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const XMLNode* requirements_node = root->getNode("requirements");
|
||||||
|
if (requirements_node == NULL)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Challenge file " + filename + " has no <requirements> node!");
|
||||||
|
}
|
||||||
|
requirements_node->get("trophies", &m_num_trophies);
|
||||||
|
|
||||||
|
|
||||||
const XMLNode* mode_node = root->getNode("mode");
|
const XMLNode* mode_node = root->getNode("mode");
|
||||||
if (mode_node == NULL)
|
if (mode_node == NULL)
|
||||||
{
|
{
|
||||||
|
@ -76,6 +76,9 @@ private:
|
|||||||
/** Features to unlock. */
|
/** Features to unlock. */
|
||||||
std::vector<UnlockableFeature> m_feature;
|
std::vector<UnlockableFeature> m_feature;
|
||||||
|
|
||||||
|
/** Number of trophies required to access this challenge */
|
||||||
|
int m_num_trophies;
|
||||||
|
|
||||||
irr::core::stringw m_challenge_description;
|
irr::core::stringw m_challenge_description;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -110,6 +113,9 @@ public:
|
|||||||
|
|
||||||
int getNumLaps() const { return m_num_laps; }
|
int getNumLaps() const { return m_num_laps; }
|
||||||
|
|
||||||
|
/** get number of required trophies to start this challenge */
|
||||||
|
int getNumTrophies() const { return m_num_trophies; }
|
||||||
|
|
||||||
void addUnlockTrackReward(const std::string &track_name);
|
void addUnlockTrackReward(const std::string &track_name);
|
||||||
void addUnlockModeReward(const std::string &internal_mode_name,
|
void addUnlockModeReward(const std::string &internal_mode_name,
|
||||||
const irr::core::stringw &user_mode_name);
|
const irr::core::stringw &user_mode_name);
|
||||||
|
@ -141,7 +141,7 @@ void OverWorld::onFirePressed(Controller* who)
|
|||||||
Vec3 kart_xyz = getKart(0)->getXYZ();
|
Vec3 kart_xyz = getKart(0)->getXYZ();
|
||||||
for (unsigned int n=0; n<challenges.size(); n++)
|
for (unsigned int n=0; n<challenges.size(); n++)
|
||||||
{
|
{
|
||||||
if (challenges[n].m_force_field.m_is_locked) continue;
|
if (challenges[n].getForceField().m_is_locked) continue;
|
||||||
|
|
||||||
if ((kart_xyz - Vec3(challenges[n].m_position)).length2_2d() < CHALLENGE_DISTANCE_SQUARED)
|
if ((kart_xyz - Vec3(challenges[n].m_position)).length2_2d() < CHALLENGE_DISTANCE_SQUARED)
|
||||||
{
|
{
|
||||||
|
@ -318,7 +318,7 @@ void RaceGUIOverworld::drawGlobalMiniMap()
|
|||||||
|
|
||||||
//const ChallengeData* c = unlock_manager->getChallenge(challenges[n].m_challenge_id);
|
//const ChallengeData* c = unlock_manager->getChallenge(challenges[n].m_challenge_id);
|
||||||
// bool locked = (m_locked_challenges.find(c) != m_locked_challenges.end());
|
// bool locked = (m_locked_challenges.find(c) != m_locked_challenges.end());
|
||||||
int state = (challenges[n].m_force_field.m_is_locked ? LOCKED : OPEN);
|
int state = (challenges[n].getForceField().m_is_locked ? LOCKED : OPEN);
|
||||||
|
|
||||||
const Challenge* c = unlock_manager->getCurrentSlot()->getChallenge(challenges[n].m_challenge_id);
|
const Challenge* c = unlock_manager->getCurrentSlot()->getChallenge(challenges[n].m_challenge_id);
|
||||||
if (c->isSolved(RaceManager::RD_HARD)) state = COMPLETED_HARD;
|
if (c->isSolved(RaceManager::RD_HARD)) state = COMPLETED_HARD;
|
||||||
@ -340,7 +340,7 @@ void RaceGUIOverworld::drawGlobalMiniMap()
|
|||||||
// ---- Draw nearby challenge if any
|
// ---- Draw nearby challenge if any
|
||||||
for (unsigned int n=0; n<challenges.size(); n++)
|
for (unsigned int n=0; n<challenges.size(); n++)
|
||||||
{
|
{
|
||||||
if (challenges[n].m_force_field.m_is_locked) continue;
|
if (challenges[n].getForceField().m_is_locked) continue;
|
||||||
|
|
||||||
if ((kart_xyz - Vec3(challenges[n].m_position)).length2_2d() < CHALLENGE_DISTANCE_SQUARED)
|
if ((kart_xyz - Vec3(challenges[n].m_position)).length2_2d() < CHALLENGE_DISTANCE_SQUARED)
|
||||||
{
|
{
|
||||||
|
@ -748,53 +748,60 @@ bool Track::loadMainTrack(const XMLNode &root)
|
|||||||
{
|
{
|
||||||
if (!irr_driver->supportsSplatting()) continue;
|
if (!irr_driver->supportsSplatting()) continue;
|
||||||
}
|
}
|
||||||
else if (condition.find("trophies") == 0)
|
else if (condition == "trophies")
|
||||||
{
|
{
|
||||||
std::vector<std::string> split = StringUtils::split(condition, ' ');
|
// Associate force fields and challenges
|
||||||
if (split.size() != 3)
|
// FIXME: this assumes that challenges will appear before force fields in scene.xml
|
||||||
|
// (which however seems to be the case atm)
|
||||||
|
int f = m_force_fields.size() - 1;
|
||||||
|
int closest_challenge_id = -1;
|
||||||
|
float closest_distance = 99999.0f;
|
||||||
|
for (unsigned int c=0; c<m_challenges.size(); c++)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "[Track] WARNING: unexpected number of tokens in '%s'\n", condition.c_str());
|
|
||||||
|
float dist = xyz.getDistanceFromSQ(m_challenges[c].m_position);
|
||||||
|
if (closest_challenge_id == -1 || dist < closest_distance)
|
||||||
|
{
|
||||||
|
closest_challenge_id = c;
|
||||||
|
closest_distance = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(closest_challenge_id >= 0);
|
||||||
|
assert(closest_challenge_id < (int)m_challenges.size());
|
||||||
|
|
||||||
|
const ChallengeData* challenge = unlock_manager->getChallenge(m_challenges[closest_challenge_id].m_challenge_id);
|
||||||
|
if (challenge == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[Track] WARNING: Cannot find challenge named '%s'\n",
|
||||||
|
m_challenges[closest_challenge_id].m_challenge_id.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const std::string& op = split[1];
|
|
||||||
int val = -1;
|
const int val = challenge->getNumTrophies();
|
||||||
if (StringUtils::fromString(split[2], val))
|
bool shown = (unlock_manager->getCurrentSlot()->getPoints() < val);
|
||||||
{
|
m_force_fields.push_back(OverworldForceField(xyz, shown, val));
|
||||||
// only 'lt' is supported atm
|
|
||||||
if (op != "lt")
|
m_challenges[closest_challenge_id].setForceField(m_force_fields[f]);
|
||||||
{
|
|
||||||
fprintf(stderr, "[Track] WARNING: operator '%s' not supported in '%s'\n", op.c_str(),
|
core::stringw msg = StringUtils::toWString(val);
|
||||||
condition.c_str());
|
core::dimension2d<u32> textsize = GUIEngine::getHighresDigitFont()->getDimension(msg.c_str());
|
||||||
}
|
scene::ISceneManager* sm = irr_driver->getSceneManager();
|
||||||
|
|
||||||
bool shown = (unlock_manager->getCurrentSlot()->getPoints() < val);
|
assert(GUIEngine::getHighresDigitFont() != NULL);
|
||||||
|
|
||||||
m_force_fields.push_back(OverworldForceField(xyz, shown, val));
|
scene::ISceneNode* sn =
|
||||||
|
sm->addBillboardTextSceneNode(GUIEngine::getHighresDigitFont(),
|
||||||
core::stringw msg = StringUtils::toWString(val);
|
msg.c_str(),
|
||||||
core::dimension2d<u32> textsize = GUIEngine::getHighresDigitFont()->getDimension(msg.c_str());
|
NULL,
|
||||||
scene::ISceneManager* sm = irr_driver->getSceneManager();
|
core::dimension2df(textsize.Width/45.0f,
|
||||||
|
textsize.Height/45.0f),
|
||||||
assert(GUIEngine::getHighresDigitFont() != NULL);
|
xyz,
|
||||||
|
-1 /* id */,
|
||||||
scene::ISceneNode* sn =
|
video::SColor(255, 255, 225, 0),
|
||||||
sm->addBillboardTextSceneNode(GUIEngine::getHighresDigitFont(),
|
video::SColor(255, 255, 89, 0));
|
||||||
msg.c_str(),
|
m_all_nodes.push_back(sn);
|
||||||
NULL,
|
if (!shown) continue;
|
||||||
core::dimension2df(textsize.Width/45.0f,
|
|
||||||
textsize.Height/45.0f),
|
|
||||||
xyz,
|
|
||||||
-1 /* id */,
|
|
||||||
video::SColor(255, 255, 225, 0),
|
|
||||||
video::SColor(255, 255, 89, 0));
|
|
||||||
m_all_nodes.push_back(sn);
|
|
||||||
if (!shown) continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "[Track] WARNING: token '%s' in '%s' should have been a number\n",
|
|
||||||
split[2].c_str(), condition.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (condition.size() > 0)
|
else if (condition.size() > 0)
|
||||||
{
|
{
|
||||||
@ -968,26 +975,6 @@ bool Track::loadMainTrack(const XMLNode &root)
|
|||||||
|
|
||||||
} // for i
|
} // for i
|
||||||
|
|
||||||
// Associate force fields and challenges
|
|
||||||
for (unsigned int c=0; c<m_challenges.size(); c++)
|
|
||||||
{
|
|
||||||
int closest_field_id = -1;
|
|
||||||
float closest_distance = 99999.0f;
|
|
||||||
for (unsigned int f=0; f<m_force_fields.size(); f++)
|
|
||||||
{
|
|
||||||
float dist = m_force_fields[f].m_position.getDistanceFromSQ(m_challenges[c].m_position);
|
|
||||||
if (closest_field_id == -1 || dist < closest_distance)
|
|
||||||
{
|
|
||||||
closest_field_id = f;
|
|
||||||
closest_distance = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(closest_field_id >= 0);
|
|
||||||
assert(closest_field_id < (int)m_force_fields.size());
|
|
||||||
m_challenges[c].m_force_field = m_force_fields[closest_field_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create LOD nodes
|
// Create LOD nodes
|
||||||
std::vector<LODNode*> lod_nodes;
|
std::vector<LODNode*> lod_nodes;
|
||||||
lodLoader.done(m_root, m_all_cached_meshes, lod_nodes);
|
lodLoader.done(m_root, m_all_cached_meshes, lod_nodes);
|
||||||
|
@ -83,14 +83,38 @@ struct OverworldForceField
|
|||||||
};
|
};
|
||||||
struct OverworldChallenge
|
struct OverworldChallenge
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
OverworldForceField m_force_field;
|
||||||
|
bool m_force_field_set;
|
||||||
|
public:
|
||||||
|
|
||||||
core::vector3df m_position;
|
core::vector3df m_position;
|
||||||
std::string m_challenge_id;
|
std::string m_challenge_id;
|
||||||
OverworldForceField m_force_field;
|
|
||||||
|
|
||||||
OverworldChallenge(core::vector3df position, std::string challenge_id)
|
OverworldChallenge(core::vector3df position, std::string challenge_id)
|
||||||
{
|
{
|
||||||
m_position = position;
|
m_position = position;
|
||||||
m_challenge_id = challenge_id;
|
m_challenge_id = challenge_id;
|
||||||
|
m_force_field_set = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setForceField(OverworldForceField f)
|
||||||
|
{
|
||||||
|
m_force_field = f;
|
||||||
|
m_force_field_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverworldForceField& getForceField()
|
||||||
|
{
|
||||||
|
assert(m_force_field_set);
|
||||||
|
return m_force_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const OverworldForceField& getForceField() const
|
||||||
|
{
|
||||||
|
assert(m_force_field_set);
|
||||||
|
return m_force_field;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user