Skip to content
Draft
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ add_executable(alicia-server
src/server/chat/AllChatDirector.cpp
src/server/chat/PrivateChatDirector.cpp
src/server/messenger/MessengerDirector.cpp
src/server/race/MagicSystem.cpp
src/server/race/RaceInstance.cpp
src/server/race/RaceNetworkHandler.cpp
src/server/ranch/RanchDirector.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1542,7 +1542,7 @@ struct AcCmdUserRaceUpdatePos
//! 1 = In the air
uint16_t member5{};
//! Race track progress
float member6{};
float progress{};
//! Ticks since connected to race director?
uint32_t member7{};

Expand Down
12 changes: 12 additions & 0 deletions include/libserver/registry/MagicRegistry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#ifndef MAGICREGISTRY_HPP
#define MAGICREGISTRY_HPP

#include <array>
#include <cstdint>
#include <filesystem>
#include <unordered_map>
Expand All @@ -30,6 +31,8 @@ namespace server::registry

struct Magic
{
using SlotWeight = uint32_t;

//! Per-magic-slot definition (MagicSlotInfo).
struct SlotInfo
{
Expand Down Expand Up @@ -67,6 +70,8 @@ struct Magic

uint32_t affectByCriticalAura{};
uint32_t criticalByDarkFire{};
//! Weights for each position on the scoreboard.
std::array<SlotWeight, 8> positionalWeights{};
};

//! Magic gauge (SP) regen settings for Magic mode.
Expand Down Expand Up @@ -122,6 +127,10 @@ class MagicRegistry
//! Returns the stat scaling for a basicType, or nullptr if none applies.
[[nodiscard]] const Magic::StatScaling* GetStatScaling(uint32_t basicType) const;

//! Returns the position weights for use in random magic selection.
[[nodiscard]] const std::vector<std::pair<Magic::SlotWeight, Magic::SlotInfo>>& GetSoloPositionWeights(uint32_t position) const;
[[nodiscard]] const std::vector<std::pair<Magic::SlotWeight, Magic::SlotInfo>>& GetTeamPositionWeights(uint32_t position) const;

private:
std::unordered_map<uint32_t, Magic::SlotInfo> _slotInfo{};
std::vector<uint32_t> _soloPool{};
Expand All @@ -130,6 +139,9 @@ class MagicRegistry
uint32_t _baseCritChanceBp{500};
//! Keyed by basicType.
std::unordered_map<uint32_t, Magic::StatScaling> _statScalings{};
//! Position weights for use in random magic selection.
std::array<std::vector<std::pair<Magic::SlotWeight, Magic::SlotInfo>>, 8> _soloPositionWeights;
std::array<std::vector<std::pair<Magic::SlotWeight, Magic::SlotInfo>>, 8> _teamPositionWeights;
};

} // namespace server::registry
Expand Down
54 changes: 54 additions & 0 deletions include/server/race/MagicSystem.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Alicia Server - dedicated server software
* Copyright (C) 2026 Story Of Alicia
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
**/

#ifndef MAGICSYSTEM_HPP
#define MAGICSYSTEM_HPP

#include "server/tracker/RaceTracker.hpp"

#include <libserver/data/DataDefinitions.hpp>
#include <libserver/registry/MagicRegistry.hpp>

#include <cstdint>

namespace server::race
{

class MagicSystem
{
public:
static uint32_t GetMountStatValue(
const tracker::RaceTracker::Racer::MountStatsSnapshot& stats,
registry::Magic::MountStat which);

//! Function to select a random item based on position weights
static const registry::Magic::SlotInfo& SelectMagicTypeByPosition(
const registry::MagicRegistry& magicRegistry,
uint32_t position,
bool isTeam);

static const registry::Magic::SlotInfo& RandomMagicItem(
const registry::MagicRegistry& magicRegistry,
tracker::RaceTracker& tracker,
data::Uid racerUid);
};

} // namespace server::race

#endif // MAGICSYSTEM_HPP
6 changes: 5 additions & 1 deletion include/server/tracker/RaceTracker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,15 @@ class RaceTracker
Oid oid{InvalidEntityOid};
State state{State::Disconnected};
Team team{Team::Solo};
protocol::Vector3 position{};
//! The racer's position in the world, as a vector.
protocol::Vector3 worldPosition{};
uint32_t starPointValue{};
uint32_t jumpComboValue{};
uint32_t courseTime{InvalidCourseTime};
std::optional<uint32_t> magicItem{};
//! The racer's progress on the race track.
//! Normalised by the client to: 0.0f <= x <= 1.0f
float raceProgress{};

//! A set of tracked items in racer's proximity.
std::unordered_set<Oid> trackedDecks;
Expand Down
12 changes: 12 additions & 0 deletions resources/config/game/magic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ magic:
affectByCriticalAura: 1
criticalByDarkFire: 1
attackRank: 2
positionalWeights: [1, 45, 45, 45, 30, 20, 3, 3]

- type: 3 # FireBall (Critical)
basicType: 2
Expand Down Expand Up @@ -90,6 +91,7 @@ magic:
massEffect: 0
affectByCriticalAura: 1
criticalByDarkFire: 0
positionalWeights: [103, 10, 10, 5, 4, 3, 3, 3]

- type: 5 # WaterShield (Critical)
basicType: 4
Expand Down Expand Up @@ -148,6 +150,7 @@ magic:
massEffect: 0
affectByCriticalAura: 1
criticalByDarkFire: 0
positionalWeights: [0, 10, 15, 18, 45, 55, 105, 105]

- type: 7 # Booster (Critical)
basicType: 6
Expand Down Expand Up @@ -206,6 +209,7 @@ magic:
massEffect: 0
affectByCriticalAura: 1
criticalByDarkFire: 0
positionalWeights: [0, 0, 0, 0, 5, 20, 50, 50]

- type: 9 # HotRodding (Critical)
basicType: 8
Expand Down Expand Up @@ -265,6 +269,7 @@ magic:
affectByCriticalAura: 1
criticalByDarkFire: 0
attackRank: 1
positionalWeights: [89, 35, 20, 20, 10, 12, 3, 3]

- type: 11 # IceWall (Critical)
basicType: 10
Expand Down Expand Up @@ -324,6 +329,7 @@ magic:
massEffect: 0
affectByCriticalAura: 1
criticalByDarkFire: 0
positionalWeights: [0, 5, 20, 20, 30, 20, 3, 3]

- type: 13 # JumpStun (Critical)
basicType: 12
Expand Down Expand Up @@ -382,6 +388,7 @@ magic:
massEffect: 0
affectByCriticalAura: 1
criticalByDarkFire: 0
positionalWeights: [5, 31, 20, 20, 17, 22, 8, 8]

- type: 15 # DarkFire (Critical)
basicType: 14
Expand Down Expand Up @@ -441,6 +448,7 @@ magic:
affectByCriticalAura: 1
criticalByDarkFire: 0
attackRank: 2
positionalWeights: [1, 45, 45, 50, 35, 20, 3, 3]

- type: 17 # Summon (Critical)
basicType: 16
Expand Down Expand Up @@ -501,6 +509,7 @@ magic:
affectByCriticalAura: 1
criticalByDarkFire: 1
attackRank: 3
positionalWeights: [0, 3, 3, 3, 4, 4, 4, 4]

- type: 19 # Lightning (Critical)
basicType: 18
Expand Down Expand Up @@ -560,6 +569,7 @@ magic:
massEffect: 0
affectByCriticalAura: 1
criticalByDarkFire: 0
positionalWeights: [1, 6, 10, 7, 8, 8, 4, 4]

- type: 21 # BufPower (Critical)
basicType: 20
Expand Down Expand Up @@ -618,6 +628,7 @@ magic:
massEffect: 0
affectByCriticalAura: 0
criticalByDarkFire: 0
positionalWeights: [0, 0, 0, 0, 0, 0, 0, 0]

- type: 23 # BufGauge (Critical)
basicType: 22
Expand Down Expand Up @@ -676,6 +687,7 @@ magic:
massEffect: 0
affectByCriticalAura: 1
criticalByDarkFire: 0
positionalWeights: [0, 10, 12, 12, 12, 16, 14, 14]

- type: 25 # BufSpeed (Critical)
basicType: 24
Expand Down
2 changes: 1 addition & 1 deletion src/libserver/network/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace server::network
namespace
{

constexpr std::size_t MaxConnectionsPerAddress = 3;
constexpr std::size_t MaxConnectionsPerAddress = 10;
constexpr std::size_t MaxTotalConnections = 1024;
constexpr std::size_t MaxConnectRatePerAddress = 10;
constexpr auto RateWindow = std::chrono::seconds(30);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1105,7 +1105,7 @@ void AcCmdUserRaceUpdatePos::Read(

stream.Read(command.member4)
.Read(command.member5)
.Read(command.member6)
.Read(command.progress)
.Read(command.member7);
}

Expand Down
36 changes: 35 additions & 1 deletion src/libserver/registry/MagicRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ uint32_t ReadSlotInfo(const YAML::Node& section, Magic::SlotInfo& slot)
slot.criticalByDarkFire = section["criticalByDarkFire"].as<uint32_t>();
slot.attackRank = section["attackRank"].as<uint32_t>(0);

// Crit items do not need the positional weights defined, base items carry that info
if (slot.type == slot.basicType)
slot.positionalWeights = section["positionalWeights"].as<std::array<uint32_t, 8>>();

return slot.type;
}

Expand Down Expand Up @@ -98,14 +102,34 @@ void MagicRegistry::ReadConfig(const std::filesystem::path& configPath)
}
}

// Pre-build the pick pools so RandomMagicItem never has to filter at runtime.
// Pre-build the pick pools and positional weights so RandomMagicItem never has to filter at runtime.
for (const auto& [type, slot] : _slotInfo)
{
if (slot.basicType != type)
continue; // skip critical variants
_teamPool.push_back(type);
if (slot.teamMode == 0)
_soloPool.push_back(type);

// Only compile weights from base type
if (slot.type != slot.basicType)
continue;

for (size_t i = 0; i < slot.positionalWeights.size(); ++i)
{
const auto& pair = std::make_pair(
slot.positionalWeights[i],
slot);

// Add to team magic item weights since team is a superset of solo
_teamPositionWeights[i].emplace_back(pair);

// Do not add to solo position weights if team item
if (slot.teamMode != 0)
continue;

_soloPositionWeights[i].emplace_back(pair);
}
}

if (const auto regenSection = magicSection["regen"])
Expand Down Expand Up @@ -202,4 +226,14 @@ const Magic::StatScaling* MagicRegistry::GetStatScaling(uint32_t basicType) cons
return it == _statScalings.cend() ? nullptr : &it->second;
}

const std::vector<std::pair<Magic::SlotWeight, Magic::SlotInfo>>& MagicRegistry::GetSoloPositionWeights(uint32_t position) const
{
return _soloPositionWeights.at(position);
}

const std::vector<std::pair<Magic::SlotWeight, Magic::SlotInfo>>& MagicRegistry::GetTeamPositionWeights(uint32_t position) const
{
return _teamPositionWeights.at(position);
}

} // namespace server::registry
Loading
Loading