mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
@@ -59,7 +59,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| Perks and traits | set_perk_image<br>set_perk_*<br>set_pyromaniac_mod<br>apply_heaveho_fix<br>set_swiftlearner_mod<br>has/set_fake_perk<br>has/set_fake_trait<br>set_selectable_perk<br>set_perkbox_title<br>show/hide_real_perks<br>perk_add_mode<br>clear_selectable_perks<br>add/remove_trait<br>seq_perk_freq | not implemented | - |
|
||||
| Virtual file system | fs_create<br>fs_copy<br>fs_find<br>fs_read/write_*<br>fs_delete<br>fs_size<br>fs_pos<br>fs_seek<br>fs_resize | 🚫 | Open an issue if you have a use case for these |
|
||||
| Combat / Knockback | set_weapon_knockback<br>set_target_knockback<br>set_attacker_knockback<br>remove_weapon_knockback<br>remove_target_knockback<br>remove_attacker_knockback | not implemented | - |
|
||||
| Maps and encounters | in_world_map<br>force_encounter{_with_flags}<br>set_map_time_multi<br>get/set_map_enter_position<br>exec_map_update_scripts<br>get/set_terrain_name<br>set_town_title<br>get/set_can_rest_on_map<br>set_rest_heal_time<br>set_rest_mode<br>set_worldmap_heal_time | implemented: in_world_map, force_encounter, force_encounter_with_flags, set_map_time_multi | - |
|
||||
| Maps and encounters | in_world_map<br>force_encounter{_with_flags}<br>set_map_time_multi<br>get/set_map_enter_position<br>exec_map_update_scripts<br>get/set_terrain_name<br>set_town_title<br>get/set_can_rest_on_map<br>set_rest_heal_time<br>set_rest_mode<br>set_worldmap_heal_time | implemented: in_world_map, force_encounter, force_encounter_with_flags, set_map_time_multi, get_terrain_name, set_terrain_name | - |
|
||||
| Maps and encounters / Worldmap | get_world_map_x/y_pos<br>set_world_map_pos | ✅ | - |
|
||||
| Audio | play_sfall_sound<br>stop_sfall_sound | ✅ | `play_sfall_sound` currently supports `.acm`, `.wav`, `.ogg` formats, and can load from `.dat` archives. `.mp3` is not yet supported. |
|
||||
| Combat / Weapons and ammo | get/set_weapon_ammo_pid<br>get/set_weapon_ammo_count | ✅ | - |
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "test_utils.h"
|
||||
#include "sfall.h"
|
||||
|
||||
procedure start begin
|
||||
variable defaultName;
|
||||
|
||||
display_msg("Testing terrain name metarules...");
|
||||
|
||||
call assertEquals("get metarule exists", metarule_exist("get_terrain_name"), 1);
|
||||
call assertEquals("set metarule exists", metarule_exist("set_terrain_name"), 1);
|
||||
|
||||
defaultName := get_terrain_name(0, 0);
|
||||
call assertNotEquals("default terrain name is not error", defaultName, "Error");
|
||||
call assertNotEquals("default terrain name is not empty", defaultName, "");
|
||||
|
||||
set_terrain_name(0, 0, "CE Terrain Override");
|
||||
call assertEquals("terrain override is returned", get_terrain_name(0, 0), "CE Terrain Override");
|
||||
|
||||
set_terrain_name(0, 0, "CE Terrain Override 2");
|
||||
call assertEquals("latest terrain override wins", get_terrain_name(0, 0), "CE Terrain Override 2");
|
||||
call assertEquals("one argument get returns error", sfall_func1("get_terrain_name", 0), -1);
|
||||
call assertEquals("invalid get returns error", get_terrain_name(-1, 0), "Error");
|
||||
call assertNotEquals("current terrain name is available", get_current_terrain_name, "Error");
|
||||
|
||||
call report_test_results("terrain_name");
|
||||
signal_close_game;
|
||||
end
|
||||
+44
-2
@@ -779,6 +779,7 @@ static void mf_get_object_ai_data(OpcodeContext& ctx);
|
||||
static void mf_get_object_data(OpcodeContext& ctx);
|
||||
static void mf_get_outline(OpcodeContext& ctx);
|
||||
static void mf_get_sfall_arg_at(OpcodeContext& ctx);
|
||||
static void mf_get_terrain_name(OpcodeContext& ctx);
|
||||
static void mf_get_text_width(OpcodeContext& ctx);
|
||||
static void mf_get_window_attribute(OpcodeContext& ctx);
|
||||
static void mf_hide_window(OpcodeContext& ctx);
|
||||
@@ -805,6 +806,7 @@ static void mf_set_flags(OpcodeContext& ctx);
|
||||
static void mf_set_iface_tag_text(OpcodeContext& ctx);
|
||||
static void mf_set_object_data(OpcodeContext& ctx);
|
||||
static void mf_set_outline(OpcodeContext& ctx);
|
||||
static void mf_set_terrain_name(OpcodeContext& ctx);
|
||||
static void mf_set_window_flag(OpcodeContext& ctx);
|
||||
static void mf_set_unique_id(OpcodeContext& ctx);
|
||||
static void mf_show_window(OpcodeContext& ctx);
|
||||
@@ -859,7 +861,7 @@ const MetaruleInfo kMetarules[] = {
|
||||
// {"get_stat_max", mf_get_stat_max, 1, 2, 0, {ARG_INT, ARG_INT}},
|
||||
// {"get_stat_min", mf_get_stat_min, 1, 2, 0, {ARG_INT, ARG_INT}},
|
||||
// {"get_string_pointer", mf_get_string_pointer, 1, 1, 0, {ARG_STRING}}, // note: deprecated; do not implement
|
||||
// {"get_terrain_name", mf_get_terrain_name, 0, 2, -1, {ARG_INT, ARG_INT}},
|
||||
{ "get_terrain_name", mf_get_terrain_name, 0, 2, -1, { ARG_INT, ARG_INT } },
|
||||
{ "get_text_width", mf_get_text_width, 1, 1, 0, { ARG_STRING } },
|
||||
{ "get_window_attribute", mf_get_window_attribute, 1, 2, -1, { ARG_INT, ARG_INT } },
|
||||
// {"has_fake_perk_npc", mf_has_fake_perk_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}},
|
||||
@@ -908,7 +910,7 @@ const MetaruleInfo kMetarules[] = {
|
||||
// {"set_rest_mode", mf_set_rest_mode, 1, 1, -1, {ARG_INT}},
|
||||
// {"set_scr_name", mf_set_scr_name, 0, 1, -1, {ARG_STRING}},
|
||||
// {"set_selectable_perk_npc", mf_set_selectable_perk_npc, 5, 5, -1, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}},
|
||||
// {"set_terrain_name", mf_set_terrain_name, 3, 3, -1, {ARG_INT, ARG_INT, ARG_STRING}},
|
||||
{ "set_terrain_name", mf_set_terrain_name, 3, 3, -1, { ARG_INT, ARG_INT, ARG_STRING } },
|
||||
// {"set_town_title", mf_set_town_title, 2, 2, -1, {ARG_INT, ARG_STRING}},
|
||||
{ "set_unique_id", mf_set_unique_id, 1, 2, -1, { ARG_OBJECT, ARG_INT } },
|
||||
// {"set_unjam_locks_time", mf_set_unjam_locks_time, 1, 1, -1, {ARG_INT}},
|
||||
@@ -1984,6 +1986,46 @@ void mf_unwield_slot(OpcodeContext& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
static const char* invalidSubtilePos = "%s() - invalid x/y coordinates for the sub-tile.";
|
||||
|
||||
static void mf_get_terrain_name(OpcodeContext& ctx)
|
||||
{
|
||||
if (ctx.numArgs() == 0) {
|
||||
ctx.setReturn(wmGetCurrentTerrainName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.numArgs() == 1) {
|
||||
ctx.printError("%s() - invalid number of arguments (%d), must be 0 or 2.", ctx.name(), ctx.numArgs());
|
||||
ctx.setReturn(ctx.metaruleInfo()->errorReturn);
|
||||
return;
|
||||
}
|
||||
|
||||
int x = ctx.arg(0).asInt();
|
||||
int y = ctx.arg(1).asInt();
|
||||
|
||||
if (!wmTerrainNameIsValidSubtile(x, y)) {
|
||||
ctx.printError(invalidSubtilePos, ctx.name());
|
||||
ctx.setReturn("Error");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.setReturn(wmGetTerrainName(x, y));
|
||||
}
|
||||
|
||||
static void mf_set_terrain_name(OpcodeContext& ctx)
|
||||
{
|
||||
int x = ctx.arg(0).asInt();
|
||||
int y = ctx.arg(1).asInt();
|
||||
|
||||
if (!wmTerrainNameIsValidSubtile(x, y)) {
|
||||
ctx.printError(invalidSubtilePos, ctx.name());
|
||||
return;
|
||||
}
|
||||
|
||||
wmSetTerrainName(x, y, ctx.stringArg(2));
|
||||
}
|
||||
|
||||
void mf_tile_by_position(OpcodeContext& ctx)
|
||||
{
|
||||
int x = ctx.arg(0).asInt();
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "animation.h"
|
||||
#include "art.h"
|
||||
@@ -472,6 +475,8 @@ void wmSetScriptWorldMapMulti(float value)
|
||||
gScriptWorldMapMulti = value;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<int, std::string>> wmTerrainNameOverrides;
|
||||
|
||||
static void wmSetFlags(int* flagsPtr, int flag, int value);
|
||||
static int wmGenDataInit();
|
||||
static int wmGenDataReset();
|
||||
@@ -822,6 +827,80 @@ static WmGenData wmGenData;
|
||||
// 0x672FB0 wmMsgFile
|
||||
static MessageList wmMsgFile;
|
||||
|
||||
static bool wmSubtileCoordsValid(int x, int y)
|
||||
{
|
||||
if (wmNumHorizontalTiles <= 0 || wmMaxTileNum <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return x >= 0
|
||||
&& x < SUBTILE_GRID_WIDTH * wmNumHorizontalTiles
|
||||
&& y >= 0
|
||||
&& y < SUBTILE_GRID_HEIGHT * (wmMaxTileNum / wmNumHorizontalTiles);
|
||||
}
|
||||
|
||||
static int wmSubtileNameOverrideKey(int x, int y)
|
||||
{
|
||||
return x + y * (wmNumHorizontalTiles * SUBTILE_GRID_WIDTH);
|
||||
}
|
||||
|
||||
static const char* wmGetTerrainNameOverride(int x, int y)
|
||||
{
|
||||
if (wmTerrainNameOverrides.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int key = wmSubtileNameOverrideKey(x, y);
|
||||
auto it = std::find_if(wmTerrainNameOverrides.crbegin(), wmTerrainNameOverrides.crend(),
|
||||
[key](const std::pair<int, std::string>& terrainNameOverride) {
|
||||
return terrainNameOverride.first == key;
|
||||
});
|
||||
|
||||
return it != wmTerrainNameOverrides.crend() ? it->second.c_str() : nullptr;
|
||||
}
|
||||
|
||||
bool wmTerrainNameIsValidSubtile(int x, int y)
|
||||
{
|
||||
return wmSubtileCoordsValid(x, y);
|
||||
}
|
||||
|
||||
void wmSetTerrainName(int x, int y, const char* name)
|
||||
{
|
||||
assert(wmSubtileCoordsValid(x, y));
|
||||
assert(name != nullptr);
|
||||
|
||||
wmTerrainNameOverrides.emplace_back(wmSubtileNameOverrideKey(x, y), name);
|
||||
}
|
||||
|
||||
const char* wmGetTerrainName(int x, int y)
|
||||
{
|
||||
assert(wmSubtileCoordsValid(x, y));
|
||||
|
||||
const char* override = wmGetTerrainNameOverride(x, y);
|
||||
if (override != nullptr) {
|
||||
return override;
|
||||
}
|
||||
|
||||
SubtileInfo* subtile;
|
||||
if (wmFindCurSubTileFromPos(x * WM_SUBTILE_SIZE, y * WM_SUBTILE_SIZE, &subtile) == -1) {
|
||||
return "Error";
|
||||
}
|
||||
|
||||
MessageListItem messageListItem;
|
||||
return getmsg(&wmMsgFile, &messageListItem, 1000 + subtile->terrain);
|
||||
}
|
||||
|
||||
const char* wmGetCurrentTerrainName()
|
||||
{
|
||||
int x = wmGenData.worldPosX / WM_SUBTILE_SIZE;
|
||||
int y = wmGenData.worldPosY / WM_SUBTILE_SIZE;
|
||||
if (!wmSubtileCoordsValid(x, y)) {
|
||||
return "Error";
|
||||
}
|
||||
|
||||
return wmGetTerrainName(x, y);
|
||||
}
|
||||
|
||||
// 0x672FB8 wmFreqValues
|
||||
static int wmFreqValues[6];
|
||||
|
||||
@@ -975,6 +1054,7 @@ static int wmGenDataInit()
|
||||
|
||||
wmForceEncounterMapId = -1;
|
||||
wmForceEncounterFlags = 0;
|
||||
wmTerrainNameOverrides.clear();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1028,6 +1108,7 @@ static int wmGenDataReset()
|
||||
|
||||
wmForceEncounterMapId = -1;
|
||||
wmForceEncounterFlags = 0;
|
||||
wmTerrainNameOverrides.clear();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1035,6 +1116,8 @@ static int wmGenDataReset()
|
||||
// 0x4BCE00 wmWorldMap_exit
|
||||
void wmWorldMap_exit()
|
||||
{
|
||||
wmTerrainNameOverrides.clear();
|
||||
|
||||
if (wmTerrainTypeList != nullptr) {
|
||||
internal_free(wmTerrainTypeList);
|
||||
wmTerrainTypeList = nullptr;
|
||||
|
||||
@@ -289,6 +289,10 @@ void wmSetPartyWorldPos(int x, int y);
|
||||
void wmCarSetCurrentArea(int area);
|
||||
void wmForceEncounter(int map, unsigned int flags);
|
||||
void wmSetScriptWorldMapMulti(float value);
|
||||
bool wmTerrainNameIsValidSubtile(int x, int y);
|
||||
void wmSetTerrainName(int x, int y, const char* name);
|
||||
const char* wmGetTerrainName(int x, int y);
|
||||
const char* wmGetCurrentTerrainName();
|
||||
int worldmapGetWindow();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
Reference in New Issue
Block a user