Initial commit
parent
6bf7e9c1fe
commit
0220cedf10
|
@ -0,0 +1,2 @@
|
|||
build/
|
||||
.vscode/
|
|
@ -0,0 +1,48 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(collision_cacher)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-Os -s -m32 -static-libgcc -static-libstdc++ -static")
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
|
||||
if (NOT DEFINED ALPACABOT_DIR)
|
||||
set(ALPACABOT_DIR $ENV{USERPROFILE}\\AlpacaBot)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED ALPACABOT_INCLUDE_DIR)
|
||||
set(ALPACABOT_INCLUDE_DIR ${ALPACABOT_DIR}\\Include)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED ALPACABOT_LIB_DIR)
|
||||
set(ALPACABOT_LIB_DIR ${ALPACABOT_DIR}\\Library)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED ALPACABOT_SCRIPT_DIR)
|
||||
set(ALPACABOT_SCRIPT_DIR ${ALPACABOT_DIR}\\Script\\Local)
|
||||
endif()
|
||||
|
||||
|
||||
set(SOURCE_FILES
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${ALPACABOT_SCRIPT_DIR}/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${ALPACABOT_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_directories(${PROJECT_NAME} PRIVATE
|
||||
${ALPACABOT_LIB_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
AlpacaLibrary
|
||||
)
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,134 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Path to the CSV file
|
||||
userProfile := os.Getenv("USERPROFILE")
|
||||
filePath := filepath.Join(userProfile, "AlpacaBot", "Collision Data", "collision_data.csv")
|
||||
|
||||
// Open the CSV file
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Read the CSV file
|
||||
reader := csv.NewReader(file)
|
||||
records, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read file: %v", err)
|
||||
}
|
||||
|
||||
// Group records by plane
|
||||
dataByPlane := make(map[string][][]string)
|
||||
for _, record := range records[1:] { // Skip the header row
|
||||
plane := record[1]
|
||||
dataByPlane[plane] = append(dataByPlane[plane], record)
|
||||
}
|
||||
|
||||
for plane, records := range dataByPlane {
|
||||
// Find the min and max x, y coordinates
|
||||
minX, minY, maxX, maxY := 1<<31-1, 1<<31-1, 0, 0
|
||||
for _, record := range records {
|
||||
x, err := strconv.Atoi(record[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
y, err := strconv.Atoi(record[3])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if x < minX {
|
||||
minX = x
|
||||
}
|
||||
if y < minY {
|
||||
minY = y
|
||||
}
|
||||
if x > maxX {
|
||||
maxX = x
|
||||
}
|
||||
if y > maxY {
|
||||
maxY = y
|
||||
}
|
||||
}
|
||||
|
||||
// Create an empty image
|
||||
width, height := maxX-minX+1, maxY-minY+1
|
||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
|
||||
// Fill in the image with data
|
||||
for _, record := range records {
|
||||
x, err := strconv.Atoi(record[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
y, err := strconv.Atoi(record[3])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
flag, err := strconv.Atoi(record[4])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Determine the color based on the flag value
|
||||
var col color.Color
|
||||
switch flag {
|
||||
case 0:
|
||||
col = color.RGBA{0, 255, 0, 255}
|
||||
case 0xFFFFFF:
|
||||
col = color.RGBA{255, 0, 0, 255}
|
||||
case 0x1000000:
|
||||
col = color.RGBA{0, 0, 255, 255}
|
||||
case 0x100:
|
||||
col = color.RGBA{255, 255, 0, 255}
|
||||
case 0x20000:
|
||||
col = color.RGBA{128, 0, 128, 255}
|
||||
case 0x200000:
|
||||
col = color.RGBA{255, 128, 0, 255}
|
||||
case 0x2:
|
||||
col = color.RGBA{0, 255, 255, 255}
|
||||
case 0x8:
|
||||
col = color.RGBA{255, 0, 255, 255}
|
||||
case 0x20:
|
||||
col = color.RGBA{128, 128, 0, 255}
|
||||
case 0x80:
|
||||
col = color.RGBA{128, 128, 128, 255}
|
||||
case 0x4:
|
||||
col = color.RGBA{0, 128, 0, 255}
|
||||
case 0x10:
|
||||
col = color.RGBA{128, 0, 0, 255}
|
||||
case 0x40:
|
||||
col = color.RGBA{0, 0, 128, 255}
|
||||
case 0x1:
|
||||
col = color.RGBA{0, 128, 128, 255}
|
||||
default:
|
||||
col = color.RGBA{0, 0, 0, 255}
|
||||
}
|
||||
|
||||
// Set the pixel in the image
|
||||
img.Set(x-minX, height-(y-minY)-1, col)
|
||||
}
|
||||
|
||||
// Save the image to a file
|
||||
outputFileName := "collision_map_plane_" + plane + ".png"
|
||||
outputFile, err := os.Create(outputFileName)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create output file: %v", err)
|
||||
}
|
||||
defer outputFile.Close()
|
||||
|
||||
png.Encode(outputFile, img)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <Game/Core.hpp>
|
||||
|
||||
void Setup() {
|
||||
ScriptInfo Info;
|
||||
Info.Name = "Collision Cacher";
|
||||
Info.Description = "Caches collision data around the player.";
|
||||
Info.Version = "1.00";
|
||||
Info.Category = "Magic";
|
||||
Info.Author = "Warren";
|
||||
Info.UID = "UID";
|
||||
Info.ForumPage = "forum.alpacabot.org";
|
||||
SetScriptInfo(Info);
|
||||
}
|
||||
|
||||
class region_key {
|
||||
public:
|
||||
std::int32_t region;
|
||||
std::int32_t plane;
|
||||
|
||||
region_key() = default;
|
||||
|
||||
region_key(std::int32_t region, std::int32_t plane)
|
||||
: region(region), plane(plane) {}
|
||||
|
||||
bool operator==(const region_key& other) const {
|
||||
return region == other.region && plane == other.plane;
|
||||
}
|
||||
};
|
||||
|
||||
class collision {
|
||||
public:
|
||||
Tile tile;
|
||||
Pathfinding::COLLISION_FLAG flag;
|
||||
|
||||
collision(const Tile& tile, Pathfinding::COLLISION_FLAG flag) : tile(tile), flag(flag) {}
|
||||
|
||||
bool operator==(const collision& other) const {
|
||||
return tile == other.tile && flag == other.flag;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<region_key> {
|
||||
std::size_t operator()(const region_key& key) const {
|
||||
std::size_t h1 = std::hash<std::int32_t>()(key.region);
|
||||
std::size_t h2 = std::hash<std::int32_t>()(key.plane);
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<Tile> {
|
||||
std::size_t operator()(const Tile& tile) const {
|
||||
std::size_t h1 = std::hash<std::int32_t>()(tile.X);
|
||||
std::size_t h2 = std::hash<std::int32_t>()(tile.Y);
|
||||
std::size_t h3 = std::hash<std::int32_t>()(tile.Plane);
|
||||
return h1 ^ (h2 << 1) ^ (h3 << 2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<collision> {
|
||||
std::size_t operator()(const collision& collision) const {
|
||||
std::size_t h1 = std::hash<Tile>()(collision.tile);
|
||||
std::size_t h2 = std::hash<std::int32_t>()(static_cast<std::int32_t>(collision.flag));
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::unordered_map<region_key, std::unordered_set<collision>> cached_regions;
|
||||
|
||||
void cache_current_region() {
|
||||
const auto get_tile_region = [](const Tile& tile) {
|
||||
return ((std::int32_t)(((tile.X >> 6) << 8) | (tile.Y >> 6)));
|
||||
};
|
||||
|
||||
const auto to_world_tile = [](const Pathfinding::TileNode& node, std::int32_t client_x, std::int32_t client_y, std::int32_t client_plane) {
|
||||
return Tile(
|
||||
node.X + client_x,
|
||||
node.Y + client_y,
|
||||
client_plane
|
||||
);
|
||||
};
|
||||
|
||||
Pathfinding::GenerateNodes();
|
||||
const std::int32_t client_x = Internal::GetClientX();
|
||||
const std::int32_t client_y = Internal::GetClientY();
|
||||
const std::int32_t client_plane = Internal::GetClientPlane();
|
||||
|
||||
for (const auto& nodes : Pathfinding::GetNodes()) {
|
||||
for (const auto& node : nodes) {
|
||||
const auto flag = node.Flag;
|
||||
const auto world_tile = to_world_tile(node, client_x, client_y, client_plane);
|
||||
|
||||
if ((flag & Pathfinding::UNINITIALIZED) || flag == Pathfinding::UNINITIALIZED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((flag == Pathfinding::CLOSED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::int32_t current_region = get_tile_region(world_tile);
|
||||
|
||||
cached_regions[region_key(current_region, client_plane)].emplace(world_tile, static_cast<Pathfinding::COLLISION_FLAG>(flag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void paint() {
|
||||
Paint::Clear();
|
||||
std::vector<std::string> texts;
|
||||
auto local_tile = Players::GetLocal().GetTile();
|
||||
for (const auto& [key, collision_data] : cached_regions) {
|
||||
texts.emplace_back(
|
||||
"(" + std::to_string(key.region) + ", " + std::to_string(key.plane) + "): " + std::to_string(collision_data.size())
|
||||
);
|
||||
|
||||
for (const auto& collision : collision_data) {
|
||||
if (collision.tile.DistanceFrom(local_tile) > 12)
|
||||
continue;
|
||||
|
||||
if ((collision.flag & Pathfinding::BLOCKED) || (collision.flag & Pathfinding::OCCUPIED)) {
|
||||
Paint::DrawTile(collision.tile, 255, 0, 0, 255);
|
||||
} else if (collision.flag & Pathfinding::NORTH) {
|
||||
Paint::DrawTile(collision.tile, 255, 0, 255, 255);
|
||||
} else if (collision.flag & Pathfinding::EAST) {
|
||||
Paint::DrawTile(collision.tile, 0, 255, 0, 255);
|
||||
} else if (collision.flag & Pathfinding::SOUTH) {
|
||||
Paint::DrawTile(collision.tile, 0, 0, 255, 255);
|
||||
} else if (collision.flag & Pathfinding::WEST) {
|
||||
Paint::DrawTile(collision.tile, 255, 165, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Point p = Point(0, 5);
|
||||
for (const auto& text : texts) {
|
||||
p += Point(0, 20);
|
||||
Paint::DrawString(text, p, 0, 255, 0, 255);
|
||||
}
|
||||
Paint::SwapBuffer();
|
||||
}
|
||||
|
||||
void verify_and_clean() {
|
||||
// verify each tile in a region actually belongs to it, if not, remove it
|
||||
for (auto it = cached_regions.begin(); it != cached_regions.end();) {
|
||||
const auto& [key, collision_data] = *it;
|
||||
const auto get_tile_region = [](const Tile& tile) {
|
||||
return ((std::int32_t)(((tile.X >> 6) << 8) | (tile.Y >> 6)));
|
||||
};
|
||||
|
||||
for (const auto& collision : collision_data) {
|
||||
if (get_tile_region(collision.tile) != key.region) {
|
||||
it->second.erase(collision);
|
||||
}
|
||||
}
|
||||
|
||||
if (it->second.empty()) {
|
||||
it = cached_regions.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_collision_data(std::ofstream& file) {
|
||||
file << "region,plane,x,y,flag\n";
|
||||
verify_and_clean();
|
||||
for (const auto& [key, collision_data] : cached_regions) {
|
||||
for (const auto& collision : collision_data) {
|
||||
if (collision.flag == Pathfinding::OPEN)
|
||||
continue;
|
||||
|
||||
file << key.region << "," << key.plane << "," << collision.tile.X << "," << collision.tile.Y << "," << static_cast<std::int32_t>(collision.flag) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void read_collision_data(std::ifstream& file) {
|
||||
std::string line;
|
||||
std::getline(file, line); // skip csv header
|
||||
while (std::getline(file, line)) {
|
||||
std::stringstream ss(line);
|
||||
std::string token;
|
||||
std::vector<std::string> tokens;
|
||||
while (std::getline(ss, token, ',')) {
|
||||
tokens.emplace_back(token);
|
||||
}
|
||||
|
||||
const auto region = std::stoi(tokens[0]);
|
||||
const auto plane = std::stoi(tokens[1]);
|
||||
const auto x = std::stoi(tokens[2]);
|
||||
const auto y = std::stoi(tokens[3]);
|
||||
const auto flag = static_cast<Pathfinding::COLLISION_FLAG>(std::stoi(tokens[4]));
|
||||
|
||||
cached_regions[region_key(region, plane)].emplace(Tile(x, y, plane), flag);
|
||||
}
|
||||
}
|
||||
|
||||
std::string data_url() {
|
||||
char *userprofile = std::getenv("USERPROFILE");
|
||||
if (userprofile == nullptr)
|
||||
return "";
|
||||
|
||||
return std::string(userprofile) + "\\AlpacaBot\\Collision Data\\collision_data.csv";
|
||||
}
|
||||
|
||||
bool OnStart() {
|
||||
SetLoopDelay(5);
|
||||
|
||||
std::string path = data_url();
|
||||
std::ifstream file(path);
|
||||
read_collision_data(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Loop() {
|
||||
cache_current_region();
|
||||
paint();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnBreak() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnEnd() {
|
||||
char *userprofile = std::getenv("USERPROFILE");
|
||||
if (userprofile == nullptr)
|
||||
return;
|
||||
|
||||
std::string path = data_url();
|
||||
if (!std::filesystem::exists(path)) {
|
||||
std::filesystem::create_directories(std::string(userprofile) + "\\AlpacaBot\\Collision Data\\");
|
||||
}
|
||||
|
||||
std::ofstream file(path);
|
||||
write_collision_data(file);
|
||||
}
|
Loading…
Reference in New Issue