Skip to content

Commit

Permalink
feat: add experimental leveled characters support, better dungeons su…
Browse files Browse the repository at this point in the history
…pport and more (#1715)
  • Loading branch information
Pospelove authored Nov 4, 2023
1 parent 072fb7b commit 9844c2b
Show file tree
Hide file tree
Showing 176 changed files with 3,470 additions and 994 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/pr-ubuntu-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,33 @@ jobs:
chown -R skymp:skymp /src /home/skymp/.cmake-js
- name: CMake Configure
id: cmake_configure
uses: addnab/docker-run-action@v3
with:
image: ${{ env.SKYMP_VCPKG_DEPS_IMAGE }}
options: |
-v ${{github.workspace}}:/src
-v ${{github.workspace}}/.cmake-js:/home/skymp/.cmake-js
-u skymp
--name configure_container
run: |
cd /src \
&& ./build.sh --configure \
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-DUNIT_DATA_DIR="/src/skyrim_data_files"
- name: Copy log file from container to workspace
if: failure()
run: |
sudo docker cp configure_container:/src/vcpkg/buildtrees/rsm-bsa/install-x64-linux-dbg-out.log ${{github.workspace}}/install-x64-linux-dbg-out.log
- name: Upload vcpkg failure logs
if: failure()
uses: actions/upload-artifact@v2
with:
name: install-x64-linux-dbg-out.log
path: ${{github.workspace}}/install-x64-linux-dbg-out.log

- name: Upload compile_commands.json
uses: actions/upload-artifact@v3
with:
Expand Down
1 change: 1 addition & 0 deletions .linelint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ ignore:
- .git/
- '**/third_party'
- 'skymp5-scripts/'
- 'overlay_ports/rsm-bsa/*.patch'

rules:
end-of-file:
Expand Down
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ endif()

set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_CURRENT_LIST_DIR}/overlay_triplets")
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/overlay_ports")
set(VCPKG_INSTALL_OPTIONS --no-print-usage --clean-after-build)

if("$ENV{CI}" STREQUAL "true")
set(VCPKG_INSTALL_OPTIONS --no-print-usage)
else()
set(VCPKG_INSTALL_OPTIONS --no-print-usage --clean-after-build)
endif()

if("$ENV{CI}" STREQUAL "true" AND WIN32)
# The same submodule but moved to a larger disk in Windows CI. See action files:
Expand Down
3 changes: 3 additions & 0 deletions cmake/link_vcpkg_dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,8 @@ function(link_vcpkg_dependencies)

find_package(OpenSSL REQUIRED)
target_link_libraries(${target} PUBLIC OpenSSL::SSL OpenSSL::Crypto)

find_package(bsa CONFIG REQUIRED)
target_link_libraries(${target} PUBLIC bsa::bsa)
endforeach()
endfunction()
18 changes: 18 additions & 0 deletions docs/docs_server_configuration_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,24 @@ Absolute paths work but aren't accessible via `uiPort`. External tooling wouldn'
}
```

## archives

Specify BSA archives that will be loaded by the server.

At this moment, used only for compiled Papyrus scripts.

Relative/absolute paths work similar to esp/esm.

```json5
{
// ...
"archives": [
"Skyrim - Misc.bsa"
]
// ...
}
```

## lang

The language, the translation of which will be obtained from the string files located in Data/strings
Expand Down
1 change: 1 addition & 0 deletions docs/release/dev/sp-added-evaluate-lvl-character.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added experimental `TESModPlatform.EvaluateLeveledNpc` native. It is unstable and shouldn't be used in user plugins. This native is required for SkyMP.
7 changes: 7 additions & 0 deletions libespm/include/libespm/ACHR.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "REFR.h"
#include "RecordHeader.h"

#pragma pack(push, 1)
Expand All @@ -11,6 +12,12 @@ class ACHR final : public RecordHeader
static constexpr auto kType = "ACHR";

bool StartsDead() const noexcept;

struct Data : public REFR::Data
{
};

Data GetData(CompressedFieldsCache& compressedFieldsCache) const noexcept;
};

static_assert(sizeof(ACHR) == sizeof(RecordHeader));
Expand Down
37 changes: 2 additions & 35 deletions libespm/include/libespm/LVLI.h
Original file line number Diff line number Diff line change
@@ -1,50 +1,17 @@
#pragma once
#include "RecordHeader.h"
#include "LeveledListBase.h"

#pragma pack(push, 1)

namespace espm {

class LVLI final : public RecordHeader
class LVLI final : public LeveledListBase
{
public:
static constexpr auto kType = "LVLI";

enum LeveledItemFlags
{
AllLevels = 0x01, //(sets it to calculate for all entries < player level,
// choosing randomly from all the entries under)
Each = 0x02, // (sets it to repeat a check every time the list is called
// (if it's called multiple times), otherwise it will use the
// same result for all counts.)
UseAll = 0x04, // (use all entries when the list is called)
SpecialLoot = 0x08,
};

struct Entry
{
char type[4] = { 'L', 'V', 'L', 'O' };
uint16_t dataSize = 0;
uint32_t level = 0;
uint32_t formId = 0;
uint32_t count = 0;
};

struct Data
{
const char* editorId = "";
uint8_t chanceNone = 0;
uint8_t leveledItemFlags = 0;
uint32_t chanceNoneGlobalId = 0;
uint8_t numEntries = 0;
const Entry* entries = nullptr;
};

Data GetData(CompressedFieldsCache& compressedFieldsCache) const noexcept;
};

static_assert(sizeof(LVLI) == sizeof(RecordHeader));
static_assert(sizeof(LVLI::Entry) == 18);

}

Expand Down
18 changes: 18 additions & 0 deletions libespm/include/libespm/LVLN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once
#include "LeveledListBase.h"

#pragma pack(push, 1)

namespace espm {

class LVLN final : public LeveledListBase
{
public:
static constexpr auto kType = "LVLN";
};

static_assert(sizeof(LVLN) == sizeof(RecordHeader));

}

#pragma pack(pop)
49 changes: 49 additions & 0 deletions libespm/include/libespm/LeveledListBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma once
#include "RecordHeader.h"

#pragma pack(push, 1)

namespace espm {

class LeveledListBase : public RecordHeader
{
public:
enum LeveledItemFlags
{
AllLevels = 0x01, //(sets it to calculate for all entries < player level,
// choosing randomly from all the entries under)
Each = 0x02, // (sets it to repeat a check every time the list is called
// (if it's called multiple times), otherwise it will use the
// same result for all counts.)
UseAll = 0x04, // (use all entries when the list is called)
SpecialLoot = 0x08,
};

struct Entry
{
char type[4] = { 'L', 'V', 'L', 'O' };
uint16_t dataSize = 0;
uint32_t level = 0;
uint32_t formId = 0;
uint32_t count = 0;
};

struct Data
{
const char* editorId = "";
uint8_t chanceNone = 0;
uint8_t leveledItemFlags = 0;
uint32_t chanceNoneGlobalId = 0;
uint8_t numEntries = 0;
const Entry* entries = nullptr;
};

Data GetData(CompressedFieldsCache& compressedFieldsCache) const noexcept;
};

static_assert(sizeof(LeveledListBase) == sizeof(RecordHeader));
static_assert(sizeof(LeveledListBase::Entry) == 18);

}

#pragma pack(pop)
41 changes: 41 additions & 0 deletions libespm/include/libespm/NPC_.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,44 @@ class NPC_ final : public RecordHeader
int8_t rank = 0;
};

enum TemplateFlags : uint16_t
{
// (Destructible Object; Traits tab, including race, gender, height,
// weight, voice type, death item; Sounds tab; Animation tab; Character Gen
// tabs)
UseTraits = 0x01,
// (Stats tab, including level, autocalc, skills, health/magicka/stamina,
// speed, bleedout, class)
UseStats = 0x02,
// (both factions and assigned crime faction)
UseFactions = 0x04,
// (both spells and perks)
UseSpelllist = 0x08,
// (AI Data tab, including aggression/confidence/morality, combat style and
// gift filter)
UseAIData = 0x10,
// (only the basic Packages listed on the AI Packages tab; rest of tab
// controlled by Def Pack List)
UseAIPackages = 0x20,
// Unused?
Unused = 0x40,
// (including name and short name, and flags for Essential, Protected,
// Respawn, Summonable, Simple Actor, and Doesn't affect stealth meter)
UseBaseData = 0x80,
// (Inventory tab, including all outfits and geared-up item -- but not
// death item)
UseInventory = 0x100,
// Scripts
UseScript = 0x200,
// (the dropdown-selected package lists on the AI Packages tab)
UseDefPackList = 0x400,
// (Attack Data tab, including override from behavior graph race, events,
// and data)
UseAttackData = 0x800,
// Keywords
UseKeywords = 0x1000
};

struct Data
{
uint32_t defaultOutfitId = 0;
Expand All @@ -30,13 +68,16 @@ class NPC_ final : public RecordHeader
std::vector<Faction> factions;

bool isEssential = false;
bool isUnique = false;
bool isProtected = false;

uint32_t race = 0;
int16_t healthOffset = 0;
int16_t magickaOffset = 0;
int16_t staminaOffset = 0;
ObjectBounds objectBounds = {};
uint32_t baseTemplate = 0;
uint16_t templateDataFlags = 0;
};

Data GetData(CompressedFieldsCache& compressedFieldsCache) const noexcept;
Expand Down
12 changes: 11 additions & 1 deletion libespm/include/libespm/REFR.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace espm {

class REFR final : public RecordHeader
class REFR : public RecordHeader
{
public:
static constexpr auto kType = "REFR";
Expand All @@ -23,6 +23,12 @@ class REFR final : public RecordHeader
float rotRadians[3];
};

struct ActivationParentInfo
{
uint32_t refrId = 0;
float delay = 0.f;
};

struct Data
{
uint32_t baseId = 0;
Expand All @@ -31,6 +37,10 @@ class REFR final : public RecordHeader
const DoorTeleport* teleport = nullptr;
const float* boundsDiv2 = nullptr;
uint32_t count = 0;
uint8_t isParentActivationOnly = 0;
std::vector<ActivationParentInfo> activationParents;
uint32_t linkedRefKeywordId = 0;
uint32_t linkedRefId = 0;
};

Data GetData(CompressedFieldsCache& compressedFieldsCache) const noexcept;
Expand Down
1 change: 1 addition & 0 deletions libespm/include/libespm/Records.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "KYWD.h"
#include "LIGH.h"
#include "LVLI.h"
#include "LVLN.h"
#include "MGEF.h"
#include "NAVM.h"
#include "NPC_.h"
Expand Down
12 changes: 12 additions & 0 deletions libespm/src/ACHR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,16 @@ bool ACHR::StartsDead() const noexcept
return this->flags & 0x200;
}

ACHR::Data ACHR::GetData(
CompressedFieldsCache& compressedFieldsCache) const noexcept
{
REFR::Data data =
reinterpret_cast<const espm::REFR*>(this)->GetData(compressedFieldsCache);

ACHR::Data res;
static_cast<REFR::Data&>(res) = data;

return res;
}

}
4 changes: 2 additions & 2 deletions libespm/src/LVLI.cpp → libespm/src/LeveledListBase.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include "libespm/LVLI.h"
#include "libespm/LeveledListBase.h"
#include "libespm/RecordHeaderAccess.h"
#include <cstring>

namespace espm {

LVLI::Data LVLI::GetData(
LeveledListBase::Data LeveledListBase::GetData(
CompressedFieldsCache& compressedFieldsCache) const noexcept
{
Data result;
Expand Down
7 changes: 6 additions & 1 deletion libespm/src/NPC_.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ NPC_::Data NPC_::GetData(
} else if (!std::memcmp(type, "ACBS", 4)) {
const uint32_t flags = *reinterpret_cast<const uint32_t*>(data);

result.isEssential = !!(flags & 0x02);
result.isEssential = !!(flags & 0x2);
result.isUnique = !!(flags & 0x20);
result.isProtected = !!(flags & 0x800);
result.magickaOffset = *reinterpret_cast<const int16_t*>(data + 4);
result.staminaOffset = *reinterpret_cast<const int16_t*>(data + 6);
result.healthOffset = *reinterpret_cast<const int16_t*>(data + 20);
result.templateDataFlags =
*reinterpret_cast<const uint16_t*>(data + 18);

} else if (!std::memcmp(type, "RNAM", 4)) {
result.race = *reinterpret_cast<const uint32_t*>(data);
Expand All @@ -39,6 +42,8 @@ NPC_::Data NPC_::GetData(
}
} else if (!std::memcmp(type, "SPLO", 4)) {
result.spells.emplace(*reinterpret_cast<const uint32_t*>(data));
} else if (!std::memcmp(type, "TPLT", 4)) {
result.baseTemplate = (*reinterpret_cast<const uint32_t*>(data));
}
},
compressedFieldsCache);
Expand Down
Loading

0 comments on commit 9844c2b

Please sign in to comment.