Skip to content
Open
18 changes: 18 additions & 0 deletions data/commands.xml
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,24 @@ here later. For now, please refer to the code for the strings being used. -->
state-scope="SS_LOBBY"
/>
</command>

<command name="itempolicy"
usage="/itempolicy"
description="Shows item policy."
permissions="UP_EVERYONE | PE_ALLOW_ANYONE"
permissions-verbose="everyone; invokable for another player"
state-scope="SS_ALWAYS"
>
<command name="="
usage="/itempolicy ((num) (sections, where each section is (bitstring data1 data2 data3 data4 num_pairs [item1 weight1...]) ))"
omit-name="true"
permissions-verbose="hammers"
description="Sets item policy to the specified one."
permissions="UP_HAMMER"
state-scope="SS_LOBBY"
/>
</command>

<command name="version"
usage="/version"
permissions="UP_EVERYONE | PE_ALLOW_ANYONE"
Expand Down
61 changes: 59 additions & 2 deletions src/items/flyable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,11 +553,68 @@ bool Flyable::hit(AbstractKart *kart_hit, PhysicalObject* object)
if (!m_has_server_state || hasAnimation())
return false;
// the owner of this flyable should not be hit by his own flyable
if(isOwnerImmunity(kart_hit)) return false;
if(isOwnerImmunity(kart_hit))
return false;

m_has_hit_something=true;
if (kart_hit == NULL)
return true;

ItemPolicy *policy = RaceManager::get()->getItemPolicy();
int leader_section_idx = policy->m_leader_section;
// If leader is not in a valid section, allow the hit
if (leader_section_idx <= -1)
return true;
// If blue flags are not enabled, ALSO allow the hit
if (!(policy->m_policy_sections[leader_section_idx].m_rules & ItemPolicyRules::IPT_BLUE_FLAGS))
return true;

// Now, if blue flags are enabled and the leader is in a valid section, that's when we start to question if the hit could be forbidden (a lapper illegally hitting a lapping or vice versa).
LinearWorld *lin_world = dynamic_cast<LinearWorld*>(World::getWorld());
float track_length = Track::getCurrentTrack()->getTrackLength();
float sender_distance = std::fmod(lin_world->getOverallDistance(m_owner->getWorldKartId()), track_length);
float recv_distance = std::fmod(lin_world->getOverallDistance(kart_hit->getWorldKartId()), track_length);

return true;
int sender_lap = lin_world->getFinishedLapsOfKart(m_owner->getWorldKartId());
int recv_lap = lin_world->getFinishedLapsOfKart(kart_hit->getWorldKartId());

float minimum_distance_empirical = 200.0f;

float distance_normal = std::fabs(sender_distance - recv_distance);
float distance_complimentary = track_length - distance_normal;

bool across_finish_line;
bool forwards_throw;
if (distance_complimentary < distance_normal) {
across_finish_line = true;
if (sender_distance > recv_distance)
forwards_throw = true;
else
forwards_throw = false;
} else {
across_finish_line = false;
}

// if the distance is less than 5% from half the track length,
// it is nonsense to try to predict if the hit is across the finish line
if (distance_normal/track_length > 0.45 && distance_normal/track_length < 0.55)
across_finish_line = false;

// for too short tracks we instead take 1/5th of the track
if (track_length < 750.0f)
minimum_distance_empirical = track_length/5.0f;

bool hit_is_valid;
// sender with a 1 lap difference whose distance is less than an empirical number are almost certainly hitting each other across the start/finish line
if (across_finish_line && forwards_throw) {
hit_is_valid = (recv_lap - sender_lap) == 1;
} else if (across_finish_line && !forwards_throw) {
hit_is_valid = (sender_lap - recv_lap) == 1;
} else {
hit_is_valid = sender_lap == recv_lap;
}

return hit_is_valid;
} // hit

// ----------------------------------------------------------------------------
Expand Down
108 changes: 88 additions & 20 deletions src/items/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,30 @@ void ItemState::initItem(ItemType type, const Vec3& xyz, const Vec3& normal)
setDisappearCounter();
} // initItem

static int getRespawnTime(ItemState::ItemType type) {
auto& stk_config = STKConfig::get();
switch (type)
{
case ItemState::ITEM_BONUS_BOX:
return stk_config->m_bonusbox_item_return_ticks;
break;
case ItemState::ITEM_NITRO_BIG:
case ItemState::ITEM_NITRO_SMALL:
return stk_config->m_nitro_item_return_ticks;
break;
case ItemState::ITEM_BANANA:
return stk_config->m_banana_item_return_ticks;
break;
case ItemState::ITEM_BUBBLEGUM:
case ItemState::ITEM_BUBBLEGUM_NOLOK:
return stk_config->m_bubblegum_item_return_ticks;
break;
default:
return stk_config->time2Ticks(2.0f);
break;
}
}

// ----------------------------------------------------------------------------
/** Update the state of the item, called once per physics frame.
* \param ticks Number of ticks to simulate. While this value is 1 when
Expand All @@ -127,6 +151,69 @@ void ItemState::update(int ticks)
m_ticks_till_return -= ticks;
} // if collected

ItemPolicy *policy = RaceManager::get()->getItemPolicy();
if (policy->m_policy_sections.size() == 0)
return;

int current_section = policy->m_leader_section;

if (current_section <= -1)
current_section = 0;

uint16_t rules_curr = policy->m_policy_sections[current_section].m_rules;
uint16_t rules_prev;
if (current_section > 0)
rules_prev = policy->m_policy_sections[current_section-1].m_rules;
else
rules_prev = rules_curr;

bool was_gum = (m_original_type==ITEM_BUBBLEGUM) || (m_type==ITEM_BUBBLEGUM_NOLOK);

bool is_nitro = (m_type==ITEM_NITRO_SMALL) || (m_type==ITEM_NITRO_BIG);
bool was_nitro = (m_original_type==ITEM_NITRO_SMALL) || (m_original_type==ITEM_NITRO_BIG);

bool forbid_prev = ((rules_prev & ItemPolicyRules::IPT_FORBID_BONUSBOX) && m_type==ITEM_BONUS_BOX) ||
((rules_prev & ItemPolicyRules::IPT_FORBID_BANANA) && m_type==ITEM_BANANA) ||
((rules_prev & ItemPolicyRules::IPT_FORBID_NITRO) && (is_nitro || was_nitro));

bool forbid_curr = ((rules_curr & ItemPolicyRules::IPT_FORBID_BONUSBOX) && m_type==ITEM_BONUS_BOX) ||
((rules_curr & ItemPolicyRules::IPT_FORBID_BANANA) && m_type==ITEM_BANANA) ||
((rules_curr & ItemPolicyRules::IPT_FORBID_NITRO) && (is_nitro || was_nitro));


// Gums that were switched into nitro are NEVER forbidden
bool instant = false;
if (was_gum && is_nitro) {
instant = true;
} else {
instant = false;
}

auto& stk_config = STKConfig::get();
// There's redundant cases here, but it is like this for maintainability
if (forbid_prev && forbid_curr)
m_ticks_till_return = stk_config->time2Ticks(99999);
else if (!forbid_prev && forbid_curr)
m_ticks_till_return = stk_config->time2Ticks(99999);
else if (forbid_prev && !forbid_curr) {
int respawn_ticks = getRespawnTime(m_type);
// If the ticks till return are abnormally high, set them back to normal.
// If we don't do it like this, it will set the ticks till return perpetually
// when transitioning from a section without to a section with this item type allowed.
if (m_ticks_till_return > 10*respawn_ticks)
m_ticks_till_return = respawn_ticks;
}
else if (!forbid_prev && !forbid_curr) {
// Nothing to do
// This wouldn't be needed normally, but we do it in case of switched items
int respawn_ticks = getRespawnTime(m_type);
if (m_ticks_till_return > 10*respawn_ticks && m_type != ITEM_EASTER_EGG)
m_ticks_till_return = respawn_ticks;
}
if (instant) {
m_ticks_till_return = 0;
}

} // update

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -154,26 +241,7 @@ void ItemState::collected(const AbstractKart *kart)
}
else
{
switch (m_type)
{
case ITEM_BONUS_BOX:
m_ticks_till_return = stk_config->m_bonusbox_item_return_ticks;
break;
case ITEM_NITRO_BIG:
case ITEM_NITRO_SMALL:
m_ticks_till_return = stk_config->m_nitro_item_return_ticks;
break;
case ITEM_BANANA:
m_ticks_till_return = stk_config->m_banana_item_return_ticks;
break;
case ITEM_BUBBLEGUM:
case ITEM_BUBBLEGUM_NOLOK:
m_ticks_till_return = stk_config->m_bubblegum_item_return_ticks;
break;
default:
m_ticks_till_return = stk_config->time2Ticks(2.0f);
break;
}
m_ticks_till_return = getRespawnTime(m_type);
}

if (RaceManager::get()->isBattleMode())
Expand Down
20 changes: 17 additions & 3 deletions src/items/powerup_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,22 +101,36 @@ void PowerupManager::unloadPowerups()
* \return The type, or POWERUP_NOTHING if the name is not found
*/
PowerupManager::PowerupType
PowerupManager::getPowerupType(const std::string &name) const
{
PowerupManager::getPowerupType(const std::string &name) {
// Must match the order of PowerupType in powerup_manager.hpp!!
static const std::string powerup_names[] = {
"", /* Nothing */
"nothing", /* Nothing */
"bubblegum", "cake", "bowling", "zipper", "plunger", "switch",
"swatter", "rubber-ball", "parachute", "anchor"
};

for(unsigned int i=POWERUP_FIRST; i<=POWERUP_LAST; i++)
{
if(name=="") return POWERUP_NOTHING;
if(powerup_names[i]==name) return(PowerupType)i;
}
return POWERUP_NOTHING;
} // getPowerupType

std::string
PowerupManager::getPowerupAsString(PowerupManager::PowerupType type) {
// Must match the order of PowerupType in powerup_manager.hpp!!
static const std::string powerup_names[] = {
"nothing", /* Nothing */
"bubblegum", "cake", "bowling", "zipper", "plunger", "switch",
"swatter", "rubber-ball", "parachute", "anchor"
};
int size = sizeof(powerup_names)/sizeof(*powerup_names);
if (type == POWERUP_NOTHING) return "nothing";
if (size < type-POWERUP_FIRST) return "nothing";
return powerup_names[type-POWERUP_FIRST+1];
} // getPowerupAsString

//-----------------------------------------------------------------------------
/** Loads powerups models and icons from the powerup.xml file.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/items/powerup_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,15 @@ class PowerupManager : public NoCopy
/** The weight distribution to be used for the current race. */
WeightsData m_current_item_weights;

PowerupType getPowerupType(const std::string &name) const;

/** Seed for random powerup, for local game it will use a random number,
* for network games it will use the start time from server. */
std::atomic<uint64_t> m_random_seed;

std::string m_config_file;

public:
static PowerupType getPowerupType(const std::string &name);
static std::string getPowerupAsString(PowerupType type);
static void unitTesting();

PowerupManager ();
Expand Down
2 changes: 2 additions & 0 deletions src/karts/abstract_kart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ AbstractKart::AbstractKart(const std::string& ident,
std::shared_ptr<GE::GERenderInfo> ri)
: Moveable()
{
item_amount_last_lap = 0;
item_type_last_lap = PowerupManager::POWERUP_NOTHING;
m_world_kart_id = world_kart_id;
if (RaceManager::get()->getKartGlobalPlayerId(m_world_kart_id) > -1)
{
Expand Down
7 changes: 6 additions & 1 deletion src/karts/abstract_kart.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ class AbstractKart : public Moveable
std::shared_ptr<GE::GERenderInfo> ri);
virtual ~AbstractKart();
// ------------------------------------------------------------------------

// amount in previous lap
int item_amount_last_lap;
PowerupManager::PowerupType item_type_last_lap;

/** Returns a name to be displayed for this kart. */
const core::stringw& getName() const { return m_name; }
// ------------------------------------------------------------------------
Expand Down Expand Up @@ -358,7 +363,7 @@ class AbstractKart : public Moveable
* \param max_speed_fraction Fraction of top speed to allow only.
* \param fade_in_time How long till maximum speed is capped. */
virtual void setSlowdown(unsigned int category, float max_speed_fraction,
int fade_in_time) = 0;
int fade_in_time, int duration=-1) = 0;
// ------------------------------------------------------------------------
/** Returns the remaining collected energy. */
virtual float getEnergy() const = 0;
Expand Down
37 changes: 35 additions & 2 deletions src/karts/kart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,9 @@ void Kart::instantSpeedIncrease(unsigned int category, float add_max_speed,

// -----------------------------------------------------------------------------
void Kart::setSlowdown(unsigned int category, float max_speed_fraction,
int fade_in_time)
int fade_in_time, int duration)
{
m_max_speed->setSlowdown(category, max_speed_fraction, fade_in_time);
m_max_speed->setSlowdown(category, max_speed_fraction, fade_in_time, duration);
} // setSlowdown

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -2671,6 +2671,39 @@ void Kart::updatePhysics(int ticks)

// Cap speed if necessary
const Material *m = getMaterial();
auto& stk_config = STKConfig::get();


ItemPolicy *itempolicy = RaceManager::get()->getItemPolicy();
bool is_restart = itempolicy->m_virtualpace_code <= -3;
bool did_restart = false;
if (is_restart) {
// Reaffirm the penalty in case someone tried to be funny and pit for tyres in the middle of a safety car restart
m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_BUBBLE, 0.1f, stk_config->time2Ticks(0.1f), -1);
int restart_time = -(itempolicy->m_virtualpace_code + 3);
float gap = itempolicy->m_policy_sections[itempolicy->m_leader_section].m_virtualpace_gaps;
gap *= getPosition();
restart_time += gap;
int current_time = World::getWorld()->getTime();
if (current_time > restart_time) {
// Set slowdown time to 0 (disable it) if its time to restart
m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_BUBBLE, 0.1f, stk_config->time2Ticks(0.1f), stk_config->time2Ticks(0));
did_restart = true;
}
}

bool is_last = getPosition() == RaceManager::get()->getNumberOfKarts();

if (is_last && did_restart) {
itempolicy->m_virtualpace_code = -1;
itempolicy->m_restart_count = -1;
}

// the only reason such a ridiculous infinite pit penalty (-1) can be given is if it's a virtual pace car restart
// plainly, the only reason this exists is because first place won't get its penalty overturned if for some reason
if (itempolicy->m_virtualpace_code == -1 && m_max_speed->getSpeedDecreaseTicksLeft(MaxSpeed::MS_DECREASE_BUBBLE) == -1) {
m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_BUBBLE, 0.1f, stk_config->time2Ticks(0.1f), stk_config->time2Ticks(0));
}

float min_speed = m && m->isZipper() ? m->getZipperMinSpeed() : -1.0f;
m_max_speed->setMinSpeed(min_speed);
Expand Down
2 changes: 1 addition & 1 deletion src/karts/kart.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ class Kart : public AbstractKart
int duration, int fade_out_time) OVERRIDE;
// ----------------------------------------------------------------------------------------
virtual void setSlowdown(unsigned int category, float max_speed_fraction,
int fade_in_time) OVERRIDE;
int fade_in_time, int duration=-1) OVERRIDE;
// ----------------------------------------------------------------------------------------
virtual int getSpeedIncreaseTicksLeft(unsigned int category) const OVERRIDE;
// ----------------------------------------------------------------------------------------
Expand Down
9 changes: 9 additions & 0 deletions src/karts/max_speed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,15 @@ int MaxSpeed::getSpeedIncreaseTicksLeft(unsigned int category)
return m_speed_increase[category].getTimeLeft();
} // getSpeedIncreaseTimeLeft

// ----------------------------------------------------------------------------
/** Returns how much decreased speed time is left over in the given category.
* \param category Which category to report on.
*/
int MaxSpeed::getSpeedDecreaseTicksLeft(unsigned int category)
{
return m_speed_decrease[category].getTimeLeft();
} // getSpeedIncreaseTimeLeft

// ----------------------------------------------------------------------------
/** Returns if increased speed is active in the given category.
* \param category Which category to report on.
Expand Down
1 change: 1 addition & 0 deletions src/karts/max_speed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ friend class KartRewinder;
void setSlowdown(unsigned int category, float max_speed_fraction,
int fade_in_time, int duration=-1);
int getSpeedIncreaseTicksLeft(unsigned int category);
int getSpeedDecreaseTicksLeft(unsigned int category);
int isSpeedIncreaseActive(unsigned int category);
int isSpeedDecreaseActive(unsigned int category);
void update(int ticks);
Expand Down
Loading
Loading