Skip to content
Open
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 scripts/api/all.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require("api/sector.lua")
require("api/modelData.lua")
require("api/shipTemplate.lua")
require("api/entity/spaceobject.lua")
Expand Down
2 changes: 1 addition & 1 deletion scripts/api/entity/spaceobject.lua
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ end
--- Example: entity:getSectorName()
function Entity:getSectorName()
local x, y = self:getPosition()
return getSectorName(x, y)
return Sector.getSectorName(x, y)
end
--- Deals a specific amount of a specific type of damage to this entity.
--- Requires a numeric value for the damage amount, and accepts an optional DamageInfo type.
Expand Down
117 changes: 117 additions & 0 deletions scripts/api/sector.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
-- Sector naming conventions

-- The game map is divided into 20U (20,000) square sectors.
-- By default, sectors are named with a letter (Y axis) and a number (X axis) with the origin coordinates 0,0 at the northwest corner of sector F5.
-- Access sector names and coordinates using global functions Sector.getSectorName(x, y) and Sector.sectorToXY(sector_name), or their legacy wrappers without the Sector namespacing.
--
-- Scenarios can override the naming scheme by assigning to the Sector-namespaced functions.
--
-- Override examples:
--
-- - Name sectors numerically (i.e. "2, 4" for two right and four down from origin)
--
-- Sector.getSectorName = function(x, y)
-- return string.format("%d,%d", math.floor(x / 20000), math.floor(y / 20000))
-- end
-- Sector.sectorToXY = function(name)
-- local x, y = name:match("(-?%d+),(-?%d+)")
-- if not x then return 0, 0, false end
-- return tonumber(x) * 20000, tonumber(y) * 20000, true
-- end

Sector = {}
local SECTOR_SIZE = 20000

-- Truncate float division
local function truncDiv(a, b)
if a >= 0 then
return math.floor(a / b)
else
return math.ceil(a / b)
end
end

local function truncMod(a, b)
return a - truncDiv(a, b) * b
end

--- string Sector.getSectorName(float x, float y)
--- Given x/y coordinates, return the name of the sector that contains those coordinates as a single string.
--- This function and Sector.sectorToXY() define the naming scheme for sectors. To change how sectors are named in a scenario, override these functions to translate coordinate values.
--- Sectors are always rendered in EmptyEpsilon radar views as 20U in size and square in shape. Overriding this function changes only how the sector grid labels are named.
--- Example:
--- Sector.getSectorName(0, 0) -- returns "F5" by default
function Sector.getSectorName(x, y)
local sector_x = math.floor(x / SECTOR_SIZE) + 5
local sector_y = math.floor(y / SECTOR_SIZE) + 5
local y_str
local x_str

if sector_y >= 0 then
if sector_y < 26 then
y_str = string.char(string.byte('A') + sector_y)
else
y_str = string.char(string.byte('A') - 1 + math.floor(sector_y / 26)) ..
string.char(string.byte('A') + (sector_y % 26))
end
else
y_str = string.char(string.byte('z') + truncDiv(sector_y + 1, 26))
if truncMod(sector_y, 26) == 0 then
y_str = y_str .. "a"
else
y_str = y_str .. string.char(string.byte('z') + 1 + truncMod(sector_y, 26))
end
end

x_str = tostring(sector_x)
return y_str .. x_str
end

--- float x, float y, bool is_valid Sector.sectorToXY(string sector_name)
--- Given a sector name, return the x/y coordinates for the sector's northwest (top-left) corner point.
--- This also returns a third Boolean value that returns true if the given sector name was valid, or false if not. Check the input's returned validity before applying the returned coordinate values.
--- This function and Sector.getSectorName() define the naming scheme for sectors. To change how sectors are named in a scenario, override these functions to translate coordinate values.
--- Sectors are always rendered in EmptyEpsilon radar views as 20U in size and square in shape. Overriding this function changes only how coordinates translate to labels.
--- Example:
--- Sector.sectorToXY("F5") -- returns 0, 0, true
function Sector.sectorToXY(sector_name)
if #sector_name < 2 then
return 0, 0, false
end

local x, y
local intpart

local a1 = string.sub(sector_name, 1, 1)
local a2 = string.sub(sector_name, 2, 2)

if string.byte(a1) >= string.byte('A') and string.byte(a2) >= string.byte('A') then
intpart = tonumber(string.sub(sector_name, 3))
if not intpart then
return 0, 0, false
end
if string.byte(a1) > string.byte('a') then
y = ((string.byte('z') - string.byte(a1)) * 26 + (string.byte('z') - string.byte(a2) + 6)) * -SECTOR_SIZE
else
y = ((string.byte(a1) - string.byte('A')) * 26 + (string.byte(a2) - string.byte('A') + 21)) * SECTOR_SIZE
end
else
local alpha = string.upper(a1)
intpart = tonumber(string.sub(sector_name, 2))
if not intpart then
return 0, 0, false
end
y = (string.byte(alpha) - string.byte('F')) * SECTOR_SIZE
end

x = (intpart - 5) * SECTOR_SIZE
return x, y, true
end

function getSectorName(x, y)
return Sector.getSectorName(x, y)
end

function sectorToXY(sector_name)
return Sector.sectorToXY(sector_name)
end
31 changes: 17 additions & 14 deletions src/gameGlobalInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,18 +416,21 @@ string GameGlobalInfo::getMissionTime() {

string getSectorName(glm::vec2 position)
{
constexpr float sector_size = 20000;
int sector_x = floorf(position.x / sector_size) + 5;
int sector_y = floorf(position.y / sector_size) + 5;
string y;
string x;
if (sector_y >= 0)
if (sector_y < 26)
y = string(char('A' + (sector_y)));
else
y = string(char('A' - 1 + (sector_y / 26))) + string(char('A' + (sector_y % 26)));
else
y = string(char('z' + ((sector_y + 1) / 26))) + ((sector_y % 26) == 0 ? "a" : string(char('z' + 1 + (sector_y % 26))));
x = string(sector_x);
return y + x;
if (gameGlobalInfo)
{
if (gameGlobalInfo->main_scenario_script)
{
auto res = gameGlobalInfo->main_scenario_script->call<string>("getSectorName", position.x, position.y);
if (res.isOk())
return res.value();
}
if (gameGlobalInfo->script_environment_base)
{
auto res = gameGlobalInfo->script_environment_base->call<string>("getSectorName", position.x, position.y);
if (res.isOk())
return res.value();
}
LOG(Warning, "Failed to call Lua getSectorName");
}
return "??";
}
1 change: 0 additions & 1 deletion src/gameGlobalInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,3 @@ class GameGlobalInfo : public MultiplayerObject, public Updatable
};

string getSectorName(glm::vec2 position);
glm::vec2 sectorToXY(string sectorName);
75 changes: 0 additions & 75 deletions src/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,6 @@ static void luaVictory(string faction)
engine->setGameSpeed(0.0);
}

static string luaGetSectorName(float x, float y)
{
return getSectorName({x, y});
}

static string luaGetScenarioSetting(string key)
{
if (gameGlobalInfo->scenario_settings.find(key) != gameGlobalInfo->scenario_settings.end())
Expand Down Expand Up @@ -328,60 +323,6 @@ static int luaCreateAdditionalScript(lua_State* L)
return 1;
}

static int luaSectorToXY(lua_State* L)
{
string sector = luaL_checkstring(L, 1);
constexpr float sector_size = 20000;
int x, y, intpart;

if(sector.length() < 2){
lua_pushnumber(L, 0);
lua_pushnumber(L, 0);
lua_pushboolean(L, false);
return 3;
}

// Y axis is complicated
if(sector[0] >= char('A') && sector[1] >= char('A')) {
// Case with two letters
char a1 = sector[0];
char a2 = sector[1];
try{
intpart = stoi(sector.substr(2));
} catch(const std::exception& e) {
lua_pushnumber(L, 0);
lua_pushnumber(L, 0);
lua_pushboolean(L, false);
return 3;
}
if(a1 > char('a')){
// Case with two lowercase letters (zz10) counting down towards the North
y = (((char('z') - a1) * 26) + (char('z') - a2 + 6)) * -sector_size; // 6 is the offset from F5 to zz5
}else{
// Case with two uppercase letters (AB20) counting up towards the South
y = (((a1 - char('A')) * 26) + (a2 - char('A') + 21)) * sector_size; // 21 is the offset from F5 to AA5
}
}else{
//Case with just one letter (A9/a9 - these are the same sector, as case only matters in the two-letter sectors)
char alphaPart = toupper(sector[0]);
try{
intpart = stoi(sector.substr(1));
}catch(const std::exception& e){
lua_pushnumber(L, 0);
lua_pushnumber(L, 0);
lua_pushboolean(L, false);
return 3;
}
y = (alphaPart - char('F')) * sector_size;
}
// X axis is simple
x = (intpart - 5) * sector_size; // 5 is the numeric component of the F5 origin
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushboolean(L, true);
return 3;
}

static bool luaIsInsideZone(float x, float y, sp::ecs::Entity e)
{
auto zone = e.getComponent<Zone>();
Expand Down Expand Up @@ -1238,22 +1179,6 @@ bool setupScriptEnvironment(sp::script::Environment& env)
/// (The GM can unpause the game, but the scenario with its update function is destroyed.)
/// Example: victory("Exuari") -- ends the scenario, Exuari win
env.setGlobal("victory", &luaVictory);
/// string getSectorName(float x, float y)
/// Returns the name of the sector containing the given x/y coordinates.
/// Coordinates 0,0 are the top-left ("northwest") point of sector F5.
/// See also SpaceObject:getSectorName().
/// Example: getSectorName(20000,-40000) -- returns "D6"
env.setGlobal("getSectorName", &luaGetSectorName);
/// glm::vec2 sectorToXY(string sector_name)
/// Returns the top-left ("northwest") x/y coordinates for the given sector mame.
/// If the sector name is invalid, this returns coordinates 0, 0. This function also returns a third optional Boolean value that indicates whether the sector name was valid.
/// Examples:
/// x, y = sectorToXY("F5") -- x = 0, y = 0
/// x, y = sectorToXY("A0") -- x = -100000, y = -100000
/// x, y = sectorToXY("zz-23") -- x = -560000, y = -120000
/// x, y, valid = sectorToXY("BA12") -- x = 140000, y = 940000, valid = true
/// x, y, valid = sectorToXY("FOOBAR9000") -- x = 0, y = 0, valid = false
env.setGlobal("sectorToXY", &luaSectorToXY);
/// bool isInsideZone(x, y, zone_entity)
/// Checks whether the given x/y coordinates are within the specified zone.
/// Example:
Expand Down
Loading