Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use nested map for SDF names to save stack space. Fix #674 #676

Merged
merged 6 commits into from
Mar 6, 2025
Merged
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
21 changes: 11 additions & 10 deletions doc/editor/Advanced.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h2>Counting and Coordinates</h2>
town is town 1, and so on. The coordinate system for Blades of Exile towns and dungeons
starts with X = 0, Y = 0 (also written (0,0) ) in the upper left.</p>

<h2>Stuff Done Flags</h2>
<h2 id="sdfs">Stuff Done Flags</h2>

<p>The single most important concept to master in scenario design is the Stuff Done Flag.
The Stuff Done Flags are numbers the game keeps track of, which are used by the game to
Expand All @@ -44,19 +44,20 @@ <h2>Stuff Done Flags</h2>

<h2>Stuff Done Flags - the Specifics</h2>

<p>But what are the Stuff Done Flags? Picture a grid of numbers, 350 wide and 50 high, all
<p>But what are the Stuff Done Flags? Picture a grid of numbers, 350 tall and 50 wide, all
of which start at 0. These 17500 numbers are your Stuff Done Flags, and they are all set
to 0 when the party starts a scenario (and are saved in the save file, which is how the
game remembers what you've done already when the save file is opened).</p>

<p>Stuff Done Flag are described by coordinates. Much as the coordinates of a spot of
terrain are given by an X and Y value, a Stuff Done Flag has coordinates too. The first
coordinate of a Stuff Done Flag is the column it is in (out of 350 columns, a number from
0 to 349), and the second coordinate of a Stuff Done Flag is the row it is in (out of 50
rows, a number from 0 to 49). For example, the taking of a shield may be attached to Stuff
Done Flag X = 112, Y = 3, also written (112,3). Later chapters often refer to the two
parts of a Stuff Done Flag (the X coordinate is the first part and the Y coordinate is the
second part). In the example, 112 is the first part, and 3 is the second part.</p>
<p>Stuff Done Flag are described by coordinates. Unlike the coordinates of a
spot of terrain (which are given by an X and Y value), SDF coordinates are given
by an R and C value. The first coordinate of a Stuff Done Flag is the row it is
in (a number from 0 to 349), and the second coordinate of a Stuff Done Flag is
the column it is in (a number from 0 to 49). For example, the taking of a shield
may be attached to Stuff Done Flag R = 112, C = 3, also written (112,3). Later
chapters often refer to the two parts of a Stuff Done Flag (the R coordinate is
the first part and the C coordinate is the second part). In the example, 112 is
the first part, and 3 is the second part.</p>

<p>All Stuff Done Flags start as 0 when the scenario is started. For every event or thing
that must be remembered, you will need to assign a Stuff Done Flag to it. When the party
Expand Down
3 changes: 3 additions & 0 deletions doc/editor/Contents.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ <h2>Table of Contents</h2>
<li><a href='Monsters.html'>Editing Monster Types</a></li>
<li><a href='Items.html'>Editing Items</a></li>
<li><a href='Advanced.html'>Advanced Topics Introduction</a></li>
<ul>
<li><a href='Advanced.html#sdfs'>Stuff Done Flags</a></li>
</ul>
<li><a href='Specials.html'>Special Encounters</a></li>
<ul>
<li><a href='Specials.html#intro'>Special Nodes</a></li>
Expand Down
4 changes: 2 additions & 2 deletions src/fileio/fileio_scen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,10 +968,10 @@ void readScenarioFromXml(ticpp::Document&& data, cScenario& scenario) {
} else if(type == "sdf") {
int row, col;
edit->GetAttribute("row", &row);
if(row < 0 || row >= scenario.sdf_names.size())
if(row < 0 || row >= SDF_ROWS)
throw xBadVal(type, "row", std::to_string(row), edit->Row(), edit->Column(), fname);
edit->GetAttribute("col", &col);
if(col < 0 || col >= scenario.sdf_names[0].size())
if(col < 0 || col >= SDF_COLUMNS)
throw xBadVal(type, "col", std::to_string(col), edit->Row(), edit->Column(), fname);
edit->GetText(&scenario.sdf_names[row][col]);
} else if(type == "graphics") {
Expand Down
3 changes: 3 additions & 0 deletions src/global.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ namespace bp = boost::process;
const int MAX_GOLD = 30000;
const int MAX_FOOD = 25000;

const int SDF_ROWS = 350;
const int SDF_COLUMNS = 50;

inline bool str_to_bool(std::string str) {
return str == "true";
}
Expand Down
11 changes: 11 additions & 0 deletions src/scenario/scenario.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,3 +605,14 @@ std::string cScenario::get_feature_flag(std::string flag) {
if(iter == this->feature_flags.end()) return "";
return iter->second;
}

std::string cScenario::get_sdf_name(int row, int col) {
if(row < 0 || row >= SDF_ROWS || col < 0 || col >= SDF_COLUMNS){
throw "Tried to access SDF name for out-of-bounds flag (" + std::to_string(row) + ", " + std::to_string(col) + ")";
}
if(sdf_names.find(row) == sdf_names.end())
return "";
if(sdf_names[row].find(col) == sdf_names[row].end())
return "";
return sdf_names[row][col];
}
8 changes: 3 additions & 5 deletions src/scenario/scenario.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ struct scenario_header_flags {

enum eContentRating {G, PG, R, NC17};

// TODO: Duplicated in party.hpp
template<typename T, size_t x, size_t y>
using array2d = std::array<std::array<T, y>, x>;

// Used for finding town entrances in the outdoors
struct town_entrance_t {
location out_sec;
Expand Down Expand Up @@ -107,7 +103,9 @@ class cScenario {
std::vector<std::string> evt_names;
std::vector<std::string> ic_names;
std::vector<std::string> itf_names;
array2d<std::string, 350, 50> sdf_names;
std::map<int, std::map<int, std::string>> sdf_names;
std::string get_sdf_name(int row, int col);

bool adjust_diff;
bool is_legacy;
fs::path scen_file; // transient
Expand Down
12 changes: 6 additions & 6 deletions src/scenedit/scen.fileio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,13 +415,13 @@ void writeScenarioToXml(ticpp::Printer&& data, cScenario& scenario) {
data.PushText(scenario.itf_names[i]);
data.CloseElement("item-typeflag");
}
for(int x = 0; x < scenario.sdf_names.size(); x++) {
for(int y = 0; y < scenario.sdf_names[x].size(); y++) {
if(scenario.sdf_names[x][y].empty()) continue;
for(int r = 0; r < SDF_ROWS; r++) {
for(int c = 0; c < SDF_COLUMNS; c++) {
if(scenario.get_sdf_name(r, c).empty()) continue;
data.OpenElement("sdf");
data.PushAttribute("row", x);
data.PushAttribute("col", y);
data.PushText(scenario.sdf_names[x][y]);
data.PushAttribute("row", r);
data.PushAttribute("col", c);
data.PushText(scenario.sdf_names[r][c]);
data.CloseElement("sdf");
}
}
Expand Down
47 changes: 25 additions & 22 deletions src/scenedit/scen.sdfpicker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ bool cStuffDonePicker::handle_scroll(std::string item_hit) {
if(item_hit == "up") {
if(viewport.y > 0) viewport.y -= rows;
} else if(item_hit == "down") {
if(viewport.y < scenario.sdf_names[0].size() - rows) viewport.y += rows;
if(viewport.y < SDF_ROWS - rows) viewport.y += rows;
} else if(item_hit == "left") {
if(viewport.x > 0) viewport.x -= cols;
} else if(item_hit == "right") {
if(viewport.x < scenario.sdf_names.size() - cols) viewport.x += cols;
if(viewport.x < SDF_COLUMNS - cols) viewport.x += cols;
}
if(viewport.x == 0) dlog["left"].hide();
else dlog["left"].show();
if(viewport.y == 0) dlog["up"].hide();
else dlog["up"].show();
if(viewport.x >= scenario.sdf_names.size() - cols) dlog["right"].hide();
if(viewport.x >= SDF_COLUMNS - cols) dlog["right"].hide();
else dlog["right"].show();
if(viewport.y >= scenario.sdf_names[0].size() - rows) dlog["down"].hide();
if(viewport.y >= SDF_ROWS - rows) dlog["down"].hide();
else dlog["down"].show();
fill_names();
if(!item_hit.empty()) select_active();
Expand Down Expand Up @@ -106,40 +106,43 @@ location cStuffDonePicker::run() {
}

void cStuffDonePicker::clamp_sdf() {
chosen_sdf.x = minmax(0, scenario.sdf_names.size() - 1, chosen_sdf.x);
chosen_sdf.y = minmax(0, scenario.sdf_names[0].size() - 1, chosen_sdf.y);
// Note: x and y in chosen_sdf are actually (c,r)
chosen_sdf.x = minmax(0, SDF_COLUMNS - 1, chosen_sdf.x);
chosen_sdf.y = minmax(0, SDF_ROWS - 1, chosen_sdf.y);
viewport.x = cols * floor(chosen_sdf.x / float(cols));
viewport.y = rows * floor(chosen_sdf.y / float(rows));
}

void cStuffDonePicker::fill_names() {
for(int x = 0; x < cols; x++) {
for(int y = 0; y < rows; y++) {
auto& field = grid->getChild("name", x, y);
for(int c = 0; c < cols; c++) {
for(int r = 0; r < rows; r++) {
auto& field = grid->getChild("name", c, r);
// Note: x and y in sdf are actually (c,r)
location sdf = viewport;
sdf.x += x;
sdf.y += y;
field.setText(scenario.sdf_names[sdf.x][sdf.y]);
if(x == 0) {
sdf.x += c;
sdf.y += r;
field.setText(scenario.get_sdf_name(sdf.y, sdf.x));
if(c == 0) {
// Add row labels
row_labels->getChild("row", 0, y).setTextToNum(sdf.y);
row_labels->getChild("row", 0, r).setTextToNum(sdf.y);
}
if(y == 0) {
if(r == 0) {
// Add column labels
col_labels->getChild("col", x, 0).setTextToNum(sdf.x);
col_labels->getChild("col", c, 0).setTextToNum(sdf.x);
}
}
}
}

void cStuffDonePicker::save_names() {
for(int x = 0; x < cols; x++) {
for(int y = 0; y < rows; y++) {
auto& field = grid->getChild("name", x, y);
for(int c = 0; c < cols; c++) {
for(int r = 0; r < rows; r++) {
auto& field = grid->getChild("name", c, r);
location sdf = viewport;
sdf.x += x;
sdf.y += y;
scenario.sdf_names[sdf.x][sdf.y] = field.getText();
sdf.x += c;
sdf.y += r;
if(!field.getText().empty())
scenario.sdf_names[sdf.y][sdf.x] = field.getText();
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/scenedit/scen.sdfpicker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class cTilemap;

class cStuffDonePicker {
cDialog dlog;
// Note: x and y in initial_sdf and chosen_sdf are (c, r), but the flag array and name map
// are indexed by [r][c]. Remember to flip the order when necessary.
location initial_sdf, chosen_sdf, viewport;
cTilemap* grid;
cTilemap* row_labels;
Expand Down
7 changes: 3 additions & 4 deletions src/universe/party.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ namespace legacy {
struct setup_save_type;
};

// TODO: Duplicated in scenario.hpp
template<typename T, size_t x, size_t y>
using array2d = std::array<std::array<T, y>, x>;
template<typename T, size_t rows, size_t cols>
using array2d = std::array<std::array<T, cols>, rows>;

struct campaign_flag_type{
array2d<unsigned char, 25, 25> idx{};
Expand Down Expand Up @@ -91,7 +90,7 @@ class cParty : public iLiving {
unsigned long age;
unsigned short gold;
unsigned short food;
array2d<unsigned char, 350, 50> stuff_done;
array2d<unsigned char, SDF_ROWS, SDF_COLUMNS> stuff_done;
// These used to be stored as magic SDFs
unsigned char hostiles_present;
bool easy_mode = false, less_wm = false;
Expand Down
Loading