Merge branch 'main' into bugfix/f2-nevada-pointers
This commit is contained in:
commit
a7fa8da1cd
.clang-formatCMakeLists.txt
src
actions.ccactions.hanimation.ccanimation.hart.ccart.hcharacter_editor.cccombat.cccombat.hcombat_ai.cccombat_ai.hconfig.cccredits.cccritter.cccritter.hdbox.ccdebug.ccdelay.ccdelay.hdraw.ccelevator.ccfile_utils.ccgame.ccgame_config.ccgame_config.hgame_dialog.ccgame_mouse.ccgame_mouse.hgame_sound.ccgeometry.ccgeometry.hgraph_lib.ccgraph_lib.hinput.ccinput.hinterpreter.ccinterpreter.hinterpreter_extra.ccinventory.ccinventory.hloadsave.ccloadsave.hmain.ccmap.ccmap.h
mapper
map_func.ccmap_func.hmapper.ccmapper.hmp_proto.ccmp_proto.hmp_scrpt.ccmp_scrpt.hmp_targt.ccmp_targt.hmp_text.ccmp_text.h
memory.ccmemory.hmovie_lib.ccobject.ccobject.hparty_member.ccparty_member.hpipboy.ccpreferences.ccproto.ccproto.hqueue.ccqueue.hrandom.ccscripts.ccscripts.hsfall_arrays.ccsfall_arrays.hsfall_config.ccsfall_config.hsfall_global_scripts.ccsfall_global_scripts.hsfall_global_vars.ccsfall_global_vars.hsfall_ini.ccsfall_ini.hsfall_kb_helpers.ccsfall_kb_helpers.hsfall_lists.ccsfall_lists.hsfall_metarules.ccsfall_metarules.hsfall_opcodes.ccskill.ccsvga.ccsvga.htile.cctile.hvcr.ccwindow.ccwindow.h
|
@ -1,2 +1,3 @@
|
|||
BasedOnStyle: WebKit
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
FixNamespaceComments: true
|
||||
|
|
|
@ -4,7 +4,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
|||
|
||||
set(EXECUTABLE_NAME fallout2-ce)
|
||||
|
||||
if (APPLE)
|
||||
if(APPLE)
|
||||
if(IOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "")
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
|
||||
|
@ -20,7 +20,7 @@ set(CMAKE_CXX_STANDARD 17)
|
|||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_EXTENSIONS NO)
|
||||
|
||||
if (ANDROID)
|
||||
if(ANDROID)
|
||||
add_library(${EXECUTABLE_NAME} SHARED)
|
||||
else()
|
||||
add_executable(${EXECUTABLE_NAME} WIN32 MACOSX_BUNDLE)
|
||||
|
@ -253,6 +253,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
|||
target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/audio_engine.cc"
|
||||
"src/audio_engine.h"
|
||||
"src/delay.cc"
|
||||
"src/delay.h"
|
||||
"src/fps_limiter.cc"
|
||||
"src/fps_limiter.h"
|
||||
"src/platform_compat.cc"
|
||||
|
@ -267,10 +269,20 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
|||
"src/sfall_config.h"
|
||||
"src/sfall_global_vars.cc"
|
||||
"src/sfall_global_vars.h"
|
||||
"src/sfall_global_scripts.cc"
|
||||
"src/sfall_global_scripts.h"
|
||||
"src/sfall_ini.cc"
|
||||
"src/sfall_ini.h"
|
||||
"src/sfall_kb_helpers.cc"
|
||||
"src/sfall_kb_helpers.h"
|
||||
"src/sfall_lists.cc"
|
||||
"src/sfall_lists.h"
|
||||
"src/sfall_metarules.cc"
|
||||
"src/sfall_metarules.h"
|
||||
"src/sfall_opcodes.cc"
|
||||
"src/sfall_opcodes.h"
|
||||
"src/sfall_arrays.cc"
|
||||
"src/sfall_arrays.h"
|
||||
"src/touch.cc"
|
||||
"src/touch.h"
|
||||
)
|
||||
|
@ -297,7 +309,7 @@ if(WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if(WIN32)
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"os/windows/fallout2-ce.ico"
|
||||
"os/windows/fallout2-ce.rc"
|
||||
|
@ -352,7 +364,7 @@ add_subdirectory("third_party/fpattern")
|
|||
target_link_libraries(${EXECUTABLE_NAME} ${FPATTERN_LIBRARY})
|
||||
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${FPATTERN_INCLUDE_DIR})
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD"))
|
||||
add_subdirectory("third_party/zlib")
|
||||
add_subdirectory("third_party/sdl2")
|
||||
else()
|
||||
|
@ -377,7 +389,7 @@ if(APPLE)
|
|||
else()
|
||||
install(TARGETS ${EXECUTABLE_NAME} DESTINATION .)
|
||||
|
||||
if (CPACK_BUNDLE_APPLE_CERT_APP)
|
||||
if(CPACK_BUNDLE_APPLE_CERT_APP)
|
||||
install(CODE "
|
||||
execute_process(COMMAND codesign --deep --force --options runtime --sign \"${CPACK_BUNDLE_APPLE_CERT_APP}\" ${CMAKE_BINARY_DIR}/Fallout II Community Edition.app)
|
||||
"
|
||||
|
|
|
@ -48,6 +48,9 @@ typedef enum ScienceRepairTargetType {
|
|||
// 0x5106D0
|
||||
static bool _action_in_explode = false;
|
||||
|
||||
// 0x5106D4
|
||||
int rotation;
|
||||
|
||||
// 0x5106E0
|
||||
static const int gNormalDeathAnimations[DAMAGE_TYPE_COUNT] = {
|
||||
ANIM_DANCING_AUTOFIRE,
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
namespace fallout {
|
||||
|
||||
extern int rotation;
|
||||
|
||||
int _action_attack(Attack* attack);
|
||||
int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3);
|
||||
int _action_use_an_object(Object* a1, Object* a2);
|
||||
|
|
|
@ -1711,6 +1711,7 @@ int _make_path(Object* object, int from, int to, unsigned char* rotations, int a
|
|||
return pathfinderFindPath(object, from, to, rotations, a5, _obj_blocking_at);
|
||||
}
|
||||
|
||||
// TODO: move pathfinding into another unit
|
||||
// 0x415EFC
|
||||
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback)
|
||||
{
|
||||
|
@ -1939,18 +1940,18 @@ static int _tile_idistance(int tile1, int tile2)
|
|||
}
|
||||
|
||||
// 0x4163AC
|
||||
int _make_straight_path(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6)
|
||||
int _make_straight_path(Object* obj, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6)
|
||||
{
|
||||
return _make_straight_path_func(a1, from, to, straightPathNodeList, obstaclePtr, a6, _obj_blocking_at);
|
||||
return _make_straight_path_func(obj, from, to, straightPathNodeList, obstaclePtr, a6, _obj_blocking_at);
|
||||
}
|
||||
|
||||
// TODO: Rather complex, but understandable, needs testing.
|
||||
//
|
||||
// 0x4163C8
|
||||
int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback)
|
||||
int _make_straight_path_func(Object* obj, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback)
|
||||
{
|
||||
if (obstaclePtr != NULL) {
|
||||
Object* obstacle = callback(a1, from, a1->elevation);
|
||||
Object* obstacle = callback(obj, from, obj->elevation);
|
||||
if (obstacle != NULL) {
|
||||
if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) {
|
||||
*obstaclePtr = obstacle;
|
||||
|
@ -1961,13 +1962,13 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
|
||||
int fromX;
|
||||
int fromY;
|
||||
tileToScreenXY(from, &fromX, &fromY, a1->elevation);
|
||||
tileToScreenXY(from, &fromX, &fromY, obj->elevation);
|
||||
fromX += 16;
|
||||
fromY += 8;
|
||||
|
||||
int toX;
|
||||
int toY;
|
||||
tileToScreenXY(to, &toX, &toY, a1->elevation);
|
||||
tileToScreenXY(to, &toX, &toY, obj->elevation);
|
||||
toX += 16;
|
||||
toY += 8;
|
||||
|
||||
|
@ -2005,7 +2006,7 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
if (ddx <= ddy) {
|
||||
int middle = ddx - ddy / 2;
|
||||
while (true) {
|
||||
tile = tileFromScreenXY(tileX, tileY, a1->elevation);
|
||||
tile = tileFromScreenXY(tileX, tileY, obj->elevation);
|
||||
|
||||
v22 += 1;
|
||||
if (v22 == a6) {
|
||||
|
@ -2016,9 +2017,9 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
if (straightPathNodeList != NULL) {
|
||||
StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]);
|
||||
pathNode->tile = tile;
|
||||
pathNode->elevation = a1->elevation;
|
||||
pathNode->elevation = obj->elevation;
|
||||
|
||||
tileToScreenXY(tile, &fromX, &fromY, a1->elevation);
|
||||
tileToScreenXY(tile, &fromX, &fromY, obj->elevation);
|
||||
pathNode->x = tileX - fromX - 16;
|
||||
pathNode->y = tileY - fromY - 8;
|
||||
}
|
||||
|
@ -2044,10 +2045,10 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
|
||||
if (tile != prevTile) {
|
||||
if (obstaclePtr != NULL) {
|
||||
Object* obj = callback(a1, tile, a1->elevation);
|
||||
if (obj != NULL) {
|
||||
if (obj != *obstaclePtr && (a6 != 32 || (obj->flags & OBJECT_SHOOT_THRU) == 0)) {
|
||||
*obstaclePtr = obj;
|
||||
Object* obstacle = callback(obj, tile, obj->elevation);
|
||||
if (obstacle != NULL) {
|
||||
if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) {
|
||||
*obstaclePtr = obstacle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2058,7 +2059,7 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
} else {
|
||||
int middle = ddy - ddx / 2;
|
||||
while (true) {
|
||||
tile = tileFromScreenXY(tileX, tileY, a1->elevation);
|
||||
tile = tileFromScreenXY(tileX, tileY, obj->elevation);
|
||||
|
||||
v22 += 1;
|
||||
if (v22 == a6) {
|
||||
|
@ -2069,9 +2070,9 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
if (straightPathNodeList != NULL) {
|
||||
StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]);
|
||||
pathNode->tile = tile;
|
||||
pathNode->elevation = a1->elevation;
|
||||
pathNode->elevation = obj->elevation;
|
||||
|
||||
tileToScreenXY(tile, &fromX, &fromY, a1->elevation);
|
||||
tileToScreenXY(tile, &fromX, &fromY, obj->elevation);
|
||||
pathNode->x = tileX - fromX - 16;
|
||||
pathNode->y = tileY - fromY - 8;
|
||||
}
|
||||
|
@ -2097,10 +2098,10 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
|
||||
if (tile != prevTile) {
|
||||
if (obstaclePtr != NULL) {
|
||||
Object* obj = callback(a1, tile, a1->elevation);
|
||||
if (obj != NULL) {
|
||||
if (obj != *obstaclePtr && (a6 != 32 || (obj->flags & OBJECT_SHOOT_THRU) == 0)) {
|
||||
*obstaclePtr = obj;
|
||||
Object* obstacle = callback(obj, tile, obj->elevation);
|
||||
if (obstacle != NULL) {
|
||||
if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) {
|
||||
*obstaclePtr = obstacle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2118,9 +2119,9 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
if (straightPathNodeList != NULL) {
|
||||
StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]);
|
||||
pathNode->tile = tile;
|
||||
pathNode->elevation = a1->elevation;
|
||||
pathNode->elevation = obj->elevation;
|
||||
|
||||
tileToScreenXY(tile, &fromX, &fromY, a1->elevation);
|
||||
tileToScreenXY(tile, &fromX, &fromY, obj->elevation);
|
||||
pathNode->x = tileX - fromX - 16;
|
||||
pathNode->y = tileY - fromY - 8;
|
||||
}
|
||||
|
@ -2128,7 +2129,7 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str
|
|||
pathNodeIndex += 1;
|
||||
} else {
|
||||
if (pathNodeIndex > 0 && straightPathNodeList != NULL) {
|
||||
straightPathNodeList[pathNodeIndex - 1].elevation = a1->elevation;
|
||||
straightPathNodeList[pathNodeIndex - 1].elevation = obj->elevation;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -145,8 +145,8 @@ int animationRegisterAnimateForever(Object* owner, int anim, int delay);
|
|||
int animationRegisterPing(int flags, int delay);
|
||||
int _make_path(Object* object, int from, int to, unsigned char* a4, int a5);
|
||||
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback);
|
||||
int _make_straight_path(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6);
|
||||
int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback);
|
||||
int _make_straight_path(Object* object, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6);
|
||||
int _make_straight_path_func(Object* object, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback);
|
||||
void _object_animate();
|
||||
int _check_move(int* actionPointsPtr);
|
||||
int _dude_move(int actionPoints);
|
||||
|
|
|
@ -438,6 +438,14 @@ void artRender(int fid, unsigned char* dest, int width, int height, int pitch)
|
|||
artUnlock(handle);
|
||||
}
|
||||
|
||||
// mapper2.exe: 0x40A03C
|
||||
int art_list_str(int fid, char* name)
|
||||
{
|
||||
// TODO: Incomplete.
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x419160
|
||||
Art* artLock(int fid, CacheEntry** handlePtr)
|
||||
{
|
||||
|
|
|
@ -123,6 +123,7 @@ char* artGetObjectTypeName(int objectType);
|
|||
int artIsObjectTypeHidden(int objectType);
|
||||
int artGetFidgetCount(int headFid);
|
||||
void artRender(int fid, unsigned char* dest, int width, int height, int pitch);
|
||||
int art_list_str(int fid, char* name);
|
||||
Art* artLock(int fid, CacheEntry** cache_entry);
|
||||
unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry** out_cache_entry);
|
||||
unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** out_cache_entry, int* widthPtr, int* heightPtr);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "db.h"
|
||||
#include "dbox.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_mouse.h"
|
||||
|
@ -1991,7 +1992,7 @@ static int _get_input_str(int win, int cancelKeyCode, char* text, int maxLength,
|
|||
|
||||
windowRefresh(win);
|
||||
|
||||
while (getTicksSince(_frame_time) < 1000 / 24) { }
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
|
@ -2281,8 +2282,7 @@ static void characterEditorDrawBigNumber(int x, int y, int flags, int value, int
|
|||
windowWidth);
|
||||
windowRefreshRect(windowHandle, &rect);
|
||||
renderPresent();
|
||||
while (getTicksSince(_frame_time) < BIG_NUM_ANIMATION_DELAY)
|
||||
;
|
||||
delay_ms(BIG_NUM_ANIMATION_DELAY - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
blitBufferToBuffer(numbersGraphicBufferPtr + BIG_NUM_WIDTH * ones,
|
||||
|
@ -2304,8 +2304,7 @@ static void characterEditorDrawBigNumber(int x, int y, int flags, int value, int
|
|||
windowWidth);
|
||||
windowRefreshRect(windowHandle, &rect);
|
||||
renderPresent();
|
||||
while (getTicksSince(_frame_time) < BIG_NUM_ANIMATION_DELAY)
|
||||
;
|
||||
delay_ms(BIG_NUM_ANIMATION_DELAY - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
blitBufferToBuffer(numbersGraphicBufferPtr + BIG_NUM_WIDTH * tens,
|
||||
|
@ -3530,11 +3529,9 @@ static int characterEditorEditAge()
|
|||
}
|
||||
|
||||
if (v33 > dbl_50170B) {
|
||||
while (getTicksSince(_frame_time) < 1000 / _repFtime)
|
||||
;
|
||||
delay_ms(1000 / _repFtime - (getTicks() - _frame_time));
|
||||
} else {
|
||||
while (getTicksSince(_frame_time) < 1000 / 24)
|
||||
;
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
keyCode = inputGetInput();
|
||||
|
@ -3548,8 +3545,7 @@ static int characterEditorEditAge()
|
|||
} else {
|
||||
windowRefresh(win);
|
||||
|
||||
while (getTicksSince(_frame_time) < 1000 / 24)
|
||||
;
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
@ -3699,8 +3695,7 @@ static void characterEditorEditGender()
|
|||
|
||||
windowRefresh(win);
|
||||
|
||||
while (getTicksSince(_frame_time) < 41)
|
||||
;
|
||||
delay_ms(41 - (getTicks() - _frame_time));
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
|
@ -3778,12 +3773,9 @@ static void characterEditorAdjustPrimaryStat(int eventCode)
|
|||
}
|
||||
|
||||
if (v11 >= 19.2) {
|
||||
unsigned int delay = 1000 / _repFtime;
|
||||
while (getTicksSince(_frame_time) < delay) {
|
||||
}
|
||||
delay_ms(1000 / _repFtime - (getTicks() - _frame_time));
|
||||
} else {
|
||||
while (getTicksSince(_frame_time) < 1000 / 24) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
@ -5279,11 +5271,9 @@ static void characterEditorHandleAdjustSkillButtonPressed(int keyCode)
|
|||
if (!isUsingKeyboard) {
|
||||
unspentSp = pcGetStat(PC_STAT_UNSPENT_SKILL_POINTS);
|
||||
if (repeatDelay >= dbl_5018F0) {
|
||||
while (getTicksSince(_frame_time) < 1000 / _repFtime) {
|
||||
}
|
||||
delay_ms(1000 / _repFtime - (getTicks() - _frame_time));
|
||||
} else {
|
||||
while (getTicksSince(_frame_time) < 1000 / 24) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
@ -6141,11 +6131,9 @@ static int perkDialogHandleInput(int count, void (*refreshProc)())
|
|||
}
|
||||
|
||||
if (v19 < dbl_5019BE) {
|
||||
while (getTicksSince(_frame_time) < 1000 / 24) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
} else {
|
||||
while (getTicksSince(_frame_time) < 1000 / _repFtime) {
|
||||
}
|
||||
delay_ms(1000 / _repFtime - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
@ -6188,11 +6176,9 @@ static int perkDialogHandleInput(int count, void (*refreshProc)())
|
|||
}
|
||||
|
||||
if (v19 < dbl_5019BE) {
|
||||
while (getTicksSince(_frame_time) < 1000 / 24) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
} else {
|
||||
while (getTicksSince(_frame_time) < 1000 / _repFtime) {
|
||||
}
|
||||
delay_ms(1000 / _repFtime - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
@ -6224,11 +6210,9 @@ static int perkDialogHandleInput(int count, void (*refreshProc)())
|
|||
}
|
||||
|
||||
if (v19 < dbl_5019BE) {
|
||||
while (getTicksSince(_frame_time) < 1000 / 24) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - _frame_time));
|
||||
} else {
|
||||
while (getTicksSince(_frame_time) < 1000 / _repFtime) {
|
||||
}
|
||||
delay_ms(1000 / _repFtime - (getTicks() - _frame_time));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
|
283
src/combat.cc
283
src/combat.cc
|
@ -37,6 +37,7 @@
|
|||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
|
@ -115,7 +116,7 @@ static int attackComputeCriticalHit(Attack* a1);
|
|||
static int _attackFindInvalidFlags(Object* a1, Object* a2);
|
||||
static int attackComputeCriticalFailure(Attack* attack);
|
||||
static void _do_random_cripple(int* flagsPtr);
|
||||
static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool a6);
|
||||
static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool useDistance);
|
||||
static void attackComputeDamage(Attack* attack, int ammoQuantity, int a3);
|
||||
static void _check_for_death(Object* a1, int a2, int* a3);
|
||||
static void _set_new_results(Object* a1, int a2);
|
||||
|
@ -3144,6 +3145,10 @@ static int _combat_input()
|
|||
}
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
// SFALL: CombatLoopHook.
|
||||
sfall_gl_scr_process_main();
|
||||
|
||||
if (_action_explode_running()) {
|
||||
// NOTE: Uninline.
|
||||
_combat_turn_run();
|
||||
|
@ -3471,16 +3476,16 @@ void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode,
|
|||
}
|
||||
|
||||
// 0x422F3C
|
||||
int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation)
|
||||
int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation)
|
||||
{
|
||||
if (a1 != gDude && hitMode == HIT_MODE_PUNCH && randomBetween(1, 4) == 1) {
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_KICK_LEG, (a1->fid & 0xF000) >> 12, (a1->fid & 0x70000000) >> 28);
|
||||
if (attacker != gDude && hitMode == HIT_MODE_PUNCH && randomBetween(1, 4) == 1) {
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_KICK_LEG, (attacker->fid & 0xF000) >> 12, (attacker->fid & 0x70000000) >> 28);
|
||||
if (artExists(fid)) {
|
||||
hitMode = HIT_MODE_KICK;
|
||||
}
|
||||
}
|
||||
|
||||
attackInit(&_main_ctd, a1, a2, hitMode, hitLocation);
|
||||
attackInit(&_main_ctd, attacker, defender, hitMode, hitLocation);
|
||||
debugPrint("computing attack...\n");
|
||||
|
||||
if (attackCompute(&_main_ctd) == -1) {
|
||||
|
@ -3508,7 +3513,7 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation)
|
|||
|
||||
bool aiming;
|
||||
if (_main_ctd.defenderHitLocation == HIT_LOCATION_TORSO || _main_ctd.defenderHitLocation == HIT_LOCATION_UNCALLED) {
|
||||
if (a1 == gDude) {
|
||||
if (attacker == gDude) {
|
||||
interfaceGetCurrentHitMode(&hitMode, &aiming);
|
||||
} else {
|
||||
aiming = false;
|
||||
|
@ -3517,22 +3522,22 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation)
|
|||
aiming = true;
|
||||
}
|
||||
|
||||
int actionPoints = weaponGetActionPointCost(a1, _main_ctd.hitMode, aiming);
|
||||
int actionPoints = weaponGetActionPointCost(attacker, _main_ctd.hitMode, aiming);
|
||||
debugPrint("sequencing attack...\n");
|
||||
|
||||
if (_action_attack(&_main_ctd) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (actionPoints > a1->data.critter.combat.ap) {
|
||||
a1->data.critter.combat.ap = 0;
|
||||
if (actionPoints > attacker->data.critter.combat.ap) {
|
||||
attacker->data.critter.combat.ap = 0;
|
||||
} else {
|
||||
a1->data.critter.combat.ap -= actionPoints;
|
||||
attacker->data.critter.combat.ap -= actionPoints;
|
||||
}
|
||||
|
||||
if (a1 == gDude) {
|
||||
interfaceRenderActionPoints(a1->data.critter.combat.ap, _combat_free_move);
|
||||
_critter_set_who_hit_me(a1, a2);
|
||||
if (attacker == gDude) {
|
||||
interfaceRenderActionPoints(attacker->data.critter.combat.ap, _combat_free_move);
|
||||
_critter_set_who_hit_me(attacker, defender);
|
||||
}
|
||||
|
||||
// SFALL
|
||||
|
@ -3540,19 +3545,19 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation)
|
|||
|
||||
_combat_call_display = 1;
|
||||
_combat_cleanup_enabled = 1;
|
||||
aiInfoSetLastTarget(a1, a2);
|
||||
aiInfoSetLastTarget(attacker, defender);
|
||||
debugPrint("running attack...\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns tile one step closer from [a1] to [a2]
|
||||
// Returns tile one step closer from [attacker] to [target]
|
||||
//
|
||||
// 0x423104
|
||||
int _combat_bullet_start(const Object* a1, const Object* a2)
|
||||
int _combat_bullet_start(const Object* attacker, const Object* target)
|
||||
{
|
||||
int rotation = tileGetRotationTo(a1->tile, a2->tile);
|
||||
return tileGetTileInDirection(a1->tile, rotation, 1);
|
||||
int rotation = tileGetRotationTo(attacker->tile, target->tile);
|
||||
return tileGetTileInDirection(attacker->tile, rotation, 1);
|
||||
}
|
||||
|
||||
// 0x423128
|
||||
|
@ -3937,17 +3942,17 @@ static int attackCompute(Attack* attack)
|
|||
|
||||
attack->tile = tile;
|
||||
|
||||
Object* v25 = attack->defender;
|
||||
_make_straight_path_func(v25, attack->defender->tile, attack->tile, NULL, &v25, 32, _obj_shoot_blocking_at);
|
||||
if (v25 != NULL && v25 != attack->defender) {
|
||||
attack->tile = v25->tile;
|
||||
Object* accidentalTarget = attack->defender;
|
||||
_make_straight_path_func(accidentalTarget, attack->defender->tile, attack->tile, NULL, &accidentalTarget, 32, _obj_shoot_blocking_at);
|
||||
if (accidentalTarget != NULL && accidentalTarget != attack->defender) {
|
||||
attack->tile = accidentalTarget->tile;
|
||||
} else {
|
||||
v25 = _obj_blocking_at(NULL, attack->tile, attack->defender->elevation);
|
||||
accidentalTarget = _obj_blocking_at(NULL, attack->tile, attack->defender->elevation);
|
||||
}
|
||||
|
||||
if (v25 != NULL && (v25->flags & OBJECT_SHOOT_THRU) == 0) {
|
||||
if (accidentalTarget != NULL && (accidentalTarget->flags & OBJECT_SHOOT_THRU) == 0) {
|
||||
attack->attackerFlags |= DAM_HIT;
|
||||
attack->defender = v25;
|
||||
attack->defender = accidentalTarget;
|
||||
attackComputeDamage(attack, 1, 2);
|
||||
}
|
||||
}
|
||||
|
@ -3969,75 +3974,75 @@ static int attackCompute(Attack* attack)
|
|||
|
||||
// compute_explosion_on_extras
|
||||
// 0x423C10
|
||||
void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4)
|
||||
void _compute_explosion_on_extras(Attack* attack, bool isFromAttacker, bool isGrenade, bool noDamage)
|
||||
{
|
||||
Object* attacker;
|
||||
Object* targetObj;
|
||||
|
||||
if (a2) {
|
||||
attacker = attack->attacker;
|
||||
if (isFromAttacker) {
|
||||
targetObj = attack->attacker;
|
||||
} else {
|
||||
if ((attack->attackerFlags & DAM_HIT) != 0) {
|
||||
attacker = attack->defender;
|
||||
targetObj = attack->defender;
|
||||
} else {
|
||||
attacker = NULL;
|
||||
targetObj = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int tile;
|
||||
if (attacker != NULL) {
|
||||
tile = attacker->tile;
|
||||
int explosionTile;
|
||||
if (targetObj != NULL) {
|
||||
explosionTile = targetObj->tile;
|
||||
} else {
|
||||
tile = attack->tile;
|
||||
explosionTile = attack->tile;
|
||||
}
|
||||
|
||||
if (tile == -1) {
|
||||
if (explosionTile == -1) {
|
||||
debugPrint("\nError: compute_explosion_on_extras: Called with bad target/tileNum");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: The math in this loop is rather complex and hard to understand.
|
||||
int v20;
|
||||
int v22 = 0;
|
||||
int ringTileIdx;
|
||||
int radius = 0;
|
||||
int rotation = 0;
|
||||
int v5 = -1;
|
||||
int v19 = tile;
|
||||
int tile = -1;
|
||||
int ringFirstTile = explosionTile;
|
||||
|
||||
// SFALL
|
||||
int maxTargets = explosionGetMaxTargets();
|
||||
// Check adjacent tiles for possible targets, going ring-by-ring
|
||||
while (attack->extrasLength < maxTargets) {
|
||||
if (v22 != 0 && (v5 == -1 || (v5 = tileGetTileInDirection(v5, rotation, 1)) != v19)) {
|
||||
v20++;
|
||||
if (v20 % v22 == 0) {
|
||||
if (radius != 0 && (tile == -1 || (tile = tileGetTileInDirection(tile, rotation, 1)) != ringFirstTile)) {
|
||||
ringTileIdx++;
|
||||
if (ringTileIdx % radius == 0) { // the larger the radius, the slower we rotate
|
||||
rotation += 1;
|
||||
if (rotation == ROTATION_COUNT) {
|
||||
rotation = ROTATION_NE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
v22++;
|
||||
if (isGrenade && weaponGetGrenadeExplosionRadius(attack->weapon) < v22) {
|
||||
v5 = -1;
|
||||
} else if (isGrenade || weaponGetRocketExplosionRadius(attack->weapon) >= v22) {
|
||||
v5 = tileGetTileInDirection(v19, ROTATION_NE, 1);
|
||||
radius++; // go to the next ring
|
||||
if (isGrenade && weaponGetGrenadeExplosionRadius(attack->weapon) < radius) {
|
||||
tile = -1;
|
||||
} else if (isGrenade || weaponGetRocketExplosionRadius(attack->weapon) >= radius) {
|
||||
tile = tileGetTileInDirection(ringFirstTile, ROTATION_NE, 1);
|
||||
} else {
|
||||
v5 = -1;
|
||||
tile = -1;
|
||||
}
|
||||
|
||||
v19 = v5;
|
||||
ringFirstTile = tile;
|
||||
rotation = ROTATION_SE;
|
||||
v20 = 0;
|
||||
ringTileIdx = 0;
|
||||
}
|
||||
|
||||
if (v5 == -1) {
|
||||
if (tile == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
Object* obstacle = _obj_blocking_at(attacker, v5, attack->attacker->elevation);
|
||||
Object* obstacle = _obj_blocking_at(targetObj, tile, attack->attacker->elevation);
|
||||
if (obstacle != NULL
|
||||
&& FID_TYPE(obstacle->fid) == OBJ_TYPE_CRITTER
|
||||
&& (obstacle->data.critter.combat.results & DAM_DEAD) == 0
|
||||
&& (obstacle->flags & OBJECT_SHOOT_THRU) == 0
|
||||
&& !_combat_is_shot_blocked(obstacle, obstacle->tile, tile, NULL, NULL)) {
|
||||
&& !_combat_is_shot_blocked(obstacle, obstacle->tile, explosionTile, NULL, NULL)) {
|
||||
if (obstacle == attack->attacker) {
|
||||
attack->attackerFlags &= ~DAM_HIT;
|
||||
attackComputeDamage(attack, 1, 2);
|
||||
|
@ -4055,7 +4060,7 @@ void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4
|
|||
attack->extrasHitLocation[index] = HIT_LOCATION_TORSO;
|
||||
attack->extras[index] = obstacle;
|
||||
attackInit(&_explosion_ctd, attack->attacker, obstacle, attack->hitMode, HIT_LOCATION_TORSO);
|
||||
if (!a4) {
|
||||
if (!noDamage) {
|
||||
_explosion_ctd.attackerFlags |= DAM_HIT;
|
||||
attackComputeDamage(&_explosion_ctd, 1, 2);
|
||||
}
|
||||
|
@ -4277,26 +4282,26 @@ static void _do_random_cripple(int* flagsPtr)
|
|||
}
|
||||
|
||||
// 0x42436C
|
||||
int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode)
|
||||
int _determine_to_hit(Object* attacker, Object* defender, int hitLocation, int hitMode)
|
||||
{
|
||||
return attackDetermineToHit(a1, a1->tile, a2, hitLocation, hitMode, true);
|
||||
return attackDetermineToHit(attacker, attacker->tile, defender, hitLocation, hitMode, true);
|
||||
}
|
||||
|
||||
// 0x424380
|
||||
int _determine_to_hit_no_range(Object* a1, Object* a2, int hitLocation, int hitMode, unsigned char* a5)
|
||||
int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5)
|
||||
{
|
||||
return attackDetermineToHit(a1, a1->tile, a2, hitLocation, hitMode, false);
|
||||
return attackDetermineToHit(attacker, attacker->tile, defender, hitLocation, hitMode, false);
|
||||
}
|
||||
|
||||
// 0x424394
|
||||
int _determine_to_hit_from_tile(Object* a1, int tile, Object* a3, int hitLocation, int hitMode)
|
||||
int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode)
|
||||
{
|
||||
return attackDetermineToHit(a1, tile, a3, hitLocation, hitMode, true);
|
||||
return attackDetermineToHit(attacker, tile, defender, hitLocation, hitMode, true);
|
||||
}
|
||||
|
||||
// determine_to_hit
|
||||
// 0x4243A8
|
||||
static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool a6)
|
||||
static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool useDistance)
|
||||
{
|
||||
Object* weapon = critterGetWeaponForHitMode(attacker, hitMode);
|
||||
|
||||
|
@ -4306,32 +4311,30 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
|||
|
||||
bool isRangedWeapon = false;
|
||||
|
||||
int accuracy;
|
||||
int toHit;
|
||||
if (weapon == NULL || isUnarmedHitMode(hitMode)) {
|
||||
accuracy = skillGetValue(attacker, SKILL_UNARMED);
|
||||
toHit = skillGetValue(attacker, SKILL_UNARMED);
|
||||
} else {
|
||||
accuracy = weaponGetSkillValue(attacker, hitMode);
|
||||
|
||||
int modifier = 0;
|
||||
toHit = weaponGetSkillValue(attacker, hitMode);
|
||||
|
||||
int attackType = weaponGetAttackTypeForHitMode(weapon, hitMode);
|
||||
if (attackType == ATTACK_TYPE_RANGED || attackType == ATTACK_TYPE_THROW) {
|
||||
isRangedWeapon = true;
|
||||
|
||||
int v29 = 0;
|
||||
int v25 = 0;
|
||||
int perceptionBonusMult = 0;
|
||||
int minEffectiveDist = 0;
|
||||
|
||||
int weaponPerk = weaponGetPerk(weapon);
|
||||
switch (weaponPerk) {
|
||||
case PERK_WEAPON_LONG_RANGE:
|
||||
v29 = 4;
|
||||
perceptionBonusMult = 4;
|
||||
break;
|
||||
case PERK_WEAPON_SCOPE_RANGE:
|
||||
v29 = 5;
|
||||
v25 = 8;
|
||||
perceptionBonusMult = 5;
|
||||
minEffectiveDist = 8;
|
||||
break;
|
||||
default:
|
||||
v29 = 2;
|
||||
perceptionBonusMult = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -4342,71 +4345,72 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
|||
perception += 2 * perkGetRank(gDude, PERK_SHARPSHOOTER);
|
||||
}
|
||||
|
||||
int distanceMod = 0;
|
||||
// SFALL: Fix for `determine_to_hit_func` function taking distance
|
||||
// into account when called from `determine_to_hit_no_range`.
|
||||
if (defender != NULL && a6) {
|
||||
modifier = objectGetDistanceBetweenTiles(attacker, tile, defender, defender->tile);
|
||||
if (defender != NULL && useDistance) {
|
||||
distanceMod = objectGetDistanceBetweenTiles(attacker, tile, defender, defender->tile);
|
||||
} else {
|
||||
modifier = 0;
|
||||
distanceMod = 0;
|
||||
}
|
||||
|
||||
if (modifier >= v25) {
|
||||
int penalty = attacker == gDude
|
||||
? v29 * (perception - 2)
|
||||
: v29 * perception;
|
||||
if (distanceMod >= minEffectiveDist) {
|
||||
int perceptionBonus = attacker == gDude
|
||||
? perceptionBonusMult * (perception - 2)
|
||||
: perceptionBonusMult * perception;
|
||||
|
||||
modifier -= penalty;
|
||||
distanceMod -= perceptionBonus;
|
||||
} else {
|
||||
modifier += v25;
|
||||
distanceMod += minEffectiveDist;
|
||||
}
|
||||
|
||||
if (-2 * perception > modifier) {
|
||||
modifier = -2 * perception;
|
||||
if (distanceMod < -2 * perception) {
|
||||
distanceMod = -2 * perception;
|
||||
}
|
||||
|
||||
if (modifier >= 0) {
|
||||
if (distanceMod >= 0) {
|
||||
if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) {
|
||||
modifier *= -12;
|
||||
distanceMod *= -12;
|
||||
} else {
|
||||
modifier *= -4;
|
||||
distanceMod *= -4;
|
||||
}
|
||||
} else {
|
||||
modifier *= -4;
|
||||
distanceMod *= -4;
|
||||
}
|
||||
|
||||
if (a6 || modifier > 0) {
|
||||
accuracy += modifier;
|
||||
if (useDistance || distanceMod > 0) {
|
||||
toHit += distanceMod;
|
||||
}
|
||||
|
||||
modifier = 0;
|
||||
int numCrittersInLof = 0;
|
||||
|
||||
if (defender != NULL && a6) {
|
||||
_combat_is_shot_blocked(attacker, tile, defender->tile, defender, &modifier);
|
||||
if (defender != NULL && useDistance) {
|
||||
_combat_is_shot_blocked(attacker, tile, defender->tile, defender, &numCrittersInLof);
|
||||
}
|
||||
|
||||
accuracy -= 10 * modifier;
|
||||
toHit -= 10 * numCrittersInLof;
|
||||
}
|
||||
|
||||
if (attacker == gDude && traitIsSelected(TRAIT_ONE_HANDER)) {
|
||||
if (weaponIsTwoHanded(weapon)) {
|
||||
accuracy -= 40;
|
||||
toHit -= 40;
|
||||
} else {
|
||||
accuracy += 20;
|
||||
toHit += 20;
|
||||
}
|
||||
}
|
||||
|
||||
int minStrength = weaponGetMinStrengthRequired(weapon);
|
||||
modifier = minStrength - critterGetStat(attacker, STAT_STRENGTH);
|
||||
int minStrengthMod = minStrength - critterGetStat(attacker, STAT_STRENGTH);
|
||||
if (attacker == gDude && perkGetRank(gDude, PERK_WEAPON_HANDLING) != 0) {
|
||||
modifier -= 3;
|
||||
minStrengthMod -= 3;
|
||||
}
|
||||
|
||||
if (modifier > 0) {
|
||||
accuracy -= 20 * modifier;
|
||||
if (minStrengthMod > 0) {
|
||||
toHit -= 20 * minStrengthMod;
|
||||
}
|
||||
|
||||
if (weaponGetPerk(weapon) == PERK_WEAPON_ACCURATE) {
|
||||
accuracy += 20;
|
||||
toHit += 20;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4417,17 +4421,17 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
|||
armorClass = 0;
|
||||
}
|
||||
|
||||
accuracy -= armorClass;
|
||||
toHit -= armorClass;
|
||||
}
|
||||
|
||||
if (isRangedWeapon) {
|
||||
accuracy += hit_location_penalty[hitLocation];
|
||||
toHit += hit_location_penalty[hitLocation];
|
||||
} else {
|
||||
accuracy += hit_location_penalty[hitLocation] / 2;
|
||||
toHit += hit_location_penalty[hitLocation] / 2;
|
||||
}
|
||||
|
||||
if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) {
|
||||
accuracy += 15;
|
||||
toHit += 15;
|
||||
}
|
||||
|
||||
if (attacker == gDude) {
|
||||
|
@ -4442,45 +4446,45 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
|||
}
|
||||
|
||||
if (lightIntensity <= 26214)
|
||||
accuracy -= 40;
|
||||
toHit -= 40;
|
||||
else if (lightIntensity <= 39321)
|
||||
accuracy -= 25;
|
||||
toHit -= 25;
|
||||
else if (lightIntensity <= 52428)
|
||||
accuracy -= 10;
|
||||
toHit -= 10;
|
||||
}
|
||||
|
||||
if (_gcsd != NULL) {
|
||||
accuracy += _gcsd->accuracyBonus;
|
||||
toHit += _gcsd->accuracyBonus;
|
||||
}
|
||||
|
||||
if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) {
|
||||
accuracy -= 25;
|
||||
toHit -= 25;
|
||||
}
|
||||
|
||||
if (targetIsCritter && defender != NULL && (defender->data.critter.combat.results & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) != 0) {
|
||||
accuracy += 40;
|
||||
toHit += 40;
|
||||
}
|
||||
|
||||
if (attacker->data.critter.combat.team != gDude->data.critter.combat.team) {
|
||||
switch (settings.preferences.combat_difficulty) {
|
||||
case 0:
|
||||
accuracy -= 20;
|
||||
toHit -= 20;
|
||||
break;
|
||||
case 2:
|
||||
accuracy += 20;
|
||||
toHit += 20;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (accuracy > 95) {
|
||||
accuracy = 95;
|
||||
if (toHit > 95) {
|
||||
toHit = 95;
|
||||
}
|
||||
|
||||
if (accuracy < -100) {
|
||||
if (toHit < -100) {
|
||||
debugPrint("Whoa! Bad skill value in determine_to_hit!\n");
|
||||
}
|
||||
|
||||
return accuracy;
|
||||
return toHit;
|
||||
}
|
||||
|
||||
// 0x4247B8
|
||||
|
@ -5875,39 +5879,35 @@ void _combat_highlight_change()
|
|||
_combat_highlight = targetHighlight;
|
||||
}
|
||||
|
||||
// Probably calculates line of sight or determines if object can see other object.
|
||||
// Checks if line of fire to the target object is blocked or not. Optionally calculate number of critters on the line of fire.
|
||||
//
|
||||
// 0x426CC4
|
||||
bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5)
|
||||
bool _combat_is_shot_blocked(Object* sourceObj, int from, int to, Object* targetObj, int* numCrittersOnLof)
|
||||
{
|
||||
if (a5 != NULL) {
|
||||
*a5 = 0;
|
||||
if (numCrittersOnLof != NULL) {
|
||||
*numCrittersOnLof = 0;
|
||||
}
|
||||
|
||||
Object* obstacle = a1;
|
||||
Object* obstacle = sourceObj;
|
||||
int current = from;
|
||||
while (obstacle != NULL && current != to) {
|
||||
_make_straight_path_func(a1, current, to, 0, &obstacle, 32, _obj_shoot_blocking_at);
|
||||
_make_straight_path_func(sourceObj, current, to, 0, &obstacle, 32, _obj_shoot_blocking_at);
|
||||
if (obstacle != NULL) {
|
||||
if (FID_TYPE(obstacle->fid) != OBJ_TYPE_CRITTER && obstacle != a4) {
|
||||
if (FID_TYPE(obstacle->fid) != OBJ_TYPE_CRITTER && obstacle != targetObj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a5 != NULL) {
|
||||
if (obstacle != a4) {
|
||||
if (a4 != NULL) {
|
||||
// SFALL: Fix for combat_is_shot_blocked_ engine
|
||||
// function not taking the flags of critters in the
|
||||
// line of fire into account when calculating the hit
|
||||
// chance penalty of ranged attacks in
|
||||
// determine_to_hit_func_ engine function.
|
||||
if ((obstacle->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_DOWN | DAM_KNOCKED_OUT)) == 0) {
|
||||
*a5 += 1;
|
||||
if (numCrittersOnLof != NULL && obstacle != targetObj && targetObj != NULL) {
|
||||
// SFALL: Fix for combat_is_shot_blocked_ engine
|
||||
// function not taking the flags of critters in the
|
||||
// line of fire into account when calculating the hit
|
||||
// chance penalty of ranged attacks in
|
||||
// determine_to_hit_func_ engine function.
|
||||
if ((obstacle->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_DOWN | DAM_KNOCKED_OUT)) == 0) {
|
||||
*numCrittersOnLof += 1;
|
||||
|
||||
if ((obstacle->flags & OBJECT_MULTIHEX) != 0) {
|
||||
*a5 += 1;
|
||||
}
|
||||
}
|
||||
if ((obstacle->flags & OBJECT_MULTIHEX) != 0) {
|
||||
*numCrittersOnLof += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6825,4 +6825,9 @@ void combat_reset_hit_location_penalty()
|
|||
}
|
||||
}
|
||||
|
||||
Attack* combat_get_data()
|
||||
{
|
||||
return &_main_ctd;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
15
src/combat.h
15
src/combat.h
|
@ -34,13 +34,13 @@ void _combat_over_from_load();
|
|||
void _combat_give_exps(int exp_points);
|
||||
void _combat_turn_run();
|
||||
void _combat(STRUCT_664980* attack);
|
||||
void attackInit(Attack* attack, Object* a2, Object* a3, int a4, int a5);
|
||||
int _combat_attack(Object* a1, Object* a2, int a3, int a4);
|
||||
int _combat_bullet_start(const Object* a1, const Object* a2);
|
||||
void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4);
|
||||
void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode, int hitLocation);
|
||||
int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation);
|
||||
int _combat_bullet_start(const Object* attacker, const Object* target);
|
||||
void _compute_explosion_on_extras(Attack* attack, bool isFromAttacker, bool isGrenade, bool noDamage);
|
||||
int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode);
|
||||
int _determine_to_hit_no_range(Object* a1, Object* a2, int a3, int a4, unsigned char* a5);
|
||||
int _determine_to_hit_from_tile(Object* a1, int a2, Object* a3, int a4, int a5);
|
||||
int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5);
|
||||
int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode);
|
||||
void attackComputeDeathFlags(Attack* attack);
|
||||
void _apply_damage(Attack* attack, bool animated);
|
||||
void _combat_display(Attack* attack);
|
||||
|
@ -52,7 +52,7 @@ void _combat_attack_this(Object* a1);
|
|||
void _combat_outline_on();
|
||||
void _combat_outline_off();
|
||||
void _combat_highlight_change();
|
||||
bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5);
|
||||
bool _combat_is_shot_blocked(Object* sourceObj, int from, int to, Object* targetObj, int* numCrittersOnLof);
|
||||
int _combat_player_knocked_out_by();
|
||||
int _combat_explode_scenery(Object* a1, Object* a2);
|
||||
void _combat_delete_critter(Object* obj);
|
||||
|
@ -74,6 +74,7 @@ bool damageModGetDisplayBonusDamage();
|
|||
int combat_get_hit_location_penalty(int hit_location);
|
||||
void combat_set_hit_location_penalty(int hit_location, int penalty);
|
||||
void combat_reset_hit_location_penalty();
|
||||
Attack* combat_get_data();
|
||||
|
||||
static inline bool isInCombat()
|
||||
{
|
||||
|
|
209
src/combat_ai.cc
209
src/combat_ai.cc
|
@ -701,6 +701,30 @@ static int aiPacketWrite(File* stream, AiPacket* ai)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// 0x428058
|
||||
int combat_ai_num()
|
||||
{
|
||||
return gAiPacketsLength;
|
||||
}
|
||||
|
||||
// 0x428060
|
||||
char* combat_ai_name(int packet_num)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (packet_num < 0 || packet_num >= gAiPacketsLength) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (index = 0; index < gAiPacketsLength; index++) {
|
||||
if (gAiPackets[index].packet_num == packet_num) {
|
||||
return gAiPackets[index].name;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get ai from object
|
||||
//
|
||||
// 0x4280B4
|
||||
|
@ -1440,7 +1464,7 @@ static int aiFindAttackers(Object* critter, Object** whoHitMePtr, Object** whoHi
|
|||
*whoHitFriendPtr = NULL;
|
||||
}
|
||||
|
||||
if (*whoHitByFriendPtr != NULL) {
|
||||
if (whoHitByFriendPtr != NULL) {
|
||||
*whoHitByFriendPtr = NULL;
|
||||
}
|
||||
|
||||
|
@ -2327,61 +2351,61 @@ static int _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender)
|
|||
}
|
||||
|
||||
// 0x429FC8
|
||||
static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool taunt)
|
||||
static int _ai_move_steps_closer(Object* critter, Object* target, int actionPoints, bool taunt)
|
||||
{
|
||||
if (actionPoints <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int distance = aiGetDistance(a1);
|
||||
int distance = aiGetDistance(critter);
|
||||
if (distance == DISTANCE_STAY) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (distance == DISTANCE_STAY_CLOSE) {
|
||||
if (a2 != gDude) {
|
||||
int currentDistance = objectGetDistanceBetween(a1, gDude);
|
||||
if (target != gDude) {
|
||||
int currentDistance = objectGetDistanceBetween(critter, gDude);
|
||||
if (currentDistance > 5
|
||||
&& objectGetDistanceBetween(a2, gDude) > 5
|
||||
&& objectGetDistanceBetween(target, gDude) > 5
|
||||
&& currentDistance + actionPoints > 5) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectGetDistanceBetween(a1, a2) <= 1) {
|
||||
if (objectGetDistanceBetween(critter, target) <= 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
|
||||
|
||||
if (taunt) {
|
||||
_combatai_msg(a1, NULL, AI_MESSAGE_TYPE_MOVE, 0);
|
||||
_combatai_msg(critter, NULL, AI_MESSAGE_TYPE_MOVE, 0);
|
||||
}
|
||||
|
||||
Object* v18 = a2;
|
||||
Object* initialTarget = target;
|
||||
|
||||
bool shouldUnhide;
|
||||
if ((a2->flags & OBJECT_MULTIHEX) != 0) {
|
||||
if ((target->flags & OBJECT_MULTIHEX) != 0) {
|
||||
shouldUnhide = true;
|
||||
a2->flags |= OBJECT_HIDDEN;
|
||||
target->flags |= OBJECT_HIDDEN;
|
||||
} else {
|
||||
shouldUnhide = false;
|
||||
}
|
||||
|
||||
if (pathfinderFindPath(a1, a1->tile, a2->tile, NULL, 0, _obj_blocking_at) == 0) {
|
||||
if (pathfinderFindPath(critter, critter->tile, target->tile, NULL, 0, _obj_blocking_at) == 0) {
|
||||
_moveBlockObj = NULL;
|
||||
if (pathfinderFindPath(a1, a1->tile, a2->tile, NULL, 0, _obj_ai_blocking_at) == 0
|
||||
if (pathfinderFindPath(critter, critter->tile, target->tile, NULL, 0, _obj_ai_blocking_at) == 0
|
||||
&& _moveBlockObj != NULL
|
||||
&& PID_TYPE(_moveBlockObj->pid) == OBJ_TYPE_CRITTER) {
|
||||
if (shouldUnhide) {
|
||||
a2->flags &= ~OBJECT_HIDDEN;
|
||||
target->flags &= ~OBJECT_HIDDEN;
|
||||
}
|
||||
|
||||
a2 = _moveBlockObj;
|
||||
if ((a2->flags & OBJECT_MULTIHEX) != 0) {
|
||||
target = _moveBlockObj;
|
||||
if ((target->flags & OBJECT_MULTIHEX) != 0) {
|
||||
shouldUnhide = true;
|
||||
a2->flags |= OBJECT_HIDDEN;
|
||||
target->flags |= OBJECT_HIDDEN;
|
||||
} else {
|
||||
shouldUnhide = false;
|
||||
}
|
||||
|
@ -2389,25 +2413,25 @@ static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool
|
|||
}
|
||||
|
||||
if (shouldUnhide) {
|
||||
a2->flags &= ~OBJECT_HIDDEN;
|
||||
target->flags &= ~OBJECT_HIDDEN;
|
||||
}
|
||||
|
||||
int tile = a2->tile;
|
||||
if (a2 == v18) {
|
||||
_cai_retargetTileFromFriendlyFire(a1, a2, &tile);
|
||||
int tile = target->tile;
|
||||
if (target == initialTarget) {
|
||||
_cai_retargetTileFromFriendlyFire(critter, target, &tile);
|
||||
}
|
||||
|
||||
if (actionPoints >= critterGetStat(a1, STAT_MAXIMUM_ACTION_POINTS) / 2 && artCritterFidShouldRun(a1->fid)) {
|
||||
if ((a2->flags & OBJECT_MULTIHEX) != 0) {
|
||||
animationRegisterRunToObject(a1, a2, actionPoints, 0);
|
||||
if (actionPoints >= critterGetStat(critter, STAT_MAXIMUM_ACTION_POINTS) / 2 && artCritterFidShouldRun(critter->fid)) {
|
||||
if ((target->flags & OBJECT_MULTIHEX) != 0) {
|
||||
animationRegisterRunToObject(critter, target, actionPoints, 0);
|
||||
} else {
|
||||
animationRegisterRunToTile(a1, tile, a1->elevation, actionPoints, 0);
|
||||
animationRegisterRunToTile(critter, tile, critter->elevation, actionPoints, 0);
|
||||
}
|
||||
} else {
|
||||
if ((a2->flags & OBJECT_MULTIHEX) != 0) {
|
||||
animationRegisterMoveToObject(a1, a2, actionPoints, 0);
|
||||
if ((target->flags & OBJECT_MULTIHEX) != 0) {
|
||||
animationRegisterMoveToObject(critter, target, actionPoints, 0);
|
||||
} else {
|
||||
animationRegisterMoveToTile(a1, tile, a1->elevation, actionPoints, 0);
|
||||
animationRegisterMoveToTile(critter, tile, critter->elevation, actionPoints, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2665,35 +2689,35 @@ static int _ai_attack(Object* attacker, Object* defender, int hitMode)
|
|||
}
|
||||
|
||||
// 0x42A7D8
|
||||
static int _ai_try_attack(Object* a1, Object* a2)
|
||||
static int _ai_try_attack(Object* attacker, Object* defender)
|
||||
{
|
||||
_critter_set_who_hit_me(a1, a2);
|
||||
_critter_set_who_hit_me(attacker, defender);
|
||||
|
||||
CritterCombatData* combatData = &(a1->data.critter.combat);
|
||||
CritterCombatData* combatData = &(attacker->data.critter.combat);
|
||||
bool taunt = true;
|
||||
|
||||
Object* weapon = critterGetItem2(a1);
|
||||
Object* weapon = critterGetItem2(attacker);
|
||||
if (weapon != NULL && itemGetType(weapon) != ITEM_TYPE_WEAPON) {
|
||||
weapon = NULL;
|
||||
}
|
||||
|
||||
int hitMode = _ai_pick_hit_mode(a1, weapon, a2);
|
||||
int minToHit = aiGetPacket(a1)->min_to_hit;
|
||||
int hitMode = _ai_pick_hit_mode(attacker, weapon, defender);
|
||||
int minToHit = aiGetPacket(attacker)->min_to_hit;
|
||||
|
||||
int actionPoints = a1->data.critter.combat.ap;
|
||||
int actionPoints = attacker->data.critter.combat.ap;
|
||||
int safeDistance = 0;
|
||||
int v42 = 0;
|
||||
int actionPointsToUse = 0;
|
||||
if (weapon != NULL
|
||||
|| (critterGetBodyType(a2) == BODY_TYPE_BIPED
|
||||
&& ((a2->fid & 0xF000) >> 12 == 0)
|
||||
&& artExists(buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_THROW_PUNCH, 0, a1->rotation + 1)))) {
|
||||
|| (critterGetBodyType(defender) == BODY_TYPE_BIPED
|
||||
&& ((defender->fid & 0xF000) >> 12 == 0)
|
||||
&& artExists(buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_THROW_PUNCH, 0, attacker->rotation + 1)))) {
|
||||
// SFALL: Check the safety of weapons based on the selected attack mode
|
||||
// instead of always the primary weapon hit mode.
|
||||
if (_combat_safety_invalidate_weapon(a1, weapon, hitMode, a2, &safeDistance)) {
|
||||
_ai_switch_weapons(a1, &hitMode, &weapon, a2);
|
||||
if (_combat_safety_invalidate_weapon(attacker, weapon, hitMode, defender, &safeDistance)) {
|
||||
_ai_switch_weapons(attacker, &hitMode, &weapon, defender);
|
||||
}
|
||||
} else {
|
||||
_ai_switch_weapons(a1, &hitMode, &weapon, a2);
|
||||
_ai_switch_weapons(attacker, &hitMode, &weapon, defender);
|
||||
}
|
||||
|
||||
unsigned char rotations[800];
|
||||
|
@ -2704,37 +2728,37 @@ static int _ai_try_attack(Object* a1, Object* a2)
|
|||
break;
|
||||
}
|
||||
|
||||
int reason = _combat_check_bad_shot(a1, a2, hitMode, false);
|
||||
int reason = _combat_check_bad_shot(attacker, defender, hitMode, false);
|
||||
if (reason == COMBAT_BAD_SHOT_NO_AMMO) {
|
||||
// out of ammo
|
||||
if (aiHaveAmmo(a1, weapon, &ammo)) {
|
||||
if (aiHaveAmmo(attacker, weapon, &ammo)) {
|
||||
int remainingAmmoQuantity = weaponReload(weapon, ammo);
|
||||
if (remainingAmmoQuantity == 0 && ammo != NULL) {
|
||||
_obj_destroy(ammo);
|
||||
}
|
||||
|
||||
if (remainingAmmoQuantity != -1) {
|
||||
int volume = _gsound_compute_relative_volume(a1);
|
||||
int volume = _gsound_compute_relative_volume(attacker);
|
||||
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL);
|
||||
_gsound_play_sfx_file_volume(sfx, volume);
|
||||
_ai_magic_hands(a1, weapon, 5002);
|
||||
_ai_magic_hands(attacker, weapon, 5002);
|
||||
|
||||
// SFALL: Fix incorrect AP cost when AI reloads a weapon.
|
||||
// CE: There is a commented out code which checks
|
||||
// available action points before performing reload. Not
|
||||
// sure why it was commented, probably needs additional
|
||||
// testing.
|
||||
int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
|
||||
if (a1->data.critter.combat.ap >= actionPointsRequired) {
|
||||
a1->data.critter.combat.ap -= actionPointsRequired;
|
||||
int actionPointsRequired = weaponGetActionPointCost(attacker, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
|
||||
if (attacker->data.critter.combat.ap >= actionPointsRequired) {
|
||||
attacker->data.critter.combat.ap -= actionPointsRequired;
|
||||
} else {
|
||||
a1->data.critter.combat.ap = 0;
|
||||
attacker->data.critter.combat.ap = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ammo = _ai_search_environ(a1, ITEM_TYPE_AMMO);
|
||||
ammo = _ai_search_environ(attacker, ITEM_TYPE_AMMO);
|
||||
if (ammo != NULL) {
|
||||
ammo = _ai_retrieve_object(a1, ammo);
|
||||
ammo = _ai_retrieve_object(attacker, ammo);
|
||||
if (ammo != NULL) {
|
||||
int remainingAmmoQuantity = weaponReload(weapon, ammo);
|
||||
if (remainingAmmoQuantity == 0) {
|
||||
|
@ -2742,62 +2766,63 @@ static int _ai_try_attack(Object* a1, Object* a2)
|
|||
}
|
||||
|
||||
if (remainingAmmoQuantity != -1) {
|
||||
int volume = _gsound_compute_relative_volume(a1);
|
||||
int volume = _gsound_compute_relative_volume(attacker);
|
||||
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL);
|
||||
_gsound_play_sfx_file_volume(sfx, volume);
|
||||
_ai_magic_hands(a1, weapon, 5002);
|
||||
_ai_magic_hands(attacker, weapon, 5002);
|
||||
|
||||
// SFALL: Fix incorrect AP cost when AI reloads a
|
||||
// weapon.
|
||||
// CE: See note above, probably need to check
|
||||
// available action points before performing
|
||||
// reload.
|
||||
int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
|
||||
if (a1->data.critter.combat.ap >= actionPointsRequired) {
|
||||
a1->data.critter.combat.ap -= actionPointsRequired;
|
||||
int actionPointsRequired = weaponGetActionPointCost(attacker, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
|
||||
if (attacker->data.critter.combat.ap >= actionPointsRequired) {
|
||||
attacker->data.critter.combat.ap -= actionPointsRequired;
|
||||
} else {
|
||||
a1->data.critter.combat.ap = 0;
|
||||
attacker->data.critter.combat.ap = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int volume = _gsound_compute_relative_volume(a1);
|
||||
int volume = _gsound_compute_relative_volume(attacker);
|
||||
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_OUT_OF_AMMO, weapon, hitMode, NULL);
|
||||
_gsound_play_sfx_file_volume(sfx, volume);
|
||||
_ai_magic_hands(a1, weapon, 5001);
|
||||
_ai_magic_hands(attacker, weapon, 5001);
|
||||
|
||||
if (_inven_unwield(a1, 1) == 0) {
|
||||
if (_inven_unwield(attacker, 1) == 0) {
|
||||
_combat_turn_run();
|
||||
}
|
||||
|
||||
_ai_switch_weapons(a1, &hitMode, &weapon, a2);
|
||||
_ai_switch_weapons(attacker, &hitMode, &weapon, defender);
|
||||
}
|
||||
}
|
||||
} else if (reason == COMBAT_BAD_SHOT_NOT_ENOUGH_AP || reason == COMBAT_BAD_SHOT_ARM_CRIPPLED || reason == COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED) {
|
||||
// 3 - not enough action points
|
||||
// 6 - crippled one arm for two-handed weapon
|
||||
// 7 - both hands crippled
|
||||
if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1) {
|
||||
if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1) {
|
||||
return -1;
|
||||
}
|
||||
} else if (reason == COMBAT_BAD_SHOT_OUT_OF_RANGE) {
|
||||
// target out of range
|
||||
int accuracy = _determine_to_hit_no_range(a1, a2, HIT_LOCATION_UNCALLED, hitMode, rotations);
|
||||
if (accuracy < minToHit) {
|
||||
debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(a1));
|
||||
_ai_run_away(a1, a2);
|
||||
int toHitNoRange = _determine_to_hit_no_range(attacker, defender, HIT_LOCATION_UNCALLED, hitMode, rotations);
|
||||
if (toHitNoRange < minToHit) {
|
||||
// hit chance is too low even at point blank range (not taking range into account)
|
||||
debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(attacker));
|
||||
_ai_run_away(attacker, defender);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (weapon != NULL) {
|
||||
if (_ai_move_steps_closer(a1, a2, actionPoints, taunt) == -1) {
|
||||
if (_ai_move_steps_closer(attacker, defender, actionPoints, taunt) == -1) {
|
||||
return -1;
|
||||
}
|
||||
taunt = false;
|
||||
} else {
|
||||
if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1 || weapon == NULL) {
|
||||
if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1 || weapon == NULL) {
|
||||
// NOTE: Uninline.
|
||||
if (_ai_move_closer(a1, a2, taunt) == -1) {
|
||||
if (_ai_move_closer(attacker, defender, taunt) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -2805,66 +2830,66 @@ static int _ai_try_attack(Object* a1, Object* a2)
|
|||
}
|
||||
} else if (reason == COMBAT_BAD_SHOT_AIM_BLOCKED) {
|
||||
// aim is blocked
|
||||
if (_ai_move_steps_closer(a1, a2, a1->data.critter.combat.ap, taunt) == -1) {
|
||||
if (_ai_move_steps_closer(attacker, defender, attacker->data.critter.combat.ap, taunt) == -1) {
|
||||
return -1;
|
||||
}
|
||||
taunt = false;
|
||||
} else if (reason == COMBAT_BAD_SHOT_OK) {
|
||||
int accuracy = _determine_to_hit(a1, a2, HIT_LOCATION_UNCALLED, hitMode);
|
||||
int accuracy = _determine_to_hit(attacker, defender, HIT_LOCATION_UNCALLED, hitMode);
|
||||
if (safeDistance != 0) {
|
||||
if (_ai_move_away(a1, a2, safeDistance) == -1) {
|
||||
if (_ai_move_away(attacker, defender, safeDistance) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (accuracy < minToHit) {
|
||||
int accuracyNoRange = _determine_to_hit_no_range(a1, a2, HIT_LOCATION_UNCALLED, hitMode, rotations);
|
||||
if (accuracyNoRange < minToHit) {
|
||||
debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(a1));
|
||||
_ai_run_away(a1, a2);
|
||||
int toHitNoRange = _determine_to_hit_no_range(attacker, defender, HIT_LOCATION_UNCALLED, hitMode, rotations);
|
||||
if (toHitNoRange < minToHit) {
|
||||
debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(attacker));
|
||||
_ai_run_away(attacker, defender);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (actionPoints > 0) {
|
||||
int v24 = pathfinderFindPath(a1, a1->tile, a2->tile, rotations, 0, _obj_blocking_at);
|
||||
if (v24 == 0) {
|
||||
v42 = actionPoints;
|
||||
int pathLength = pathfinderFindPath(attacker, attacker->tile, defender->tile, rotations, 0, _obj_blocking_at);
|
||||
if (pathLength == 0) {
|
||||
actionPointsToUse = actionPoints;
|
||||
} else {
|
||||
if (v24 < actionPoints) {
|
||||
actionPoints = v24;
|
||||
if (pathLength < actionPoints) {
|
||||
actionPoints = pathLength;
|
||||
}
|
||||
|
||||
int tile = a1->tile;
|
||||
int tile = attacker->tile;
|
||||
int index;
|
||||
for (index = 0; index < actionPoints; index++) {
|
||||
tile = tileGetTileInDirection(tile, rotations[index], 1);
|
||||
|
||||
v42++;
|
||||
actionPointsToUse++;
|
||||
|
||||
int v27 = _determine_to_hit_from_tile(a1, tile, a2, HIT_LOCATION_UNCALLED, hitMode);
|
||||
if (v27 >= minToHit) {
|
||||
int toHit = _determine_to_hit_from_tile(attacker, tile, defender, HIT_LOCATION_UNCALLED, hitMode);
|
||||
if (toHit >= minToHit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == actionPoints) {
|
||||
v42 = actionPoints;
|
||||
actionPointsToUse = actionPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_ai_move_steps_closer(a1, a2, v42, taunt) == -1) {
|
||||
debugPrint("%s: FLEEING: Can't possibly get closer to Target!", critterGetName(a1));
|
||||
_ai_run_away(a1, a2);
|
||||
if (_ai_move_steps_closer(attacker, defender, actionPointsToUse, taunt) == -1) {
|
||||
debugPrint("%s: FLEEING: Can't possibly get closer to Target!", critterGetName(attacker));
|
||||
_ai_run_away(attacker, defender);
|
||||
return 0;
|
||||
}
|
||||
|
||||
taunt = false;
|
||||
if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) {
|
||||
if (_ai_attack(attacker, defender, hitMode) == -1 || weaponGetActionPointCost(attacker, hitMode, 0) > attacker->data.critter.combat.ap) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) {
|
||||
if (_ai_attack(attacker, defender, hitMode) == -1 || weaponGetActionPointCost(attacker, hitMode, 0) > attacker->data.critter.combat.ap) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ void aiReset();
|
|||
int aiExit();
|
||||
int aiLoad(File* stream);
|
||||
int aiSave(File* stream);
|
||||
int combat_ai_num();
|
||||
char* combat_ai_name(int packet_num);
|
||||
int aiGetAreaAttackMode(Object* obj);
|
||||
int aiGetRunAwayMode(Object* obj);
|
||||
int aiGetBestWeapon(Object* obj);
|
||||
|
|
|
@ -280,24 +280,28 @@ bool configRead(Config* config, const char* filePath, bool isDb)
|
|||
|
||||
if (isDb) {
|
||||
File* stream = fileOpen(filePath, "rb");
|
||||
if (stream != NULL) {
|
||||
while (fileReadString(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
fileClose(stream);
|
||||
|
||||
// CE: Return `false` if file does not exists in database.
|
||||
if (stream == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (fileReadString(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
fileClose(stream);
|
||||
} else {
|
||||
FILE* stream = compat_fopen(filePath, "rt");
|
||||
if (stream != NULL) {
|
||||
while (compat_fgets(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
// CE: Return `false` if file does not exists on the file system.
|
||||
if (stream == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: This function returns `true` even if the file was not actually
|
||||
// read. I'm pretty sure it's bug.
|
||||
while (compat_fgets(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
fclose(stream);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -393,7 +397,7 @@ static bool configParseLine(Config* config, char* string)
|
|||
// keys there.
|
||||
|
||||
// Skip leading whitespace.
|
||||
while (isspace(*string)) {
|
||||
while (isspace(static_cast<unsigned char>(*string))) {
|
||||
string++;
|
||||
}
|
||||
|
||||
|
@ -496,7 +500,7 @@ static bool configTrimString(char* string)
|
|||
// Starting from the end of the string, loop while it's a whitespace and
|
||||
// decrement string length.
|
||||
char* pch = string + length - 1;
|
||||
while (length != 0 && isspace(*pch)) {
|
||||
while (length != 0 && isspace(static_cast<unsigned char>(*pch))) {
|
||||
length--;
|
||||
pch--;
|
||||
}
|
||||
|
@ -507,7 +511,7 @@ static bool configTrimString(char* string)
|
|||
// Starting from the beginning of the string loop while it's a whitespace
|
||||
// and decrement string length.
|
||||
pch = string;
|
||||
while (isspace(*pch)) {
|
||||
while (isspace(static_cast<unsigned char>(*pch))) {
|
||||
pch++;
|
||||
length--;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "cycle.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "draw.h"
|
||||
#include "game_mouse.h"
|
||||
#include "input.h"
|
||||
|
@ -172,8 +173,7 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
|
|||
windowBuffer,
|
||||
windowWidth);
|
||||
|
||||
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
|
||||
}
|
||||
delay_ms(CREDITS_WINDOW_SCROLLING_DELAY - (getTicks() - tick));
|
||||
|
||||
tick = getTicks();
|
||||
|
||||
|
@ -215,8 +215,7 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
|
|||
windowBuffer,
|
||||
windowWidth);
|
||||
|
||||
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
|
||||
}
|
||||
delay_ms(CREDITS_WINDOW_SCROLLING_DELAY - (getTicks() - tick));
|
||||
|
||||
tick = getTicks();
|
||||
|
||||
|
|
|
@ -1396,4 +1396,40 @@ bool _critter_flag_check(int pid, int flag)
|
|||
return (proto->critter.data.flags & flag) != 0;
|
||||
}
|
||||
|
||||
// 0x42E6F0
|
||||
void critter_flag_set(int pid, int flag)
|
||||
{
|
||||
Proto* proto;
|
||||
|
||||
if (pid == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PID_TYPE(pid) != OBJ_TYPE_CRITTER) {
|
||||
return;
|
||||
}
|
||||
|
||||
protoGetProto(pid, &proto);
|
||||
|
||||
proto->critter.data.flags |= flag;
|
||||
}
|
||||
|
||||
// 0x42E71C
|
||||
void critter_flag_unset(int pid, int flag)
|
||||
{
|
||||
Proto* proto;
|
||||
|
||||
if (pid == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PID_TYPE(pid) != OBJ_TYPE_CRITTER) {
|
||||
return;
|
||||
}
|
||||
|
||||
protoGetProto(pid, &proto);
|
||||
|
||||
proto->critter.data.flags &= ~flag;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -70,6 +70,8 @@ int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int a2);
|
|||
bool critterIsEncumbered(Object* critter);
|
||||
bool critterIsFleeing(Object* a1);
|
||||
bool _critter_flag_check(int pid, int flag);
|
||||
void critter_flag_set(int pid, int flag);
|
||||
void critter_flag_unset(int pid, int flag);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
|
14
src/dbox.cc
14
src/dbox.cc
|
@ -9,6 +9,7 @@
|
|||
#include "character_editor.h"
|
||||
#include "color.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_sound.h"
|
||||
|
@ -885,8 +886,8 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
|||
}
|
||||
|
||||
unsigned int delay = (scrollCounter > 14.4) ? 1000 / scrollDelay : 1000 / 24;
|
||||
while (getTicksSince(scrollTick) < delay) {
|
||||
}
|
||||
|
||||
delay_ms(delay - (getTicks() - scrollTick));
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
rc = 1;
|
||||
|
@ -909,8 +910,7 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
|||
doubleClickSelectedFileIndex = -2;
|
||||
}
|
||||
|
||||
while (getTicksSince(tick) < (1000 / 24)) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - tick));
|
||||
}
|
||||
|
||||
if (_game_user_wants_to_quit) {
|
||||
|
@ -1335,8 +1335,7 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
|||
// FIXME: Missing windowRefresh makes blinking useless.
|
||||
|
||||
unsigned int delay = (scrollCounter > 14.4) ? 1000 / scrollDelay : 1000 / 24;
|
||||
while (getTicksSince(scrollTick) < delay) {
|
||||
}
|
||||
delay_ms(delay - (getTicks() - scrollTick));
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
rc = 1;
|
||||
|
@ -1369,8 +1368,7 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
|||
doubleClickSelectedFileIndex = -2;
|
||||
}
|
||||
|
||||
while (getTicksSince(tick) < (1000 / 24)) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - tick));
|
||||
}
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
|
|
12
src/debug.cc
12
src/debug.cc
|
@ -56,7 +56,7 @@ void _debug_register_mono()
|
|||
// 0x4C6D18
|
||||
void _debug_register_log(const char* fileName, const char* mode)
|
||||
{
|
||||
if ((mode[0] == 'w' && mode[1] == 'a') && mode[1] == 't') {
|
||||
if ((mode[0] == 'w' || mode[0] == 'a') && mode[1] == 't') {
|
||||
if (_fd != NULL) {
|
||||
fclose(_fd);
|
||||
}
|
||||
|
@ -104,14 +104,8 @@ void _debug_register_env()
|
|||
// NOTE: Uninline.
|
||||
_debug_register_screen();
|
||||
} else if (strcmp(copy, "gnw") == 0) {
|
||||
if (gDebugPrintProc != _win_debug) {
|
||||
if (_fd != NULL) {
|
||||
fclose(_fd);
|
||||
_fd = NULL;
|
||||
}
|
||||
|
||||
gDebugPrintProc = _win_debug;
|
||||
}
|
||||
// NOTE: Uninline.
|
||||
_debug_register_func(_win_debug);
|
||||
}
|
||||
|
||||
internal_free(copy);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#include "delay.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
void delay_ms(int ms)
|
||||
{
|
||||
if (ms <= 0) {
|
||||
return;
|
||||
}
|
||||
SDL_Delay(ms);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef DELAY_H
|
||||
#define DELAY_H
|
||||
|
||||
void delay_ms(int ms);
|
||||
|
||||
#endif
|
|
@ -27,7 +27,7 @@ void bufferDrawLine(unsigned char* buf, int pitch, int x1, int y1, int x2, int y
|
|||
|
||||
p1 = buf + pitch * y1 + x1;
|
||||
p2 = buf + pitch * y2 + x2;
|
||||
while (p1 < p2) {
|
||||
while (p1 <= p2) {
|
||||
*p1 = color;
|
||||
*p2 = color;
|
||||
p1 += pitch;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "art.h"
|
||||
#include "cycle.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "draw.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
|
@ -453,8 +454,7 @@ int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tileP
|
|||
|
||||
windowRefresh(gElevatorWindow);
|
||||
|
||||
while (getTicksSince(tick) < delay) {
|
||||
}
|
||||
delay_ms(delay - (getTicks() - tick));
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
|
|
|
@ -150,16 +150,8 @@ int _gzdecompress_file(const char* existingFilePath, const char* newFilePath)
|
|||
|
||||
static void fileCopy(const char* existingFilePath, const char* newFilePath)
|
||||
{
|
||||
char nativeExistingFilePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativeExistingFilePath, existingFilePath);
|
||||
compat_windows_path_to_native(nativeExistingFilePath);
|
||||
|
||||
char nativeNewFilePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativeNewFilePath, newFilePath);
|
||||
compat_windows_path_to_native(nativeNewFilePath);
|
||||
|
||||
FILE* in = fopen(nativeExistingFilePath, "rb");
|
||||
FILE* out = fopen(nativeNewFilePath, "wb");
|
||||
FILE* in = compat_fopen(existingFilePath, "rb");
|
||||
FILE* out = compat_fopen(newFilePath, "wb");
|
||||
if (in != NULL && out != NULL) {
|
||||
std::vector<unsigned char> buffer(0xFFFF);
|
||||
|
||||
|
|
44
src/game.cc
44
src/game.cc
|
@ -50,8 +50,11 @@
|
|||
#include "random.h"
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_arrays.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "sfall_global_vars.h"
|
||||
#include "sfall_ini.h"
|
||||
#include "sfall_lists.h"
|
||||
#include "skill.h"
|
||||
#include "skilldex.h"
|
||||
|
@ -62,6 +65,7 @@
|
|||
#include "trait.h"
|
||||
#include "version.h"
|
||||
#include "window_manager.h"
|
||||
#include "window_manager_private.h"
|
||||
#include "worldmap.h"
|
||||
|
||||
namespace fallout {
|
||||
|
@ -165,6 +169,20 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
|||
showSplash();
|
||||
}
|
||||
|
||||
// CE: Handle debug mode (exactly as seen in `mapper2.exe`).
|
||||
const char* debugMode = settings.debug.mode.c_str();
|
||||
if (compat_stricmp(debugMode, "environment") == 0) {
|
||||
_debug_register_env();
|
||||
} else if (compat_stricmp(debugMode, "screen") == 0) {
|
||||
_debug_register_screen();
|
||||
} else if (compat_stricmp(debugMode, "log") == 0) {
|
||||
_debug_register_log("debug.log", "wt");
|
||||
} else if (compat_stricmp(debugMode, "mono") == 0) {
|
||||
_debug_register_mono();
|
||||
} else if (compat_stricmp(debugMode, "gnw") == 0) {
|
||||
_debug_register_func(_win_debug);
|
||||
}
|
||||
|
||||
interfaceFontsInit();
|
||||
fontManagerAdd(&gModernFontManager);
|
||||
fontSetCurrent(font);
|
||||
|
@ -338,8 +356,8 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
|||
// SFALL
|
||||
premadeCharactersInit();
|
||||
|
||||
if (!sfallGlobalVarsInit()) {
|
||||
debugPrint("Failed on sfallGlobalVarsInit");
|
||||
if (!sfall_gl_vars_init()) {
|
||||
debugPrint("Failed on sfall_gl_vars_init");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -348,6 +366,20 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (!sfallArraysInit()) {
|
||||
debugPrint("Failed on sfallArraysInit");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sfall_gl_scr_init()) {
|
||||
debugPrint("Failed on sfall_gl_scr_init");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* customConfigBasePath;
|
||||
configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, &customConfigBasePath);
|
||||
sfall_ini_set_base_path(customConfigBasePath);
|
||||
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList);
|
||||
|
||||
return 0;
|
||||
|
@ -392,9 +424,11 @@ void gameReset()
|
|||
_init_options_menu();
|
||||
|
||||
// SFALL
|
||||
sfallGlobalVarsReset();
|
||||
sfall_gl_vars_reset();
|
||||
sfallListsReset();
|
||||
messageListRepositoryReset();
|
||||
sfallArraysReset();
|
||||
sfall_gl_scr_reset();
|
||||
}
|
||||
|
||||
// 0x442C34
|
||||
|
@ -403,8 +437,10 @@ void gameExit()
|
|||
debugPrint("\nGame Exit\n");
|
||||
|
||||
// SFALL
|
||||
sfall_gl_scr_exit();
|
||||
sfallArraysExit();
|
||||
sfallListsExit();
|
||||
sfallGlobalVarsExit();
|
||||
sfall_gl_vars_exit();
|
||||
premadeCharactersExit();
|
||||
|
||||
tileDisable();
|
||||
|
|
|
@ -125,10 +125,26 @@ bool gameConfigInit(bool isMapper, int argc, char** argv)
|
|||
char* ch = strrchr(executable, '\\');
|
||||
if (ch != NULL) {
|
||||
*ch = '\0';
|
||||
snprintf(gGameConfigFilePath, sizeof(gGameConfigFilePath), "%s\\%s", executable, GAME_CONFIG_FILE_NAME);
|
||||
if (isMapper) {
|
||||
snprintf(gGameConfigFilePath,
|
||||
sizeof(gGameConfigFilePath),
|
||||
"%s\\%s",
|
||||
executable,
|
||||
MAPPER_CONFIG_FILE_NAME);
|
||||
} else {
|
||||
snprintf(gGameConfigFilePath,
|
||||
sizeof(gGameConfigFilePath),
|
||||
"%s\\%s",
|
||||
executable,
|
||||
GAME_CONFIG_FILE_NAME);
|
||||
}
|
||||
*ch = '\\';
|
||||
} else {
|
||||
strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME);
|
||||
if (isMapper) {
|
||||
strcpy(gGameConfigFilePath, MAPPER_CONFIG_FILE_NAME);
|
||||
} else {
|
||||
strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Read contents of `fallout2.cfg` into config. The values from the file
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
namespace fallout {
|
||||
|
||||
// The file name of the main config file.
|
||||
#define GAME_CONFIG_FILE_NAME "fallout2.cfg"
|
||||
#define MAPPER_CONFIG_FILE_NAME "mapper2.cfg"
|
||||
|
||||
#define GAME_CONFIG_SYSTEM_KEY "system"
|
||||
#define GAME_CONFIG_PREFERENCES_KEY "preferences"
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "critter.h"
|
||||
#include "cycle.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "dialog.h"
|
||||
#include "display_monitor.h"
|
||||
#include "draw.h"
|
||||
|
@ -2936,7 +2937,6 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
|
|||
int v7;
|
||||
unsigned char* v9;
|
||||
Rect rect;
|
||||
unsigned int tick;
|
||||
|
||||
v7 = a6;
|
||||
v9 = a4;
|
||||
|
@ -2971,9 +2971,7 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
|
|||
v7 += 10;
|
||||
v9 -= 10 * (GAME_DIALOG_WINDOW_WIDTH);
|
||||
|
||||
tick = getTicks();
|
||||
while (getTicksSince(tick) < 33) {
|
||||
}
|
||||
delay_ms(33);
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
|
@ -3011,9 +3009,7 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
|
|||
|
||||
rect.top += 10;
|
||||
|
||||
tick = getTicks();
|
||||
while (getTicksSince(tick) < 33) {
|
||||
}
|
||||
delay_ms(33);
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
|
|
|
@ -1328,6 +1328,12 @@ int gameMouseGetCursor()
|
|||
return gGameMouseCursor;
|
||||
}
|
||||
|
||||
// 0x44C9F0
|
||||
void gmouse_set_mapper_mode(int mode)
|
||||
{
|
||||
_gmouse_mapper_mode = mode;
|
||||
}
|
||||
|
||||
// 0x44C9F8
|
||||
void _gmouse_3d_enable_modes()
|
||||
{
|
||||
|
@ -2458,4 +2464,9 @@ void gameMouseRefreshImmediately()
|
|||
renderPresent();
|
||||
}
|
||||
|
||||
Object* gmouse_get_outlined_object()
|
||||
{
|
||||
return gGameMouseHighlightedItem;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -86,6 +86,7 @@ void gameMouseRefresh();
|
|||
void _gmouse_handle_event(int mouseX, int mouseY, int mouseState);
|
||||
int gameMouseSetCursor(int cursor);
|
||||
int gameMouseGetCursor();
|
||||
void gmouse_set_mapper_mode(int mode);
|
||||
void gameMouseSetMode(int a1);
|
||||
int gameMouseGetMode();
|
||||
void gameMouseCycleMode();
|
||||
|
@ -102,6 +103,7 @@ void gameMouseLoadItemHighlight();
|
|||
void _gmouse_remove_item_outline(Object* object);
|
||||
|
||||
void gameMouseRefreshImmediately();
|
||||
Object* gmouse_get_outlined_object();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
|
|
@ -1370,7 +1370,7 @@ char* gameSoundBuildInterfaceName(const char* a1)
|
|||
// 0x451760
|
||||
char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* target)
|
||||
{
|
||||
int v6;
|
||||
int soundVariant;
|
||||
char weaponSoundCode;
|
||||
char effectTypeCode;
|
||||
char materialCode;
|
||||
|
@ -1384,12 +1384,12 @@ char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* ta
|
|||
if (hitMode != HIT_MODE_LEFT_WEAPON_PRIMARY
|
||||
&& hitMode != HIT_MODE_RIGHT_WEAPON_PRIMARY
|
||||
&& hitMode != HIT_MODE_PUNCH) {
|
||||
v6 = 2;
|
||||
soundVariant = 2;
|
||||
} else {
|
||||
v6 = 1;
|
||||
soundVariant = 1;
|
||||
}
|
||||
} else {
|
||||
v6 = 1;
|
||||
soundVariant = 1;
|
||||
}
|
||||
|
||||
int damageType = weaponGetDamageType(NULL, weapon);
|
||||
|
@ -1438,7 +1438,7 @@ char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* ta
|
|||
}
|
||||
}
|
||||
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, v6, materialCode, 1);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, soundVariant, materialCode, 1);
|
||||
compat_strupr(_sfx_file_name);
|
||||
return _sfx_file_name;
|
||||
}
|
||||
|
|
|
@ -107,6 +107,62 @@ void _rect_clip_list(RectListNode** rectListNodePtr, Rect* rect)
|
|||
}
|
||||
}
|
||||
|
||||
// 0x4C6AAC
|
||||
RectListNode* rect_clip(Rect* b, Rect* t)
|
||||
{
|
||||
RectListNode* list = NULL;
|
||||
Rect clipped_t;
|
||||
|
||||
if (rectIntersection(t, b, &clipped_t) == 0) {
|
||||
RectListNode** next = &list;
|
||||
Rect clipped_b[4];
|
||||
int k;
|
||||
|
||||
clipped_b[0].left = b->left;
|
||||
clipped_b[0].top = b->top;
|
||||
clipped_b[0].right = b->right;
|
||||
clipped_b[0].bottom = clipped_t.top - 1;
|
||||
|
||||
clipped_b[1].left = b->left;
|
||||
clipped_b[1].top = clipped_t.top;
|
||||
clipped_b[1].right = clipped_t.left - 1;
|
||||
clipped_b[1].bottom = clipped_t.bottom;
|
||||
|
||||
clipped_b[2].left = clipped_t.right + 1;
|
||||
clipped_b[2].top = clipped_t.top;
|
||||
clipped_b[2].right = b->right;
|
||||
clipped_b[2].bottom = clipped_t.bottom;
|
||||
|
||||
clipped_b[3].left = b->left;
|
||||
clipped_b[3].top = clipped_t.bottom + 1;
|
||||
clipped_b[3].right = b->right;
|
||||
clipped_b[3].bottom = b->bottom;
|
||||
|
||||
for (k = 0; k < 4; k++) {
|
||||
if (clipped_b[k].left <= clipped_b[k].right && clipped_b[k].top <= clipped_b[k].bottom) {
|
||||
*next = _rect_malloc();
|
||||
if (*next == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*next)->rect = clipped_b[k];
|
||||
(*next)->next = NULL;
|
||||
next = &((*next)->next);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list = _rect_malloc();
|
||||
if (list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list->rect = *b;
|
||||
list->next = NULL;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// 0x4C6BB8
|
||||
RectListNode* _rect_malloc()
|
||||
{
|
||||
|
|
|
@ -27,6 +27,7 @@ typedef struct RectListNode {
|
|||
|
||||
void _GNW_rect_exit();
|
||||
void _rect_clip_list(RectListNode** rectListNodePtr, Rect* rect);
|
||||
RectListNode* rect_clip(Rect* b, Rect* t);
|
||||
RectListNode* _rect_malloc();
|
||||
void _rect_free(RectListNode* entry);
|
||||
void rectUnion(const Rect* s1, const Rect* s2, Rect* r);
|
||||
|
|
|
@ -52,6 +52,14 @@ unsigned char HighRGB(unsigned char color)
|
|||
return std::max(std::max(r, g), b);
|
||||
}
|
||||
|
||||
// 0x44ED98
|
||||
int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7)
|
||||
{
|
||||
// TODO: Incomplete.
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x44F250
|
||||
int graphCompress(unsigned char* a1, unsigned char* a2, int a3)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
namespace fallout {
|
||||
|
||||
unsigned char HighRGB(unsigned char color);
|
||||
int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7);
|
||||
int graphCompress(unsigned char* a1, unsigned char* a2, int a3);
|
||||
int graphDecompress(unsigned char* a1, unsigned char* a2, int a3);
|
||||
void grayscalePaletteUpdate(int a1, int a2);
|
||||
|
|
15
src/input.cc
15
src/input.cc
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "audio_engine.h"
|
||||
#include "color.h"
|
||||
#include "delay.h"
|
||||
#include "dinput.h"
|
||||
#include "draw.h"
|
||||
#include "kb.h"
|
||||
|
@ -199,6 +200,13 @@ int inputGetInput()
|
|||
return -1;
|
||||
}
|
||||
|
||||
// 0x4C8BC8
|
||||
void get_input_position(int* x, int* y)
|
||||
{
|
||||
*x = _input_mx;
|
||||
*y = _input_my;
|
||||
}
|
||||
|
||||
// 0x4C8BDC
|
||||
void _process_bk()
|
||||
{
|
||||
|
@ -634,12 +642,7 @@ void inputPauseForTocks(unsigned int delay)
|
|||
// 0x4C93B8
|
||||
void inputBlockForTocks(unsigned int ms)
|
||||
{
|
||||
unsigned int start = SDL_GetTicks();
|
||||
unsigned int diff;
|
||||
do {
|
||||
// NOTE: Uninline
|
||||
diff = getTicksSince(start);
|
||||
} while (diff < ms);
|
||||
delay_ms(ms);
|
||||
}
|
||||
|
||||
// 0x4C93E0
|
||||
|
|
|
@ -13,6 +13,7 @@ typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, uns
|
|||
int inputInit(int a1);
|
||||
void inputExit();
|
||||
int inputGetInput();
|
||||
void get_input_position(int* x, int* y);
|
||||
void _process_bk();
|
||||
void enqueueInputEvent(int a1);
|
||||
void inputEventQueueReset();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "interpreter_lib.h"
|
||||
#include "memory_manager.h"
|
||||
#include "platform_compat.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
@ -30,20 +31,19 @@ static int _outputStr(char* a1);
|
|||
static int _checkWait(Program* program);
|
||||
static char* programGetCurrentProcedureName(Program* s);
|
||||
static opcode_t stackReadInt16(unsigned char* data, int pos);
|
||||
static int stackReadInt32(unsigned char* a1, int a2);
|
||||
static void stackWriteInt16(int value, unsigned char* a2, int a3);
|
||||
static void stackWriteInt32(int value, unsigned char* stack, int pos);
|
||||
static void stackPushInt16(unsigned char* a1, int* a2, int value);
|
||||
static void stackPushInt32(unsigned char* a1, int* a2, int value);
|
||||
static int stackPopInt32(unsigned char* a1, int* a2);
|
||||
static opcode_t stackPopInt16(unsigned char* a1, int* a2);
|
||||
static int stackReadInt32(unsigned char* data, int pos);
|
||||
static void stackWriteInt16(int value, unsigned char* data, int pos);
|
||||
static void stackWriteInt32(int value, unsigned char* data, int pos);
|
||||
static void stackPushInt16(unsigned char* data, int* pointer, int value);
|
||||
static void stackPushInt32(unsigned char* data, int* pointer, int value);
|
||||
static int stackPopInt32(unsigned char* data, int* pointer);
|
||||
static opcode_t stackPopInt16(unsigned char* data, int* pointer);
|
||||
static void _interpretIncStringRef(Program* program, opcode_t opcode, int value);
|
||||
static void programReturnStackPushInt16(Program* program, int value);
|
||||
static opcode_t programReturnStackPopInt16(Program* program);
|
||||
static int programReturnStackPopInt32(Program* program);
|
||||
static void _detachProgram(Program* program);
|
||||
static void _purgeProgram(Program* program);
|
||||
static void programFree(Program* program);
|
||||
static opcode_t _getOp(Program* program);
|
||||
static void programMarkHeap(Program* program);
|
||||
static void opNoop(Program* program);
|
||||
|
@ -51,7 +51,7 @@ static void opPush(Program* program);
|
|||
static void opPushBase(Program* program);
|
||||
static void opPopBase(Program* program);
|
||||
static void opPopToBase(Program* program);
|
||||
static void op802C(Program* program);
|
||||
static void opSetGlobal(Program* program);
|
||||
static void opDump(Program* program);
|
||||
static void opDelayedCall(Program* program);
|
||||
static void opConditionalCall(Program* program);
|
||||
|
@ -230,17 +230,17 @@ static char* programGetCurrentProcedureName(Program* program)
|
|||
int procedureCount = stackReadInt32(program->procedures, 0);
|
||||
unsigned char* ptr = program->procedures + 4;
|
||||
|
||||
int procedureOffset = stackReadInt32(ptr, 16);
|
||||
int identifierOffset = stackReadInt32(ptr, 0);
|
||||
int procedureOffset = stackReadInt32(ptr, offsetof(Procedure, bodyOffset));
|
||||
int identifierOffset = stackReadInt32(ptr, offsetof(Procedure, nameOffset));
|
||||
|
||||
for (int index = 0; index < procedureCount; index++) {
|
||||
int nextProcedureOffset = stackReadInt32(ptr + 24, 16);
|
||||
int nextProcedureOffset = stackReadInt32(ptr + 24, offsetof(Procedure, bodyOffset));
|
||||
if (program->instructionPointer >= procedureOffset && program->instructionPointer < nextProcedureOffset) {
|
||||
return (char*)(program->identifiers + identifierOffset);
|
||||
}
|
||||
|
||||
ptr += 24;
|
||||
identifierOffset = stackReadInt32(ptr, 0);
|
||||
identifierOffset = stackReadInt32(ptr, offsetof(Procedure, nameOffset));
|
||||
}
|
||||
|
||||
return _aCouldnTFindPro;
|
||||
|
@ -421,7 +421,7 @@ static void _purgeProgram(Program* program)
|
|||
}
|
||||
|
||||
// 0x467614
|
||||
static void programFree(Program* program)
|
||||
void programFree(Program* program)
|
||||
{
|
||||
// NOTE: Uninline.
|
||||
_detachProgram(program);
|
||||
|
@ -712,7 +712,7 @@ static void opPopToBase(Program* program)
|
|||
}
|
||||
|
||||
// 0x467DE0
|
||||
static void op802C(Program* program)
|
||||
static void opSetGlobal(Program* program)
|
||||
{
|
||||
program->basePointer = program->stackValues->size();
|
||||
}
|
||||
|
@ -745,10 +745,10 @@ static void opDelayedCall(Program* program)
|
|||
delay += 1000 * _timerFunc() / _timerTick;
|
||||
}
|
||||
|
||||
int flags = stackReadInt32(procedure_ptr, 4);
|
||||
int flags = stackReadInt32(procedure_ptr, offsetof(Procedure, flags));
|
||||
|
||||
stackWriteInt32(delay, procedure_ptr, 8);
|
||||
stackWriteInt32(flags | PROCEDURE_FLAG_TIMED, procedure_ptr, 4);
|
||||
stackWriteInt32(delay, procedure_ptr, offsetof(Procedure, time));
|
||||
stackWriteInt32(flags | PROCEDURE_FLAG_TIMED, procedure_ptr, offsetof(Procedure, flags));
|
||||
}
|
||||
|
||||
// 0x468034
|
||||
|
@ -761,10 +761,10 @@ static void opConditionalCall(Program* program)
|
|||
}
|
||||
|
||||
unsigned char* procedure_ptr = program->procedures + 4 + 24 * data[0];
|
||||
int flags = stackReadInt32(procedure_ptr, 4);
|
||||
int flags = stackReadInt32(procedure_ptr, offsetof(Procedure, flags));
|
||||
|
||||
stackWriteInt32(flags | PROCEDURE_FLAG_CONDITIONAL, procedure_ptr, 4);
|
||||
stackWriteInt32(data[1], procedure_ptr, 12);
|
||||
stackWriteInt32(flags | PROCEDURE_FLAG_CONDITIONAL, procedure_ptr, offsetof(Procedure, flags));
|
||||
stackWriteInt32(data[1], procedure_ptr, offsetof(Procedure, conditionOffset));
|
||||
}
|
||||
|
||||
// 0x46817C
|
||||
|
@ -788,9 +788,9 @@ static void opCancel(Program* program)
|
|||
}
|
||||
|
||||
Procedure* proc = (Procedure*)(program->procedures + 4 + data * sizeof(*proc));
|
||||
proc->field_4 = 0;
|
||||
proc->field_8 = 0;
|
||||
proc->field_C = 0;
|
||||
proc->flags = 0;
|
||||
proc->time = 0;
|
||||
proc->conditionOffset = 0;
|
||||
}
|
||||
|
||||
// 0x468330
|
||||
|
@ -802,9 +802,9 @@ static void opCancelAll(Program* program)
|
|||
// TODO: Original code uses different approach, check.
|
||||
Procedure* proc = (Procedure*)(program->procedures + 4 + index * sizeof(*proc));
|
||||
|
||||
proc->field_4 = 0;
|
||||
proc->field_8 = 0;
|
||||
proc->field_C = 0;
|
||||
proc->flags = 0;
|
||||
proc->time = 0;
|
||||
proc->conditionOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2040,12 +2040,12 @@ static void opCall(Program* program)
|
|||
|
||||
unsigned char* ptr = program->procedures + 4 + 24 * value;
|
||||
|
||||
int flags = stackReadInt32(ptr, 4);
|
||||
if ((flags & 4) != 0) {
|
||||
int flags = stackReadInt32(ptr, offsetof(Procedure, flags));
|
||||
if ((flags & PROCEDURE_FLAG_IMPORTED) != 0) {
|
||||
// TODO: Incomplete.
|
||||
} else {
|
||||
program->instructionPointer = stackReadInt32(ptr, 16);
|
||||
if ((flags & 0x10) != 0) {
|
||||
program->instructionPointer = stackReadInt32(ptr, offsetof(Procedure, bodyOffset));
|
||||
if ((flags & PROCEDURE_FLAG_CRITICAL) != 0) {
|
||||
program->flags |= PROGRAM_FLAG_CRITICAL_SECTION;
|
||||
}
|
||||
}
|
||||
|
@ -2248,7 +2248,7 @@ static void opFetchProcedureAddress(Program* program)
|
|||
{
|
||||
int procedureIndex = programStackPopInteger(program);
|
||||
|
||||
int address = stackReadInt32(program->procedures + 4 + sizeof(Procedure) * procedureIndex, 16);
|
||||
int address = stackReadInt32(program->procedures + 4 + sizeof(Procedure) * procedureIndex, offsetof(Procedure, bodyOffset));
|
||||
programStackPushInteger(program, address);
|
||||
}
|
||||
|
||||
|
@ -2308,8 +2308,8 @@ static void opExportProcedure(Program* program)
|
|||
|
||||
unsigned char* proc_ptr = program->procedures + 4 + sizeof(Procedure) * procedureIndex;
|
||||
|
||||
char* procedureName = programGetIdentifier(program, stackReadInt32(proc_ptr, 0));
|
||||
int procedureAddress = stackReadInt32(proc_ptr, 16);
|
||||
char* procedureName = programGetIdentifier(program, stackReadInt32(proc_ptr, offsetof(Procedure, nameOffset)));
|
||||
int procedureAddress = stackReadInt32(proc_ptr, offsetof(Procedure, bodyOffset));
|
||||
|
||||
if (externalProcedureCreate(program, procedureName, procedureAddress, argumentCount) != 0) {
|
||||
char err[256];
|
||||
|
@ -2478,9 +2478,9 @@ static void opCheckProcedureArgumentCount(Program* program)
|
|||
int expectedArgumentCount = programStackPopInteger(program);
|
||||
int procedureIndex = programStackPopInteger(program);
|
||||
|
||||
int actualArgumentCount = stackReadInt32(program->procedures + 4 + 24 * procedureIndex, 20);
|
||||
int actualArgumentCount = stackReadInt32(program->procedures + 4 + 24 * procedureIndex, offsetof(Procedure, argCount));
|
||||
if (actualArgumentCount != expectedArgumentCount) {
|
||||
const char* identifier = programGetIdentifier(program, stackReadInt32(program->procedures + 4 + 24 * procedureIndex, 0));
|
||||
const char* identifier = programGetIdentifier(program, stackReadInt32(program->procedures + 4 + 24 * procedureIndex, offsetof(Procedure, nameOffset)));
|
||||
char err[260];
|
||||
snprintf(err, sizeof(err), "Wrong number of args to procedure %s\n", identifier);
|
||||
programFatalError(err);
|
||||
|
@ -2501,7 +2501,7 @@ static void opLookupStringProc(Program* program)
|
|||
// Start with 1 since we've skipped main procedure, which is always at
|
||||
// index 0.
|
||||
for (int index = 1; index < procedureCount; index++) {
|
||||
int offset = stackReadInt32(procedurePtr, 0);
|
||||
int offset = stackReadInt32(procedurePtr, offsetof(Procedure, nameOffset));
|
||||
const char* procedureName = programGetIdentifier(program, offset);
|
||||
if (compat_stricmp(procedureName, procedureNameToLookup) == 0) {
|
||||
programStackPushInteger(program, index);
|
||||
|
@ -2566,7 +2566,7 @@ void interpreterRegisterOpcodeHandlers()
|
|||
interpreterRegisterOpcode(OPCODE_POP_BASE, opPopBase);
|
||||
interpreterRegisterOpcode(OPCODE_POP_TO_BASE, opPopToBase);
|
||||
interpreterRegisterOpcode(OPCODE_PUSH_BASE, opPushBase);
|
||||
interpreterRegisterOpcode(OPCODE_SET_GLOBAL, op802C);
|
||||
interpreterRegisterOpcode(OPCODE_SET_GLOBAL, opSetGlobal);
|
||||
interpreterRegisterOpcode(OPCODE_FETCH_PROCEDURE_ADDRESS, opFetchProcedureAddress);
|
||||
interpreterRegisterOpcode(OPCODE_DUMP, opDump);
|
||||
interpreterRegisterOpcode(OPCODE_IF, opIf);
|
||||
|
@ -2786,9 +2786,9 @@ void _executeProc(Program* program, int procedureIndex)
|
|||
char err[256];
|
||||
|
||||
procedurePtr = program->procedures + 4 + sizeof(Procedure) * procedureIndex;
|
||||
procedureFlags = stackReadInt32(procedurePtr, 4);
|
||||
procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags));
|
||||
if ((procedureFlags & PROCEDURE_FLAG_IMPORTED) != 0) {
|
||||
procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, 0));
|
||||
procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, offsetof(Procedure, nameOffset)));
|
||||
externalProgram = externalProcedureGetProgram(procedureIdentifier, &externalProcedureAddress, &externalProcedureArgumentCount);
|
||||
if (externalProgram != NULL) {
|
||||
if (externalProcedureArgumentCount == 0) {
|
||||
|
@ -2805,7 +2805,7 @@ void _executeProc(Program* program, int procedureIndex)
|
|||
_setupExternalCall(program, externalProgram, externalProcedureAddress, 28);
|
||||
|
||||
procedurePtr = externalProgram->procedures + 4 + sizeof(Procedure) * procedureIndex;
|
||||
procedureFlags = stackReadInt32(procedurePtr, 4);
|
||||
procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags));
|
||||
|
||||
if ((procedureFlags & PROCEDURE_FLAG_CRITICAL) != 0) {
|
||||
// NOTE: Uninline.
|
||||
|
@ -2813,7 +2813,7 @@ void _executeProc(Program* program, int procedureIndex)
|
|||
_interpret(externalProgram, 0);
|
||||
}
|
||||
} else {
|
||||
procedureAddress = stackReadInt32(procedurePtr, 16);
|
||||
procedureAddress = stackReadInt32(procedurePtr, offsetof(Procedure, bodyOffset));
|
||||
|
||||
// NOTE: Uninline.
|
||||
_setupCall(program, procedureAddress, 20);
|
||||
|
@ -2836,7 +2836,7 @@ int programFindProcedure(Program* program, const char* name)
|
|||
|
||||
unsigned char* ptr = program->procedures + 4;
|
||||
for (int index = 0; index < procedureCount; index++) {
|
||||
int identifierOffset = stackReadInt32(ptr, offsetof(Procedure, field_0));
|
||||
int identifierOffset = stackReadInt32(ptr, offsetof(Procedure, nameOffset));
|
||||
if (compat_stricmp((char*)(program->identifiers + identifierOffset), name) == 0) {
|
||||
return index;
|
||||
}
|
||||
|
@ -2861,10 +2861,10 @@ void _executeProcedure(Program* program, int procedureIndex)
|
|||
jmp_buf env;
|
||||
|
||||
procedurePtr = program->procedures + 4 + sizeof(Procedure) * procedureIndex;
|
||||
procedureFlags = stackReadInt32(procedurePtr, 4);
|
||||
procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags));
|
||||
|
||||
if ((procedureFlags & PROCEDURE_FLAG_IMPORTED) != 0) {
|
||||
procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, 0));
|
||||
procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, offsetof(Procedure, nameOffset)));
|
||||
externalProgram = externalProcedureGetProgram(procedureIdentifier, &externalProcedureAddress, &externalProcedureArgumentCount);
|
||||
if (externalProgram != NULL) {
|
||||
if (externalProcedureArgumentCount == 0) {
|
||||
|
@ -2882,7 +2882,7 @@ void _executeProcedure(Program* program, int procedureIndex)
|
|||
_interpretOutput(err);
|
||||
}
|
||||
} else {
|
||||
procedureAddress = stackReadInt32(procedurePtr, 16);
|
||||
procedureAddress = stackReadInt32(procedurePtr, offsetof(Procedure, bodyOffset));
|
||||
|
||||
// NOTE: Uninline.
|
||||
_setupCall(program, procedureAddress, 24);
|
||||
|
@ -2918,14 +2918,14 @@ static void _doEvents()
|
|||
|
||||
procedurePtr = programListNode->program->procedures + 4;
|
||||
for (procedureIndex = 0; procedureIndex < procedureCount; procedureIndex++) {
|
||||
procedureFlags = stackReadInt32(procedurePtr, 4);
|
||||
procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags));
|
||||
if ((procedureFlags & PROCEDURE_FLAG_CONDITIONAL) != 0) {
|
||||
memcpy(env, programListNode->program, sizeof(env));
|
||||
oldProgramFlags = programListNode->program->flags;
|
||||
oldInstructionPointer = programListNode->program->instructionPointer;
|
||||
|
||||
programListNode->program->flags = 0;
|
||||
programListNode->program->instructionPointer = stackReadInt32(procedurePtr, 12);
|
||||
programListNode->program->instructionPointer = stackReadInt32(procedurePtr, offsetof(Procedure, conditionOffset));
|
||||
_interpret(programListNode->program, -1);
|
||||
|
||||
if ((programListNode->program->flags & PROGRAM_FLAG_0x04) == 0) {
|
||||
|
@ -2936,16 +2936,16 @@ static void _doEvents()
|
|||
|
||||
if (data != 0) {
|
||||
// NOTE: Uninline.
|
||||
stackWriteInt32(0, procedurePtr, 4);
|
||||
stackWriteInt32(0, procedurePtr, offsetof(Procedure, flags));
|
||||
_executeProc(programListNode->program, procedureIndex);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(programListNode->program, env, sizeof(env));
|
||||
} else if ((procedureFlags & PROCEDURE_FLAG_TIMED) != 0) {
|
||||
if ((unsigned int)stackReadInt32(procedurePtr, 8) < time) {
|
||||
if ((unsigned int)stackReadInt32(procedurePtr, offsetof(Procedure, time)) < time) {
|
||||
// NOTE: Uninline.
|
||||
stackWriteInt32(0, procedurePtr, 4);
|
||||
stackWriteInt32(0, procedurePtr, offsetof(Procedure, flags));
|
||||
_executeProc(programListNode->program, procedureIndex);
|
||||
}
|
||||
}
|
||||
|
@ -3022,6 +3022,15 @@ Program* runScript(char* name)
|
|||
// 0x46E1EC
|
||||
void _updatePrograms()
|
||||
{
|
||||
// CE: Implementation is different. Sfall inserts global scripts into
|
||||
// program list upon creation, so engine does not diffirentiate between
|
||||
// global and normal scripts. Global scripts in CE are not part of program
|
||||
// list, so we need a separate call to continue execution (usually
|
||||
// non-critical calls scheduled from managed windows). One more thing to
|
||||
// note is that global scripts in CE cannot handle conditional/timed procs
|
||||
// (which are not used anyway).
|
||||
sfall_gl_scr_update(_cpuBurstSize);
|
||||
|
||||
ProgramListNode* curr = gInterpreterProgramListHead;
|
||||
while (curr != NULL) {
|
||||
ProgramListNode* next = curr->next;
|
||||
|
@ -3262,7 +3271,7 @@ void* programReturnStackPopPointer(Program* program)
|
|||
return programValue.pointerValue;
|
||||
}
|
||||
|
||||
bool ProgramValue::isEmpty()
|
||||
bool ProgramValue::isEmpty() const
|
||||
{
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
|
@ -3279,19 +3288,19 @@ bool ProgramValue::isEmpty()
|
|||
}
|
||||
|
||||
// Matches Sfall implementation.
|
||||
bool ProgramValue::isInt()
|
||||
bool ProgramValue::isInt() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_INT;
|
||||
}
|
||||
|
||||
// Matches Sfall implementation.
|
||||
bool ProgramValue::isFloat()
|
||||
bool ProgramValue::isFloat() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
// Matches Sfall implementation.
|
||||
float ProgramValue::asFloat()
|
||||
float ProgramValue::asFloat() const
|
||||
{
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
|
@ -3303,4 +3312,42 @@ float ProgramValue::asFloat()
|
|||
}
|
||||
}
|
||||
|
||||
bool ProgramValue::isString() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_STRING || opcode == VALUE_TYPE_DYNAMIC_STRING;
|
||||
}
|
||||
|
||||
ProgramValue::ProgramValue()
|
||||
{
|
||||
opcode = VALUE_TYPE_INT;
|
||||
integerValue = 0;
|
||||
}
|
||||
ProgramValue::ProgramValue(int value)
|
||||
{
|
||||
opcode = VALUE_TYPE_INT;
|
||||
integerValue = value;
|
||||
};
|
||||
ProgramValue::ProgramValue(Object* value)
|
||||
{
|
||||
opcode = VALUE_TYPE_PTR;
|
||||
pointerValue = value;
|
||||
};
|
||||
|
||||
bool ProgramValue::isPointer() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_PTR;
|
||||
}
|
||||
|
||||
int ProgramValue::asInt() const
|
||||
{
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
return integerValue;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
return static_cast<int>(floatValue);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef INTERPRETER_H
|
||||
#define INTERPRETER_H
|
||||
|
||||
#include "object.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <vector>
|
||||
|
@ -132,15 +133,20 @@ enum RawValueType {
|
|||
typedef unsigned short opcode_t;
|
||||
|
||||
typedef struct Procedure {
|
||||
int field_0;
|
||||
int field_4;
|
||||
int field_8;
|
||||
int field_C;
|
||||
int field_10;
|
||||
int field_14;
|
||||
int nameOffset;
|
||||
int flags;
|
||||
int time;
|
||||
int conditionOffset;
|
||||
int bodyOffset;
|
||||
int argCount;
|
||||
} Procedure;
|
||||
|
||||
typedef struct ProgramValue {
|
||||
class ProgramValue {
|
||||
public:
|
||||
ProgramValue();
|
||||
ProgramValue(int value);
|
||||
ProgramValue(Object* value);
|
||||
|
||||
opcode_t opcode;
|
||||
union {
|
||||
int integerValue;
|
||||
|
@ -148,12 +154,14 @@ typedef struct ProgramValue {
|
|||
void* pointerValue;
|
||||
};
|
||||
|
||||
ProgramValue() : opcode(0), pointerValue(nullptr) {}
|
||||
bool isEmpty();
|
||||
bool isInt();
|
||||
bool isFloat();
|
||||
float asFloat();
|
||||
} ProgramValue;
|
||||
bool isEmpty() const;
|
||||
bool isInt() const;
|
||||
bool isFloat() const;
|
||||
bool isString() const;
|
||||
float asFloat() const;
|
||||
bool isPointer() const;
|
||||
int asInt() const;
|
||||
};
|
||||
|
||||
typedef std::vector<ProgramValue> ProgramStack;
|
||||
|
||||
|
@ -196,6 +204,7 @@ void _interpretOutputFunc(int (*func)(char*));
|
|||
int _interpretOutput(const char* format, ...);
|
||||
[[noreturn]] void programFatalError(const char* str, ...);
|
||||
void _interpretDecStringRef(Program* program, opcode_t a2, int a3);
|
||||
void programFree(Program* program);
|
||||
Program* programCreateByPath(const char* path);
|
||||
char* programGetString(Program* program, opcode_t opcode, int offset);
|
||||
char* programGetIdentifier(Program* program, int offset);
|
||||
|
|
|
@ -1580,12 +1580,22 @@ static void opPickup(Program* program)
|
|||
return;
|
||||
}
|
||||
|
||||
if (script->target == NULL) {
|
||||
Object* self = script->target;
|
||||
|
||||
// SFALL: Override `self` via `op_set_self`.
|
||||
// CE: Implementation is different. Sfall integrates via `scriptGetSid` by
|
||||
// returning fake script with overridden `self` (and `target` in this case).
|
||||
if (script->overriddenSelf != nullptr) {
|
||||
self = script->overriddenSelf;
|
||||
script->overriddenSelf = nullptr;
|
||||
}
|
||||
|
||||
if (self == NULL) {
|
||||
scriptPredefinedError(program, "pickup_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
actionPickUp(script->target, object);
|
||||
actionPickUp(self, object);
|
||||
}
|
||||
|
||||
// drop_obj
|
||||
|
@ -4548,6 +4558,15 @@ static void opUseObjectOnObject(Program* program)
|
|||
}
|
||||
|
||||
Object* self = scriptGetSelf(program);
|
||||
|
||||
// SFALL: Override `self` via `op_set_self`.
|
||||
// CE: Implementation is different. Sfall integrates via `scriptGetSid` by
|
||||
// returning fake script with overridden `self`.
|
||||
if (script->overriddenSelf != nullptr) {
|
||||
self = script->overriddenSelf;
|
||||
script->overriddenSelf = nullptr;
|
||||
}
|
||||
|
||||
if (PID_TYPE(self->pid) == OBJ_TYPE_CRITTER) {
|
||||
_action_use_an_item_on_object(self, target, item);
|
||||
} else {
|
||||
|
|
|
@ -5981,4 +5981,9 @@ int _inven_set_timer(Object* a1)
|
|||
return seconds;
|
||||
}
|
||||
|
||||
Object* inven_get_current_target_obj()
|
||||
{
|
||||
return _target_stack[_target_curr_stack];
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -27,6 +27,7 @@ int inventoryOpenLooting(Object* a1, Object* a2);
|
|||
int inventoryOpenStealing(Object* a1, Object* a2);
|
||||
void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5);
|
||||
int _inven_set_timer(Object* a1);
|
||||
Object* inven_get_current_target_obj();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
|
415
src/loadsave.cc
415
src/loadsave.cc
|
@ -18,6 +18,7 @@
|
|||
#include "db.h"
|
||||
#include "dbox.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "display_monitor.h"
|
||||
#include "draw.h"
|
||||
#include "file_utils.h"
|
||||
|
@ -45,6 +46,9 @@
|
|||
#include "random.h"
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "sfall_global_vars.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
|
@ -58,6 +62,12 @@
|
|||
|
||||
namespace fallout {
|
||||
|
||||
#define LOAD_SAVE_SIGNATURE "FALLOUT SAVE FILE"
|
||||
#define LOAD_SAVE_DESCRIPTION_LENGTH 30
|
||||
#define LOAD_SAVE_HANDLER_COUNT 27
|
||||
|
||||
#define LSGAME_MSG_NAME "LSGAME.MSG"
|
||||
|
||||
#define LS_WINDOW_WIDTH 640
|
||||
#define LS_WINDOW_HEIGHT 480
|
||||
|
||||
|
@ -80,8 +90,8 @@ namespace fallout {
|
|||
#define ITEMS_DIR_NAME "items"
|
||||
#define PROTO_FILE_EXT "pro"
|
||||
|
||||
#define LOAD_SAVE_DESCRIPTION_LENGTH (30)
|
||||
#define LOAD_SAVE_HANDLER_COUNT (27)
|
||||
typedef int LoadGameHandler(File* stream);
|
||||
typedef int SaveGameHandler(File* stream);
|
||||
|
||||
typedef enum LoadSaveWindowType {
|
||||
LOAD_SAVE_WINDOW_TYPE_SAVE_GAME,
|
||||
|
@ -104,33 +114,28 @@ typedef enum LoadSaveScrollDirection {
|
|||
LOAD_SAVE_SCROLL_DIRECTION_DOWN,
|
||||
} LoadSaveScrollDirection;
|
||||
|
||||
typedef int LoadGameHandler(File* stream);
|
||||
typedef int SaveGameHandler(File* stream);
|
||||
|
||||
#define LSGAME_MSG_NAME ("LSGAME.MSG")
|
||||
|
||||
typedef struct STRUCT_613D30 {
|
||||
char field_0[24];
|
||||
short field_18;
|
||||
short field_1A;
|
||||
typedef struct LoadSaveSlotData {
|
||||
char signature[24];
|
||||
short versionMinor;
|
||||
short versionMajor;
|
||||
// TODO: The type is probably char, but it's read with the same function as
|
||||
// reading unsigned chars, which in turn probably result of collapsing
|
||||
// reading functions.
|
||||
unsigned char field_1C;
|
||||
char character_name[32];
|
||||
unsigned char versionRelease;
|
||||
char characterName[32];
|
||||
char description[LOAD_SAVE_DESCRIPTION_LENGTH];
|
||||
short field_5C;
|
||||
short field_5E;
|
||||
short field_60;
|
||||
int field_64;
|
||||
short field_68;
|
||||
short field_6A;
|
||||
short field_6C;
|
||||
int field_70;
|
||||
short field_74;
|
||||
short field_76;
|
||||
char file_name[16];
|
||||
} STRUCT_613D30;
|
||||
short fileMonth;
|
||||
short fileDay;
|
||||
short fileYear;
|
||||
int fileTime;
|
||||
short gameMonth;
|
||||
short gameDay;
|
||||
short gameYear;
|
||||
unsigned int gameTime;
|
||||
short elevation;
|
||||
short map;
|
||||
char fileName[16];
|
||||
} LoadSaveSlotData;
|
||||
|
||||
typedef enum LoadSaveFrm {
|
||||
LOAD_SAVE_FRM_BACKGROUND,
|
||||
|
@ -153,10 +158,10 @@ static int lsgLoadGameInSlot(int slot);
|
|||
static int lsgSaveHeaderInSlot(int slot);
|
||||
static int lsgLoadHeaderInSlot(int slot);
|
||||
static int _GetSlotList();
|
||||
static void _ShowSlotList(int a1);
|
||||
static void _DrawInfoBox(int a1);
|
||||
static int _LoadTumbSlot(int a1);
|
||||
static int _GetComment(int a1);
|
||||
static void _ShowSlotList(int windowType);
|
||||
static void _DrawInfoBox(int slot);
|
||||
static int _LoadTumbSlot(int slot);
|
||||
static int _GetComment(int slot);
|
||||
static int _get_input_str2(int win, int doneKeyCode, int cancelKeyCode, char* description, int maxLength, int x, int y, int textColor, int backgroundColor, int flags);
|
||||
static int _DummyFunc(File* stream);
|
||||
static int _PrepLoad(File* stream);
|
||||
|
@ -164,8 +169,7 @@ static int _EndLoad(File* stream);
|
|||
static int _GameMap2Slot(File* stream);
|
||||
static int _SlotMap2Game(File* stream);
|
||||
static int _mygets(char* dest, File* stream);
|
||||
static int _copy_file(const char* a1, const char* a2);
|
||||
static int _MapDirErase(const char* path, const char* a2);
|
||||
static int _copy_file(const char* existingFileName, const char* newFileName);
|
||||
static int _SaveBackup();
|
||||
static int _RestoreSave();
|
||||
static int _LoadObjDudeCid(File* stream);
|
||||
|
@ -198,7 +202,7 @@ static bool gLoadSaveWindowIsoWasEnabled = false;
|
|||
static int _map_backup_count = -1;
|
||||
|
||||
// 0x5193C8
|
||||
static int _automap_db_flag = 0;
|
||||
static bool _automap_db_flag = false;
|
||||
|
||||
// 0x5193CC
|
||||
static const char* _patches = NULL;
|
||||
|
@ -266,7 +270,7 @@ static LoadGameHandler* _master_load_list[LOAD_SAVE_HANDLER_COUNT] = {
|
|||
};
|
||||
|
||||
// 0x5194C4
|
||||
static int _loadingGame = 0;
|
||||
static bool _loadingGame = false;
|
||||
|
||||
// lsgame.msg
|
||||
//
|
||||
|
@ -274,7 +278,7 @@ static int _loadingGame = 0;
|
|||
static MessageList gLoadSaveMessageList;
|
||||
|
||||
// 0x613D30
|
||||
static STRUCT_613D30 _LSData[10];
|
||||
static LoadSaveSlotData _LSData[10];
|
||||
|
||||
// 0x614280
|
||||
static int _LSstatus[10];
|
||||
|
@ -326,6 +330,9 @@ static int gLoadSaveWindowOldFont;
|
|||
|
||||
static FrmImage _loadsaveFrmImages[LOAD_SAVE_FRM_COUNT];
|
||||
|
||||
static int quickSaveSlots = 0;
|
||||
static bool autoQuickSaveSlots = false;
|
||||
|
||||
// 0x47B7E4
|
||||
void _InitLoadSave()
|
||||
{
|
||||
|
@ -333,17 +340,22 @@ void _InitLoadSave()
|
|||
_slot_cursor = 0;
|
||||
_patches = settings.system.master_patches_path.c_str();
|
||||
|
||||
_MapDirErase("MAPS\\", "SAV");
|
||||
_MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
_MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
MapDirErase("MAPS\\", "SAV");
|
||||
MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_AUTO_QUICK_SAVE, &quickSaveSlots);
|
||||
if (quickSaveSlots > 0 && quickSaveSlots <= 10) {
|
||||
autoQuickSaveSlots = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x47B85C
|
||||
void _ResetLoadSave()
|
||||
{
|
||||
_MapDirErase("MAPS\\", "SAV");
|
||||
_MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
_MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
MapDirErase("MAPS\\", "SAV");
|
||||
MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT);
|
||||
}
|
||||
|
||||
// SaveGame
|
||||
|
@ -357,7 +369,18 @@ int lsgSaveGame(int mode)
|
|||
_ls_error_code = 0;
|
||||
_patches = settings.system.master_patches_path.c_str();
|
||||
|
||||
// SFALL: skip slot selection if auto quicksave is enabled
|
||||
if (autoQuickSaveSlots) {
|
||||
_quick_done = true;
|
||||
}
|
||||
|
||||
if (mode == LOAD_SAVE_MODE_QUICK && _quick_done) {
|
||||
// SFALL: cycle through first N slots for quicksaving
|
||||
if (autoQuickSaveSlots) {
|
||||
if (++_slot_cursor >= quickSaveSlots) {
|
||||
_slot_cursor = 0;
|
||||
}
|
||||
}
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
strcat(_gmpath, "SAVE.DAT");
|
||||
|
||||
|
@ -471,7 +494,7 @@ int lsgSaveGame(int mode)
|
|||
break;
|
||||
}
|
||||
|
||||
_ShowSlotList(0);
|
||||
_ShowSlotList(LOAD_SAVE_WINDOW_TYPE_SAVE_GAME);
|
||||
_DrawInfoBox(_slot_cursor);
|
||||
windowRefresh(gLoadSaveWindow);
|
||||
|
||||
|
@ -650,9 +673,9 @@ int lsgSaveGame(int mode)
|
|||
}
|
||||
|
||||
if (scrollCounter > 14.4) {
|
||||
while (getTicksSince(start) < 1000 / scrollVelocity) { }
|
||||
delay_ms(1000 / scrollVelocity - (getTicks() - start));
|
||||
} else {
|
||||
while (getTicksSince(start) < 1000 / 24) { }
|
||||
delay_ms(1000 / 24 - (getTicks() - start));
|
||||
}
|
||||
|
||||
keyCode = inputGetInput();
|
||||
|
@ -696,8 +719,7 @@ int lsgSaveGame(int mode)
|
|||
doubleClickSlot = -1;
|
||||
}
|
||||
|
||||
while (getTicksSince(tick) < 1000 / 24) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - tick));
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
|
@ -976,7 +998,7 @@ int lsgLoadGame(int mode)
|
|||
break;
|
||||
}
|
||||
|
||||
_ShowSlotList(2);
|
||||
_ShowSlotList(LOAD_SAVE_WINDOW_TYPE_LOAD_GAME);
|
||||
_DrawInfoBox(_slot_cursor);
|
||||
windowRefresh(gLoadSaveWindow);
|
||||
renderPresent();
|
||||
|
@ -1147,15 +1169,15 @@ int lsgLoadGame(int mode)
|
|||
break;
|
||||
}
|
||||
|
||||
_ShowSlotList(2);
|
||||
_ShowSlotList(LOAD_SAVE_WINDOW_TYPE_LOAD_GAME);
|
||||
_DrawInfoBox(_slot_cursor);
|
||||
windowRefresh(gLoadSaveWindow);
|
||||
}
|
||||
|
||||
if (scrollCounter > 14.4) {
|
||||
while (getTicksSince(start) < 1000 / scrollVelocity) { }
|
||||
delay_ms(1000 / scrollVelocity - (getTicks() - start));
|
||||
} else {
|
||||
while (getTicksSince(start) < 1000 / 24) { }
|
||||
delay_ms(1000 / 24 - (getTicks() - start));
|
||||
}
|
||||
|
||||
keyCode = inputGetInput();
|
||||
|
@ -1194,7 +1216,7 @@ int lsgLoadGame(int mode)
|
|||
}
|
||||
|
||||
_DrawInfoBox(_slot_cursor);
|
||||
_ShowSlotList(2);
|
||||
_ShowSlotList(LOAD_SAVE_WINDOW_TYPE_LOAD_GAME);
|
||||
}
|
||||
|
||||
windowRefresh(gLoadSaveWindow);
|
||||
|
@ -1205,7 +1227,7 @@ int lsgLoadGame(int mode)
|
|||
doubleClickSlot = -1;
|
||||
}
|
||||
|
||||
while (getTicksSince(time) < 1000 / 24) { }
|
||||
delay_ms(1000 / 24 - (getTicks() - time));
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
|
@ -1547,7 +1569,7 @@ static int lsgPerformSaveGame()
|
|||
debugPrint("\nLOADSAVE: ** Error opening save game for writing! **\n");
|
||||
_RestoreSave();
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
_MapDirErase(_gmpath, "BAK");
|
||||
MapDirErase(_gmpath, "BAK");
|
||||
_partyMemberUnPrepSave();
|
||||
backgroundSoundResume();
|
||||
return -1;
|
||||
|
@ -1560,7 +1582,7 @@ static int lsgPerformSaveGame()
|
|||
fileClose(_flptr);
|
||||
_RestoreSave();
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
_MapDirErase(_gmpath, "BAK");
|
||||
MapDirErase(_gmpath, "BAK");
|
||||
_partyMemberUnPrepSave();
|
||||
backgroundSoundResume();
|
||||
return -1;
|
||||
|
@ -1574,7 +1596,7 @@ static int lsgPerformSaveGame()
|
|||
fileClose(_flptr);
|
||||
_RestoreSave();
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
_MapDirErase(_gmpath, "BAK");
|
||||
MapDirErase(_gmpath, "BAK");
|
||||
_partyMemberUnPrepSave();
|
||||
backgroundSoundResume();
|
||||
return -1;
|
||||
|
@ -1587,8 +1609,75 @@ static int lsgPerformSaveGame()
|
|||
|
||||
fileClose(_flptr);
|
||||
|
||||
// SFALL: Save sfallgv.sav.
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
_MapDirErase(_gmpath, "BAK");
|
||||
strcat(_gmpath, "sfallgv.sav");
|
||||
|
||||
_flptr = fileOpen(_gmpath, "wb");
|
||||
if (_flptr != NULL) {
|
||||
do {
|
||||
if (!sfall_gl_vars_save(_flptr)) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving global vars **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: For now fill remaining sections with zeros to that Sfall
|
||||
// can successfully read our global vars and skip the rest.
|
||||
|
||||
int nextObjectId = 0;
|
||||
if (fileWrite(&nextObjectId, sizeof(nextObjectId), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving next object id **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int addedYears = 0;
|
||||
if (fileWrite(&addedYears, sizeof(addedYears), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving added years **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int fakeTraitsCount = 0;
|
||||
if (fileWrite(&fakeTraitsCount, sizeof(fakeTraitsCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving fake traits **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int fakePerksCount = 0;
|
||||
if (fileWrite(&fakePerksCount, sizeof(fakePerksCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving fake perks **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int fakeSelectablePerksCount = 0;
|
||||
if (fileWrite(&fakeSelectablePerksCount, sizeof(fakeSelectablePerksCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving fake selectable perks **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int arraysCountOld = 0;
|
||||
if (fileWrite(&arraysCountOld, sizeof(arraysCountOld), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving arrays (old fmt) **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int arraysCountNew = 0;
|
||||
if (fileWrite(&arraysCountNew, sizeof(arraysCountNew), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving arrays (new fmt) **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int drugPidsCount = 0;
|
||||
if (fileWrite(&drugPidsCount, sizeof(drugPidsCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving drug pids **\n");
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
fileClose(_flptr);
|
||||
}
|
||||
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
MapDirErase(_gmpath, "BAK");
|
||||
|
||||
gLoadSaveMessageListItem.num = 140;
|
||||
if (messageListGetItem(&gLoadSaveMessageList, &gLoadSaveMessageListItem)) {
|
||||
|
@ -1603,7 +1692,7 @@ static int lsgPerformSaveGame()
|
|||
}
|
||||
|
||||
// 0x47DC60
|
||||
int _isLoadingGame()
|
||||
bool _isLoadingGame()
|
||||
{
|
||||
return _loadingGame;
|
||||
}
|
||||
|
@ -1611,7 +1700,7 @@ int _isLoadingGame()
|
|||
// 0x47DC68
|
||||
static int lsgLoadGameInSlot(int slot)
|
||||
{
|
||||
_loadingGame = 1;
|
||||
_loadingGame = true;
|
||||
|
||||
if (isInCombat()) {
|
||||
interfaceBarEndButtonsHide(false);
|
||||
|
@ -1622,13 +1711,13 @@ static int lsgLoadGameInSlot(int slot)
|
|||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
strcat(_gmpath, "SAVE.DAT");
|
||||
|
||||
STRUCT_613D30* ptr = &(_LSData[slot]);
|
||||
LoadSaveSlotData* ptr = &(_LSData[slot]);
|
||||
debugPrint("\nLOADSAVE: Load name: %s\n", ptr->description);
|
||||
|
||||
_flptr = fileOpen(_gmpath, "rb");
|
||||
if (_flptr == NULL) {
|
||||
debugPrint("\nLOADSAVE: ** Error opening load game file for reading! **\n");
|
||||
_loadingGame = 0;
|
||||
_loadingGame = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1637,7 +1726,7 @@ static int lsgLoadGameInSlot(int slot)
|
|||
debugPrint("\nLOADSAVE: ** Error reading save game header! **\n");
|
||||
fileClose(_flptr);
|
||||
gameReset();
|
||||
_loadingGame = 0;
|
||||
_loadingGame = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1652,7 +1741,7 @@ static int lsgLoadGameInSlot(int slot)
|
|||
debugPrint("LOADSAVE: Load function #%d data size read: %d bytes.\n", index, fileTell(_flptr) - pos);
|
||||
fileClose(_flptr);
|
||||
gameReset();
|
||||
_loadingGame = 0;
|
||||
_loadingGame = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1662,8 +1751,26 @@ static int lsgLoadGameInSlot(int slot)
|
|||
debugPrint("LOADSAVE: Total load data read: %ld bytes.\n", fileTell(_flptr));
|
||||
fileClose(_flptr);
|
||||
|
||||
// SFALL: Load sfallgv.sav.
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
strcat(_gmpath, "sfallgv.sav");
|
||||
|
||||
_flptr = fileOpen(_gmpath, "rb");
|
||||
if (_flptr != NULL) {
|
||||
do {
|
||||
if (!sfall_gl_vars_load(_flptr)) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error loading global vars **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: For now silently ignore remaining sections.
|
||||
} while (0);
|
||||
|
||||
fileClose(_flptr);
|
||||
}
|
||||
|
||||
snprintf(_str, sizeof(_str), "%s\\", "MAPS");
|
||||
_MapDirErase(_str, "BAK");
|
||||
MapDirErase(_str, "BAK");
|
||||
_proto_dude_update_gender();
|
||||
|
||||
// Game Loaded.
|
||||
|
@ -1674,7 +1781,10 @@ static int lsgLoadGameInSlot(int slot)
|
|||
debugPrint("\nError: Couldn't find LoadSave Message!");
|
||||
}
|
||||
|
||||
_loadingGame = 0;
|
||||
_loadingGame = false;
|
||||
|
||||
// SFALL: Start global scripts.
|
||||
sfall_gl_scr_exec_start_proc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1684,10 +1794,10 @@ static int lsgSaveHeaderInSlot(int slot)
|
|||
{
|
||||
_ls_error_code = 4;
|
||||
|
||||
STRUCT_613D30* ptr = &(_LSData[slot]);
|
||||
strncpy(ptr->field_0, "FALLOUT SAVE FILE", 24);
|
||||
LoadSaveSlotData* ptr = &(_LSData[slot]);
|
||||
strncpy(ptr->signature, LOAD_SAVE_SIGNATURE, 24);
|
||||
|
||||
if (fileWrite(ptr->field_0, 1, 24, _flptr) == -1) {
|
||||
if (fileWrite(ptr->signature, 1, 24, _flptr) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1695,22 +1805,22 @@ static int lsgSaveHeaderInSlot(int slot)
|
|||
temp[0] = VERSION_MAJOR;
|
||||
temp[1] = VERSION_MINOR;
|
||||
|
||||
ptr->field_18 = temp[0];
|
||||
ptr->field_1A = temp[1];
|
||||
ptr->versionMinor = temp[0];
|
||||
ptr->versionMajor = temp[1];
|
||||
|
||||
if (fileWriteInt16List(_flptr, temp, 2) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptr->field_1C = VERSION_RELEASE;
|
||||
ptr->versionRelease = VERSION_RELEASE;
|
||||
if (fileWriteUInt8(_flptr, VERSION_RELEASE) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* characterName = critterGetName(gDude);
|
||||
strncpy(ptr->character_name, characterName, 32);
|
||||
strncpy(ptr->characterName, characterName, 32);
|
||||
|
||||
if (fileWrite(ptr->character_name, 32, 1, _flptr) != 1) {
|
||||
if (fileWrite(ptr->characterName, 32, 1, _flptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1725,16 +1835,16 @@ static int lsgSaveHeaderInSlot(int slot)
|
|||
temp[1] = local->tm_mon + 1;
|
||||
temp[2] = local->tm_year + 1900;
|
||||
|
||||
ptr->field_5E = temp[0];
|
||||
ptr->field_5C = temp[1];
|
||||
ptr->field_60 = temp[2];
|
||||
ptr->field_64 = local->tm_hour + local->tm_min;
|
||||
ptr->fileDay = temp[0];
|
||||
ptr->fileMonth = temp[1];
|
||||
ptr->fileYear = temp[2];
|
||||
ptr->fileTime = local->tm_hour + local->tm_min;
|
||||
|
||||
if (fileWriteInt16List(_flptr, temp, 3) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_db_fwriteLong(_flptr, ptr->field_64) == -1) {
|
||||
if (_db_fwriteLong(_flptr, ptr->fileTime) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1746,23 +1856,23 @@ static int lsgSaveHeaderInSlot(int slot)
|
|||
temp[0] = month;
|
||||
temp[1] = day;
|
||||
temp[2] = year;
|
||||
ptr->field_70 = gameTimeGetTime();
|
||||
ptr->gameTime = gameTimeGetTime();
|
||||
|
||||
if (fileWriteInt16List(_flptr, temp, 3) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_db_fwriteLong(_flptr, ptr->field_70) == -1) {
|
||||
if (fileWriteUInt32(_flptr, ptr->gameTime) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptr->field_74 = gElevation;
|
||||
if (fileWriteInt16(_flptr, ptr->field_74) == -1) {
|
||||
ptr->elevation = gElevation;
|
||||
if (fileWriteInt16(_flptr, ptr->elevation) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptr->field_76 = mapGetCurrentMap();
|
||||
if (fileWriteInt16(_flptr, ptr->field_76) == -1) {
|
||||
ptr->map = mapGetCurrentMap();
|
||||
if (fileWriteInt16(_flptr, ptr->map) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1771,8 +1881,8 @@ static int lsgSaveHeaderInSlot(int slot)
|
|||
|
||||
// NOTE: Uppercased from "sav".
|
||||
char* v1 = _strmfe(_str, mapName, "SAV");
|
||||
strncpy(ptr->file_name, v1, 16);
|
||||
if (fileWrite(ptr->file_name, 16, 1, _flptr) != 1) {
|
||||
strncpy(ptr->fileName, v1, 16);
|
||||
if (fileWrite(ptr->fileName, 16, 1, _flptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1795,13 +1905,13 @@ static int lsgLoadHeaderInSlot(int slot)
|
|||
{
|
||||
_ls_error_code = 3;
|
||||
|
||||
STRUCT_613D30* ptr = &(_LSData[slot]);
|
||||
LoadSaveSlotData* ptr = &(_LSData[slot]);
|
||||
|
||||
if (fileRead(ptr->field_0, 1, 24, _flptr) != 24) {
|
||||
if (fileRead(ptr->signature, 1, 24, _flptr) != 24) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp(ptr->field_0, "FALLOUT SAVE FILE", 18) != 0) {
|
||||
if (strncmp(ptr->signature, LOAD_SAVE_SIGNATURE, 18) != 0) {
|
||||
debugPrint("\nLOADSAVE: ** Invalid save file on load! **\n");
|
||||
_ls_error_code = 2;
|
||||
return -1;
|
||||
|
@ -1812,20 +1922,20 @@ static int lsgLoadHeaderInSlot(int slot)
|
|||
return -1;
|
||||
}
|
||||
|
||||
ptr->field_18 = v8[0];
|
||||
ptr->field_1A = v8[1];
|
||||
ptr->versionMinor = v8[0];
|
||||
ptr->versionMajor = v8[1];
|
||||
|
||||
if (fileReadUInt8(_flptr, &(ptr->field_1C)) == -1) {
|
||||
if (fileReadUInt8(_flptr, &(ptr->versionRelease)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ptr->field_18 != 1 || ptr->field_1A != 2 || ptr->field_1C != 'R') {
|
||||
debugPrint("\nLOADSAVE: Load slot #%d Version: %d.%d%c\n", slot, ptr->field_18, ptr->field_1A, ptr->field_1C);
|
||||
if (ptr->versionMinor != 1 || ptr->versionMajor != 2 || ptr->versionRelease != 'R') {
|
||||
debugPrint("\nLOADSAVE: Load slot #%d Version: %d.%d%c\n", slot, ptr->versionMinor, ptr->versionMajor, ptr->versionRelease);
|
||||
_ls_error_code = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileRead(ptr->character_name, 32, 1, _flptr) != 1) {
|
||||
if (fileRead(ptr->characterName, 32, 1, _flptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1837,11 +1947,11 @@ static int lsgLoadHeaderInSlot(int slot)
|
|||
return -1;
|
||||
}
|
||||
|
||||
ptr->field_5C = v8[0];
|
||||
ptr->field_5E = v8[1];
|
||||
ptr->field_60 = v8[2];
|
||||
ptr->fileMonth = v8[0];
|
||||
ptr->fileDay = v8[1];
|
||||
ptr->fileYear = v8[2];
|
||||
|
||||
if (_db_freadInt(_flptr, &(ptr->field_64)) == -1) {
|
||||
if (_db_freadInt(_flptr, &(ptr->fileTime)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1849,23 +1959,23 @@ static int lsgLoadHeaderInSlot(int slot)
|
|||
return -1;
|
||||
}
|
||||
|
||||
ptr->field_68 = v8[0];
|
||||
ptr->field_6A = v8[1];
|
||||
ptr->field_6C = v8[2];
|
||||
ptr->gameMonth = v8[0];
|
||||
ptr->gameDay = v8[1];
|
||||
ptr->gameYear = v8[2];
|
||||
|
||||
if (_db_freadInt(_flptr, &(ptr->field_70)) == -1) {
|
||||
if (fileReadUInt32(_flptr, &(ptr->gameTime)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileReadInt16(_flptr, &(ptr->field_74)) == -1) {
|
||||
if (fileReadInt16(_flptr, &(ptr->elevation)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileReadInt16(_flptr, &(ptr->field_76)) == -1) {
|
||||
if (fileReadInt16(_flptr, &(ptr->map)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileRead(ptr->file_name, 1, 16, _flptr) != 16) {
|
||||
if (fileRead(ptr->fileName, 1, 16, _flptr) != 16) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1919,7 +2029,7 @@ static int _GetSlotList()
|
|||
}
|
||||
|
||||
// 0x47E6D8
|
||||
static void _ShowSlotList(int a1)
|
||||
static void _ShowSlotList(int windowType)
|
||||
{
|
||||
bufferFill(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 87 + 55, 230, 353, LS_WINDOW_WIDTH, gLoadSaveWindowBuffer[LS_WINDOW_WIDTH * 86 + 55] & 0xFF);
|
||||
|
||||
|
@ -1927,7 +2037,7 @@ static void _ShowSlotList(int a1)
|
|||
for (int index = 0; index < 10; index += 1) {
|
||||
|
||||
int color = index == _slot_cursor ? _colorTable[32747] : _colorTable[992];
|
||||
const char* text = getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, a1 != 0 ? 110 : 109);
|
||||
const char* text = getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, windowType != 0 ? 110 : 109);
|
||||
snprintf(_str, sizeof(_str), "[ %s %.2d: ]", text, index + 1);
|
||||
fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * y + 55, _str, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color);
|
||||
|
||||
|
@ -1961,7 +2071,7 @@ static void _ShowSlotList(int a1)
|
|||
}
|
||||
|
||||
// 0x47E8E0
|
||||
static void _DrawInfoBox(int a1)
|
||||
static void _DrawInfoBox(int slot)
|
||||
{
|
||||
blitBufferToBuffer(_loadsaveFrmImages[LOAD_SAVE_FRM_BACKGROUND].getData() + LS_WINDOW_WIDTH * 254 + 396, 164, 60, LS_WINDOW_WIDTH, gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 254 + 396, 640);
|
||||
|
||||
|
@ -1969,26 +2079,28 @@ static void _DrawInfoBox(int a1)
|
|||
const char* text;
|
||||
int color = _colorTable[992];
|
||||
|
||||
switch (_LSstatus[a1]) {
|
||||
switch (_LSstatus[slot]) {
|
||||
case SLOT_STATE_OCCUPIED:
|
||||
do {
|
||||
STRUCT_613D30* ptr = &(_LSData[a1]);
|
||||
fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 254 + 396, ptr->character_name, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color);
|
||||
if (1) {
|
||||
LoadSaveSlotData* ptr = &(_LSData[slot]);
|
||||
fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 254 + 396, ptr->characterName, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color);
|
||||
|
||||
int v4 = ptr->field_70 / 600;
|
||||
int v5 = v4 % 60;
|
||||
int v6 = 25 * (v4 / 60 % 24);
|
||||
int v21 = 4 * v6 + v5;
|
||||
|
||||
text = getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, 116 + ptr->field_68);
|
||||
snprintf(_str, sizeof(_str), "%.2d %s %.4d %.4d", ptr->field_6A, text, ptr->field_6C, v21);
|
||||
snprintf(_str,
|
||||
sizeof(_str),
|
||||
"%.2d %s %.4d %.4d",
|
||||
ptr->gameDay,
|
||||
getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, 116 + ptr->gameMonth),
|
||||
ptr->gameYear,
|
||||
100 * ((ptr->gameTime / 600) / 60 % 24) + (ptr->gameTime / 600) % 60);
|
||||
|
||||
int v2 = fontGetLineHeight();
|
||||
fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * (256 + v2) + 397, _str, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color);
|
||||
|
||||
const char* v22 = mapGetName(ptr->field_76, ptr->field_74);
|
||||
const char* v9 = mapGetCityName(ptr->field_76);
|
||||
snprintf(_str, sizeof(_str), "%s %s", v9, v22);
|
||||
snprintf(_str,
|
||||
sizeof(_str),
|
||||
"%s %s",
|
||||
mapGetCityName(ptr->map),
|
||||
mapGetName(ptr->map, ptr->elevation));
|
||||
|
||||
int y = v2 + 3 + v2 + 256;
|
||||
short beginnings[WORD_WRAP_MAX_COUNT];
|
||||
|
@ -2003,7 +2115,7 @@ static void _DrawInfoBox(int a1)
|
|||
y += v2 + 2;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
return;
|
||||
case SLOT_STATE_EMPTY:
|
||||
// Empty.
|
||||
|
@ -2030,30 +2142,28 @@ static void _DrawInfoBox(int a1)
|
|||
}
|
||||
|
||||
// 0x47EC48
|
||||
static int _LoadTumbSlot(int a1)
|
||||
static int _LoadTumbSlot(int slot)
|
||||
{
|
||||
File* stream;
|
||||
int v2;
|
||||
|
||||
v2 = _LSstatus[_slot_cursor];
|
||||
if (v2 != 0 && v2 != 2 && v2 != 3) {
|
||||
if (_LSstatus[_slot_cursor] != SLOT_STATE_EMPTY
|
||||
&& _LSstatus[_slot_cursor] != SLOT_STATE_ERROR
|
||||
&& _LSstatus[_slot_cursor] != SLOT_STATE_UNSUPPORTED_VERSION) {
|
||||
snprintf(_str, sizeof(_str), "%s\\%s%.2d\\%s", "SAVEGAME", "SLOT", _slot_cursor + 1, "SAVE.DAT");
|
||||
debugPrint(" Filename %s\n", _str);
|
||||
|
||||
stream = fileOpen(_str, "rb");
|
||||
File* stream = fileOpen(_str, "rb");
|
||||
if (stream == NULL) {
|
||||
debugPrint("\nLOADSAVE: ** (A) Error reading thumbnail #%d! **\n", a1);
|
||||
debugPrint("\nLOADSAVE: ** (A) Error reading thumbnail #%d! **\n", slot);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileSeek(stream, 131, SEEK_SET) != 0) {
|
||||
debugPrint("\nLOADSAVE: ** (B) Error reading thumbnail #%d! **\n", a1);
|
||||
debugPrint("\nLOADSAVE: ** (B) Error reading thumbnail #%d! **\n", slot);
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileRead(_thumbnail_image, LS_PREVIEW_SIZE, 1, stream) != 1) {
|
||||
debugPrint("\nLOADSAVE: ** (C) Error reading thumbnail #%d! **\n", a1);
|
||||
debugPrint("\nLOADSAVE: ** (C) Error reading thumbnail #%d! **\n", slot);
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
@ -2065,7 +2175,7 @@ static int _LoadTumbSlot(int a1)
|
|||
}
|
||||
|
||||
// 0x47ED5C
|
||||
static int _GetComment(int a1)
|
||||
static int _GetComment(int slot)
|
||||
{
|
||||
// Maintain original position in original resolution, otherwise center it.
|
||||
int commentWindowX = screenGetWidth() != 640
|
||||
|
@ -2166,7 +2276,7 @@ static int _GetComment(int a1)
|
|||
|
||||
char description[LOAD_SAVE_DESCRIPTION_LENGTH];
|
||||
if (_LSstatus[_slot_cursor] == SLOT_STATE_OCCUPIED) {
|
||||
strncpy(description, _LSData[a1].description, LOAD_SAVE_DESCRIPTION_LENGTH);
|
||||
strncpy(description, _LSData[slot].description, LOAD_SAVE_DESCRIPTION_LENGTH);
|
||||
} else {
|
||||
memset(description, '\0', LOAD_SAVE_DESCRIPTION_LENGTH);
|
||||
}
|
||||
|
@ -2175,8 +2285,8 @@ static int _GetComment(int a1)
|
|||
|
||||
int backgroundColor = *(_loadsaveFrmImages[LOAD_SAVE_FRM_BOX].getData() + _loadsaveFrmImages[LOAD_SAVE_FRM_BOX].getWidth() * 35 + 24);
|
||||
if (_get_input_str2(window, 507, 508, description, LOAD_SAVE_DESCRIPTION_LENGTH - 1, 24, 35, _colorTable[992], backgroundColor, 0) == 0) {
|
||||
strncpy(_LSData[a1].description, description, LOAD_SAVE_DESCRIPTION_LENGTH);
|
||||
_LSData[a1].description[LOAD_SAVE_DESCRIPTION_LENGTH - 1] = '\0';
|
||||
strncpy(_LSData[slot].description, description, LOAD_SAVE_DESCRIPTION_LENGTH);
|
||||
_LSData[slot].description[LOAD_SAVE_DESCRIPTION_LENGTH - 1] = '\0';
|
||||
rc = 1;
|
||||
} else {
|
||||
rc = 0;
|
||||
|
@ -2277,8 +2387,7 @@ static int _get_input_str2(int win, int doneKeyCode, int cancelKeyCode, char* de
|
|||
windowRefresh(win);
|
||||
}
|
||||
|
||||
while (getTicksSince(tick) < 1000 / 24) {
|
||||
}
|
||||
delay_ms(1000 / 24 - (getTicks() - tick));
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
|
@ -2306,7 +2415,7 @@ static int _PrepLoad(File* stream)
|
|||
gameReset();
|
||||
gameMouseSetCursor(MOUSE_CURSOR_WAIT_PLANET);
|
||||
gMapHeader.name[0] = '\0';
|
||||
gameTimeSetTime(_LSData[_slot_cursor].field_70);
|
||||
gameTimeSetTime(_LSData[_slot_cursor].gameTime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2314,7 +2423,7 @@ static int _PrepLoad(File* stream)
|
|||
static int _EndLoad(File* stream)
|
||||
{
|
||||
wmMapMusicStart();
|
||||
dudeSetName(_LSData[_slot_cursor].character_name);
|
||||
dudeSetName(_LSData[_slot_cursor].characterName);
|
||||
interfaceBarRefresh();
|
||||
indicatorBarRefresh();
|
||||
tileWindowRefresh();
|
||||
|
@ -2376,7 +2485,7 @@ static int _GameMap2Slot(File* stream)
|
|||
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
|
||||
if (_MapDirErase(_gmpath, "SAV") == -1) {
|
||||
if (MapDirErase(_gmpath, "SAV") == -1) {
|
||||
fileNameListFree(&fileNameList, 0);
|
||||
return -1;
|
||||
}
|
||||
|
@ -2455,19 +2564,19 @@ static int _SlotMap2Game(File* stream)
|
|||
|
||||
snprintf(_str0, sizeof(_str0), "%s\\", PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME);
|
||||
|
||||
if (_MapDirErase(_str0, PROTO_FILE_EXT) == -1) {
|
||||
if (MapDirErase(_str0, PROTO_FILE_EXT) == -1) {
|
||||
debugPrint("LOADSAVE: returning 3\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(_str0, sizeof(_str0), "%s\\", PROTO_DIR_NAME "\\" ITEMS_DIR_NAME);
|
||||
if (_MapDirErase(_str0, PROTO_FILE_EXT) == -1) {
|
||||
if (MapDirErase(_str0, PROTO_FILE_EXT) == -1) {
|
||||
debugPrint("LOADSAVE: returning 4\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(_str0, sizeof(_str0), "%s\\", "MAPS");
|
||||
if (_MapDirErase(_str0, "SAV") == -1) {
|
||||
if (MapDirErase(_str0, "SAV") == -1) {
|
||||
debugPrint("LOADSAVE: returning 5\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -2525,7 +2634,7 @@ static int _SlotMap2Game(File* stream)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (mapLoadSaved(_LSData[_slot_cursor].file_name) == -1) {
|
||||
if (mapLoadSaved(_LSData[_slot_cursor].fileName) == -1) {
|
||||
debugPrint("LOADSAVE: returning 13\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -2561,7 +2670,7 @@ static int _mygets(char* dest, File* stream)
|
|||
}
|
||||
|
||||
// 0x47FE58
|
||||
static int _copy_file(const char* a1, const char* a2)
|
||||
static int _copy_file(const char* existingFileName, const char* newFileName)
|
||||
{
|
||||
File* stream1;
|
||||
File* stream2;
|
||||
|
@ -2575,7 +2684,7 @@ static int _copy_file(const char* a1, const char* a2)
|
|||
buf = NULL;
|
||||
result = -1;
|
||||
|
||||
stream1 = fileOpen(a1, "rb");
|
||||
stream1 = fileOpen(existingFileName, "rb");
|
||||
if (stream1 == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -2585,7 +2694,7 @@ static int _copy_file(const char* a1, const char* a2)
|
|||
goto out;
|
||||
}
|
||||
|
||||
stream2 = fileOpen(a2, "wb");
|
||||
stream2 = fileOpen(newFileName, "wb");
|
||||
if (stream2 == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -2622,7 +2731,7 @@ out:
|
|||
}
|
||||
|
||||
if (stream2 != NULL) {
|
||||
fileClose(stream1);
|
||||
fileClose(stream2);
|
||||
}
|
||||
|
||||
if (buf != NULL) {
|
||||
|
@ -2638,11 +2747,11 @@ void lsgInit()
|
|||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "%s\\", "MAPS");
|
||||
_MapDirErase(path, "SAV");
|
||||
MapDirErase(path, "SAV");
|
||||
}
|
||||
|
||||
// 0x480040
|
||||
static int _MapDirErase(const char* relativePath, const char* extension)
|
||||
int MapDirErase(const char* relativePath, const char* extension)
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "%s*.%s", relativePath, extension);
|
||||
|
@ -2726,7 +2835,7 @@ static int _SaveBackup()
|
|||
char* v2 = _strmfe(_str2, "AUTOMAP.DB", "BAK");
|
||||
snprintf(_str1, sizeof(_str1), "%s\\%s", _gmpath, v2);
|
||||
|
||||
_automap_db_flag = 0;
|
||||
_automap_db_flag = false;
|
||||
|
||||
File* stream2 = fileOpen(_str0, "rb");
|
||||
if (stream2 != NULL) {
|
||||
|
@ -2736,7 +2845,7 @@ static int _SaveBackup()
|
|||
return -1;
|
||||
}
|
||||
|
||||
_automap_db_flag = 1;
|
||||
_automap_db_flag = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -18,8 +18,9 @@ void _InitLoadSave();
|
|||
void _ResetLoadSave();
|
||||
int lsgSaveGame(int mode);
|
||||
int lsgLoadGame(int mode);
|
||||
int _isLoadingGame();
|
||||
bool _isLoadingGame();
|
||||
void lsgInit();
|
||||
int MapDirErase(const char* path, const char* extension);
|
||||
int _MapDirEraseFile_(const char* a1, const char* a2);
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "selfrun.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window.h"
|
||||
|
@ -148,6 +149,9 @@ int falloutMain(int argc, char** argv)
|
|||
_main_load_new(mapNameCopy);
|
||||
free(mapNameCopy);
|
||||
|
||||
// SFALL: AfterNewGameStartHook.
|
||||
sfall_gl_scr_exec_start_proc();
|
||||
|
||||
mainLoop();
|
||||
paletteFadeTo(gPaletteWhite);
|
||||
|
||||
|
@ -357,6 +361,10 @@ static void mainLoop()
|
|||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
// SFALL: MainLoopHook.
|
||||
sfall_gl_scr_process_main();
|
||||
|
||||
gameHandleKey(keyCode, false);
|
||||
|
||||
scriptsHandleRequests();
|
||||
|
|
|
@ -108,7 +108,7 @@ int* gMapLocalVars = NULL;
|
|||
|
||||
// map_vars
|
||||
// 0x51956C
|
||||
static int* gMapGlobalVars = NULL;
|
||||
int* gMapGlobalVars = NULL;
|
||||
|
||||
// local_vars_num
|
||||
// 0x519570
|
||||
|
@ -116,7 +116,7 @@ int gMapLocalVarsLength = 0;
|
|||
|
||||
// map_vars_num
|
||||
// 0x519574
|
||||
static int gMapGlobalVarsLength = 0;
|
||||
int gMapGlobalVarsLength = 0;
|
||||
|
||||
// Current elevation.
|
||||
//
|
||||
|
@ -1746,7 +1746,7 @@ static int mapHeaderWrite(MapHeader* ptr, File* stream)
|
|||
if (fileWriteInt32(stream, ptr->darkness) == -1) return -1;
|
||||
if (fileWriteInt32(stream, ptr->globalVariablesCount) == -1) return -1;
|
||||
if (fileWriteInt32(stream, ptr->field_34) == -1) return -1;
|
||||
if (fileWriteInt32(stream, ptr->lastVisitTime) == -1) return -1;
|
||||
if (fileWriteUInt32(stream, ptr->lastVisitTime) == -1) return -1;
|
||||
if (fileWriteInt32List(stream, ptr->field_3C, 44) == -1) return -1;
|
||||
|
||||
return 0;
|
||||
|
@ -1766,7 +1766,7 @@ static int mapHeaderRead(MapHeader* ptr, File* stream)
|
|||
if (fileReadInt32(stream, &(ptr->darkness)) == -1) return -1;
|
||||
if (fileReadInt32(stream, &(ptr->globalVariablesCount)) == -1) return -1;
|
||||
if (fileReadInt32(stream, &(ptr->field_34)) == -1) return -1;
|
||||
if (fileReadInt32(stream, &(ptr->lastVisitTime)) == -1) return -1;
|
||||
if (fileReadUInt32(stream, &(ptr->lastVisitTime)) == -1) return -1;
|
||||
if (fileReadInt32List(stream, ptr->field_3C, 44) == -1) return -1;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -54,7 +54,7 @@ typedef struct MapHeader {
|
|||
int field_34;
|
||||
|
||||
// Time in game ticks when PC last visited this map.
|
||||
int lastVisitTime;
|
||||
unsigned int lastVisitTime;
|
||||
int field_3C[44];
|
||||
} MapHeader;
|
||||
|
||||
|
@ -69,7 +69,9 @@ typedef void IsoWindowRefreshProc(Rect* rect);
|
|||
|
||||
extern int gMapSid;
|
||||
extern int* gMapLocalVars;
|
||||
extern int* gMapGlobalVars;
|
||||
extern int gMapLocalVarsLength;
|
||||
extern int gMapGlobalVarsLength;
|
||||
extern int gElevation;
|
||||
|
||||
extern MessageList gMapMessageList;
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
#include "mapper/map_func.h"
|
||||
|
||||
#include "actions.h"
|
||||
#include "color.h"
|
||||
#include "game_mouse.h"
|
||||
#include "input.h"
|
||||
#include "map.h"
|
||||
#include "memory.h"
|
||||
#include "mouse.h"
|
||||
#include "proto.h"
|
||||
#include "svga.h"
|
||||
#include "tile.h"
|
||||
#include "window_manager.h"
|
||||
#include "window_manager_private.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// 0x5595CC
|
||||
static bool block_obj_view_on = false;
|
||||
|
||||
// 0x4825B0
|
||||
void setup_map_dirs()
|
||||
{
|
||||
// TODO: Incomplete.
|
||||
}
|
||||
|
||||
// 0x4826B4
|
||||
void copy_proto_lists()
|
||||
{
|
||||
// TODO: Incomplete.
|
||||
}
|
||||
|
||||
// 0x482708
|
||||
void place_entrance_hex()
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int tile;
|
||||
|
||||
while (inputGetInput() != -2) {
|
||||
}
|
||||
|
||||
if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) {
|
||||
if (_mouse_click_in(0, 0, _scr_size.right - _scr_size.left, _scr_size.bottom - _scr_size.top - 100)) {
|
||||
mouseGetPosition(&x, &y);
|
||||
|
||||
tile = tileFromScreenXY(x, y, gElevation);
|
||||
if (tile != -1) {
|
||||
if (tileSetCenter(tile, TILE_SET_CENTER_FLAG_IGNORE_SCROLL_RESTRICTIONS) == 0) {
|
||||
mapSetEnteringLocation(tile, gElevation, rotation);
|
||||
} else {
|
||||
win_timed_msg("ERROR: Entrance out of range!", _colorTable[31744]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4841C4
|
||||
void pick_region(Rect* rect)
|
||||
{
|
||||
Rect temp;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
gameMouseSetCursor(MOUSE_CURSOR_PLUS);
|
||||
gameMouseObjectsHide();
|
||||
|
||||
while (1) {
|
||||
if (inputGetInput() == -2
|
||||
&& (mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get_input_position(&x, &y);
|
||||
temp.left = x;
|
||||
temp.top = y;
|
||||
temp.right = x;
|
||||
temp.bottom = y;
|
||||
|
||||
while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) == 0) {
|
||||
inputGetInput();
|
||||
|
||||
get_input_position(&x, &y);
|
||||
|
||||
if (x != temp.right || y != temp.bottom) {
|
||||
erase_rect(rect);
|
||||
sort_rect(rect, &temp);
|
||||
draw_rect(rect, _colorTable[32747]);
|
||||
}
|
||||
}
|
||||
|
||||
erase_rect(rect);
|
||||
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
|
||||
gameMouseObjectsShow();
|
||||
}
|
||||
|
||||
// 0x484294
|
||||
void sort_rect(Rect* a, Rect* b)
|
||||
{
|
||||
if (b->right > b->left) {
|
||||
a->left = b->left;
|
||||
a->right = b->right;
|
||||
} else {
|
||||
a->left = b->right;
|
||||
a->right = b->left;
|
||||
}
|
||||
|
||||
if (b->bottom > b->top) {
|
||||
a->top = b->top;
|
||||
a->bottom = b->bottom;
|
||||
} else {
|
||||
a->top = b->bottom;
|
||||
a->bottom = b->top;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4842D4
|
||||
void draw_rect(Rect* rect, unsigned char color)
|
||||
{
|
||||
int width = rect->right - rect->left;
|
||||
int height = rect->bottom - rect->top;
|
||||
int max_dimension;
|
||||
|
||||
if (height < width) {
|
||||
max_dimension = width;
|
||||
} else {
|
||||
max_dimension = height;
|
||||
}
|
||||
|
||||
unsigned char* buffer = (unsigned char*)internal_malloc(max_dimension);
|
||||
if (buffer != NULL) {
|
||||
memset(buffer, color, max_dimension);
|
||||
_scr_blit(buffer, width, 1, 0, 0, width, 1, rect->left, rect->top);
|
||||
_scr_blit(buffer, 1, height, 0, 0, 1, height, rect->left, rect->top);
|
||||
_scr_blit(buffer, width, 1, 0, 0, width, 1, rect->left, rect->bottom);
|
||||
_scr_blit(buffer, 1, height, 0, 0, 1, height, rect->right, rect->top);
|
||||
internal_free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4843A0
|
||||
void erase_rect(Rect* rect)
|
||||
{
|
||||
Rect r = *rect;
|
||||
|
||||
r.bottom = rect->top;
|
||||
windowRefreshAll(&r);
|
||||
|
||||
r.bottom = rect->bottom;
|
||||
r.left = rect->right;
|
||||
windowRefreshAll(&r);
|
||||
|
||||
r.left = rect->left;
|
||||
r.top = rect->bottom;
|
||||
windowRefreshAll(&r);
|
||||
|
||||
r.top = rect->top;
|
||||
r.right = rect->left;
|
||||
windowRefreshAll(&r);
|
||||
}
|
||||
|
||||
// 0x484400
|
||||
int toolbar_proto(int type, int id)
|
||||
{
|
||||
if (id < proto_max_id(type)) {
|
||||
return (type << 24) | id;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x485D44
|
||||
bool map_toggle_block_obj_viewing_on()
|
||||
{
|
||||
return block_obj_view_on;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef FALLOUT_MAPPER_MAP_FUNC_H_
|
||||
#define FALLOUT_MAPPER_MAP_FUNC_H_
|
||||
|
||||
#include "geometry.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
void setup_map_dirs();
|
||||
void copy_proto_lists();
|
||||
void place_entrance_hex();
|
||||
void pick_region(Rect* rect);
|
||||
void sort_rect(Rect* a, Rect* b);
|
||||
void draw_rect(Rect* rect, unsigned char color);
|
||||
void erase_rect(Rect* rect);
|
||||
int toolbar_proto(int type, int id);
|
||||
bool map_toggle_block_obj_viewing_on();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_MAPPER_MAP_FUNC_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
|||
#ifndef FALLOUT_MAPPER_MAPPER_H_
|
||||
#define FALLOUT_MAPPER_MAPPER_H_
|
||||
|
||||
#include "map.h"
|
||||
#include "obj_types.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
extern MapTransition mapInfo;
|
||||
|
||||
extern int menu_val_0[8];
|
||||
extern int menu_val_2[8];
|
||||
extern int menu_val_1[21];
|
||||
extern unsigned char* tool;
|
||||
extern int tool_win;
|
||||
|
||||
int mapper_main(int argc, char** argv);
|
||||
void print_toolbar_name(int object_type);
|
||||
int mapper_inven_unwield(Object* obj, int right_hand);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_MAPPER_MAPPER_H_ */
|
|
@ -0,0 +1,607 @@
|
|||
#include "mapper/mp_proto.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "combat_ai.h"
|
||||
#include "critter.h"
|
||||
#include "input.h"
|
||||
#include "kb.h"
|
||||
#include "mapper/mp_targt.h"
|
||||
#include "memory.h"
|
||||
#include "proto.h"
|
||||
#include "svga.h"
|
||||
#include "window_manager.h"
|
||||
#include "window_manager_private.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define CRITTER_FLAG_COUNT 10
|
||||
|
||||
#define YES 0
|
||||
#define NO 1
|
||||
|
||||
static int proto_choose_container_flags(Proto* proto);
|
||||
static int proto_subdata_setup_int_button(const char* title, int key, int value, int min_value, int max_value, int* y, int a7);
|
||||
static int proto_subdata_setup_fid_button(const char* title, int key, int fid, int* y, int a5);
|
||||
static int proto_subdata_setup_pid_button(const char* title, int key, int pid, int* y, int a5);
|
||||
static void proto_critter_flags_redraw(int win, int pid);
|
||||
static int proto_critter_flags_modify(int pid);
|
||||
static int mp_pick_kill_type();
|
||||
|
||||
static char kYes[] = "YES";
|
||||
static char kNo[] = "NO";
|
||||
|
||||
// 0x53DAFC
|
||||
static char default_proto_builder_name[36] = "EVERTS SCOTTY";
|
||||
|
||||
// 0x559924
|
||||
char* proto_builder_name = default_proto_builder_name;
|
||||
|
||||
// 0x559B94
|
||||
static const char* wall_light_strs[] = {
|
||||
"North/South",
|
||||
"East/West",
|
||||
"North Corner",
|
||||
"South Corner",
|
||||
"East Corner",
|
||||
"West Corner",
|
||||
};
|
||||
|
||||
// 0x559C50
|
||||
static char* yesno[] = {
|
||||
kYes,
|
||||
kNo,
|
||||
};
|
||||
|
||||
// 0x559C58
|
||||
int edit_window_color = 1;
|
||||
|
||||
// 0x559C60
|
||||
bool can_modify_protos = false;
|
||||
|
||||
// 0x559C68
|
||||
static int subwin = -1;
|
||||
|
||||
// 0x559C6C
|
||||
static int critFlagList[CRITTER_FLAG_COUNT] = {
|
||||
CRITTER_NO_STEAL,
|
||||
CRITTER_NO_DROP,
|
||||
CRITTER_NO_LIMBS,
|
||||
CRITTER_NO_AGE,
|
||||
CRITTER_NO_HEAL,
|
||||
CRITTER_INVULNERABLE,
|
||||
CRITTER_FLAT,
|
||||
CRITTER_SPECIAL_DEATH,
|
||||
CRITTER_LONG_LIMBS,
|
||||
CRITTER_NO_KNOCKBACK,
|
||||
};
|
||||
|
||||
// 0x559C94
|
||||
static const char* critFlagStrs[CRITTER_FLAG_COUNT] = {
|
||||
"_Steal",
|
||||
"_Drop",
|
||||
"_Limbs",
|
||||
"_Ages",
|
||||
"_Heal",
|
||||
"Invuln.,",
|
||||
"_Flattens",
|
||||
"Special",
|
||||
"Rng",
|
||||
"_Knock",
|
||||
};
|
||||
|
||||
// 0x4922F8
|
||||
void init_mapper_protos()
|
||||
{
|
||||
edit_window_color = _colorTable[10570];
|
||||
can_modify_protos = target_overriden();
|
||||
}
|
||||
|
||||
// 0x492840
|
||||
int proto_choose_container_flags(Proto* proto)
|
||||
{
|
||||
int win = windowCreate(320,
|
||||
185,
|
||||
220,
|
||||
205,
|
||||
edit_window_color,
|
||||
WINDOW_MOVE_ON_TOP);
|
||||
if (win == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_win_register_text_button(win,
|
||||
10,
|
||||
11,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
'1',
|
||||
"Magic Hands Grnd",
|
||||
0);
|
||||
|
||||
if ((proto->item.data.container.openFlags & 0x1) != 0) {
|
||||
windowDrawText(win,
|
||||
yesno[YES],
|
||||
50,
|
||||
125,
|
||||
15,
|
||||
_colorTable[32747] | 0x10000);
|
||||
} else {
|
||||
windowDrawText(win,
|
||||
yesno[NO],
|
||||
50,
|
||||
125,
|
||||
15,
|
||||
_colorTable[32747] | 0x10000);
|
||||
}
|
||||
|
||||
_win_register_text_button(win,
|
||||
10,
|
||||
32,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
'2',
|
||||
"Cannot Pick Up",
|
||||
0);
|
||||
|
||||
if (_proto_action_can_pickup(proto->pid)) {
|
||||
windowDrawText(win,
|
||||
yesno[YES],
|
||||
50,
|
||||
125,
|
||||
36,
|
||||
_colorTable[32747] | 0x10000);
|
||||
} else {
|
||||
windowDrawText(win,
|
||||
yesno[NO],
|
||||
50,
|
||||
125,
|
||||
36,
|
||||
_colorTable[32747] | 0x10000);
|
||||
}
|
||||
|
||||
windowDrawBorder(win);
|
||||
windowRefresh(win);
|
||||
|
||||
while (1) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int input = inputGetInput();
|
||||
if (input == KEY_ESCAPE
|
||||
|| input == KEY_BAR
|
||||
|| input == KEY_RETURN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (input == '1') {
|
||||
proto->item.data.container.openFlags ^= 0x1;
|
||||
|
||||
if ((proto->item.data.container.openFlags & 0x1) != 0) {
|
||||
windowDrawText(win,
|
||||
yesno[YES],
|
||||
50,
|
||||
125,
|
||||
15,
|
||||
_colorTable[32747] | 0x10000);
|
||||
} else {
|
||||
windowDrawText(win,
|
||||
yesno[NO],
|
||||
50,
|
||||
125,
|
||||
15,
|
||||
_colorTable[32747] | 0x10000);
|
||||
}
|
||||
|
||||
windowRefresh(win);
|
||||
} else if (input == '2') {
|
||||
proto->item.extendedFlags ^= 0x8000;
|
||||
|
||||
if (_proto_action_can_pickup(proto->pid)) {
|
||||
windowDrawText(win,
|
||||
yesno[YES],
|
||||
50,
|
||||
125,
|
||||
36,
|
||||
_colorTable[32747] | 0x10000);
|
||||
} else {
|
||||
windowDrawText(win,
|
||||
yesno[NO],
|
||||
50,
|
||||
125,
|
||||
36,
|
||||
_colorTable[32747] | 0x10000);
|
||||
}
|
||||
|
||||
windowRefresh(win);
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
windowDestroy(win);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x492A3C
|
||||
int proto_subdata_setup_int_button(const char* title, int key, int value, int min_value, int max_value, int* y, int a7)
|
||||
{
|
||||
char text[36];
|
||||
int button_x;
|
||||
int value_offset_x;
|
||||
|
||||
button_x = 10;
|
||||
value_offset_x = 90;
|
||||
|
||||
if (a7 == 9) {
|
||||
*y -= 189;
|
||||
}
|
||||
|
||||
if (a7 > 8) {
|
||||
button_x = 165;
|
||||
value_offset_x -= 16;
|
||||
}
|
||||
|
||||
_win_register_text_button(subwin,
|
||||
button_x,
|
||||
*y,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
key,
|
||||
title,
|
||||
0);
|
||||
|
||||
if (value >= min_value && value < max_value) {
|
||||
sprintf(text, "%d", value);
|
||||
windowDrawText(subwin,
|
||||
text,
|
||||
38,
|
||||
button_x + value_offset_x,
|
||||
*y + 4,
|
||||
_colorTable[32747] | 0x10000);
|
||||
} else {
|
||||
windowDrawText(subwin,
|
||||
"<ERROR>",
|
||||
38,
|
||||
button_x + value_offset_x,
|
||||
*y + 4,
|
||||
_colorTable[31744] | 0x10000);
|
||||
}
|
||||
|
||||
*y += 21;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x492B28
|
||||
int proto_subdata_setup_fid_button(const char* title, int key, int fid, int* y, int a5)
|
||||
{
|
||||
char text[36];
|
||||
char* pch;
|
||||
int button_x;
|
||||
int value_offset_x;
|
||||
|
||||
button_x = 10;
|
||||
value_offset_x = 90;
|
||||
|
||||
if (a5 == 9) {
|
||||
*y -= 189;
|
||||
}
|
||||
|
||||
if (a5 > 8) {
|
||||
button_x = 165;
|
||||
value_offset_x -= 16;
|
||||
}
|
||||
|
||||
_win_register_text_button(subwin,
|
||||
button_x,
|
||||
*y,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
key,
|
||||
title,
|
||||
0);
|
||||
|
||||
if (art_list_str(fid, text) != -1) {
|
||||
pch = strchr(text, '.');
|
||||
if (pch != NULL) {
|
||||
*pch = '\0';
|
||||
}
|
||||
|
||||
windowDrawText(subwin,
|
||||
text,
|
||||
80,
|
||||
button_x + value_offset_x,
|
||||
*y + 4,
|
||||
_colorTable[32747] | 0x10000);
|
||||
} else {
|
||||
windowDrawText(subwin,
|
||||
"None",
|
||||
80,
|
||||
button_x + value_offset_x,
|
||||
*y + 4,
|
||||
_colorTable[992] | 0x10000);
|
||||
}
|
||||
|
||||
*y += 21;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x492C20
|
||||
int proto_subdata_setup_pid_button(const char* title, int key, int pid, int* y, int a5)
|
||||
{
|
||||
int button_x;
|
||||
int value_offset_x;
|
||||
|
||||
button_x = 10;
|
||||
value_offset_x = 90;
|
||||
|
||||
if (a5 == 9) {
|
||||
*y -= 189;
|
||||
}
|
||||
|
||||
if (a5 > 8) {
|
||||
button_x = 165;
|
||||
value_offset_x = 74;
|
||||
}
|
||||
|
||||
_win_register_text_button(subwin,
|
||||
button_x,
|
||||
*y,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
key,
|
||||
title,
|
||||
0);
|
||||
|
||||
if (pid != -1) {
|
||||
windowDrawText(subwin,
|
||||
protoGetName(pid),
|
||||
49,
|
||||
button_x + value_offset_x,
|
||||
*y + 4,
|
||||
_colorTable[32747] | 0x10000);
|
||||
} else {
|
||||
windowDrawText(subwin,
|
||||
"None",
|
||||
49,
|
||||
button_x + value_offset_x,
|
||||
*y + 4,
|
||||
_colorTable[992] | 0x10000);
|
||||
}
|
||||
|
||||
*y += 21;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x495438
|
||||
const char* proto_wall_light_str(int flags)
|
||||
{
|
||||
if ((flags & 0x8000000) != 0) {
|
||||
return wall_light_strs[1];
|
||||
}
|
||||
|
||||
if ((flags & 0x10000000) != 0) {
|
||||
return wall_light_strs[2];
|
||||
}
|
||||
|
||||
if ((flags & 0x20000000) != 0) {
|
||||
return wall_light_strs[3];
|
||||
}
|
||||
|
||||
if ((flags & 0x40000000) != 0) {
|
||||
return wall_light_strs[4];
|
||||
}
|
||||
|
||||
if ((flags & 0x80000000) != 0) {
|
||||
return wall_light_strs[5];
|
||||
}
|
||||
|
||||
return wall_light_strs[0];
|
||||
}
|
||||
|
||||
// 0x4960B8
|
||||
void proto_critter_flags_redraw(int win, int pid)
|
||||
{
|
||||
int index;
|
||||
int color;
|
||||
int x = 110;
|
||||
|
||||
for (index = 0; index < CRITTER_FLAG_COUNT; index++) {
|
||||
if (_critter_flag_check(pid, critFlagList[index])) {
|
||||
color = _colorTable[992];
|
||||
} else {
|
||||
color = _colorTable[10570];
|
||||
}
|
||||
|
||||
windowDrawText(win, critFlagStrs[index], 44, x, 195, color | 0x10000);
|
||||
x += 48;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x496120
|
||||
int proto_critter_flags_modify(int pid)
|
||||
{
|
||||
Proto* proto;
|
||||
int rc;
|
||||
int flags = 0;
|
||||
int index;
|
||||
|
||||
if (protoGetProto(pid, &proto) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Can't be stolen from?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_NO_STEAL;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Can't Drop items?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_NO_DROP;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Can't lose limbs?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_NO_LIMBS;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Dead Bodies Can't Age?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_NO_AGE;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Can't Heal by Aging?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_NO_HEAL;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Is Invlunerable????", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_INVULNERABLE;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Can't Flatten on Death?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_FLAT;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Has Special Death?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_SPECIAL_DEATH;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Has Extra Hand-To-Hand Range?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_LONG_LIMBS;
|
||||
}
|
||||
|
||||
rc = win_yes_no("Can't be knocked back?", 340, 200, _colorTable[15855]);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
flags |= CRITTER_NO_KNOCKBACK;
|
||||
}
|
||||
|
||||
if (!can_modify_protos) {
|
||||
win_timed_msg("Can't modify protos!", _colorTable[31744] | 0x10000);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (index = 0; index < CRITTER_FLAG_COUNT; index++) {
|
||||
if ((critFlagList[index] & flags) != 0) {
|
||||
critter_flag_set(pid, critFlagList[index]);
|
||||
} else {
|
||||
critter_flag_unset(pid, critFlagList[index]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x497520
|
||||
int mp_pick_kill_type()
|
||||
{
|
||||
char* names[KILL_TYPE_COUNT];
|
||||
int index;
|
||||
|
||||
for (index = 0; index < KILL_TYPE_COUNT; index++) {
|
||||
names[index] = killTypeGetName(index);
|
||||
}
|
||||
|
||||
return _win_list_select("Kill Type",
|
||||
names,
|
||||
KILL_TYPE_COUNT,
|
||||
NULL,
|
||||
50,
|
||||
100,
|
||||
_colorTable[15855]);
|
||||
}
|
||||
|
||||
// 0x497568
|
||||
int proto_pick_ai_packet(int* value)
|
||||
{
|
||||
int count;
|
||||
char** names;
|
||||
int index;
|
||||
int rc;
|
||||
|
||||
count = combat_ai_num();
|
||||
if (count <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
names = (char**)internal_malloc(sizeof(char*) * count);
|
||||
for (index = 0; index < count; index++) {
|
||||
names[index] = (char*)internal_malloc(strlen(combat_ai_name(index)) + 1);
|
||||
strcpy(names[index], combat_ai_name(index));
|
||||
}
|
||||
|
||||
rc = _win_list_select("AI Packet",
|
||||
names,
|
||||
count,
|
||||
NULL,
|
||||
50,
|
||||
100,
|
||||
_colorTable[15855]);
|
||||
if (rc != -1) {
|
||||
*value = rc;
|
||||
}
|
||||
|
||||
for (index = 0; index < count; index++) {
|
||||
internal_free(names[index]);
|
||||
}
|
||||
|
||||
internal_free(names);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef FALLOUT_MAPPER_MP_PROTO_H_
|
||||
#define FALLOUT_MAPPER_MP_PROTO_H_
|
||||
|
||||
namespace fallout {
|
||||
|
||||
extern char* proto_builder_name;
|
||||
extern bool can_modify_protos;
|
||||
|
||||
void init_mapper_protos();
|
||||
const char* proto_wall_light_str(int flags);
|
||||
int proto_pick_ai_packet(int* value);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_MAPPER_MP_PROTO_H_ */
|
|
@ -0,0 +1,90 @@
|
|||
#include "mapper/mp_scrpt.h"
|
||||
|
||||
#include "art.h"
|
||||
#include "object.h"
|
||||
#include "scripts.h"
|
||||
#include "tile.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// 0x49B170
|
||||
int map_scr_remove_spatial(int tile, int elevation)
|
||||
{
|
||||
Script* scr;
|
||||
Object* obj;
|
||||
Rect rect;
|
||||
|
||||
scr = scriptGetFirstSpatialScript(elevation);
|
||||
while (scr != NULL) {
|
||||
if (builtTileGetTile(scr->sp.built_tile) == tile) {
|
||||
scriptRemove(scr->sid);
|
||||
|
||||
scr = scriptGetFirstSpatialScript(elevation);
|
||||
continue;
|
||||
}
|
||||
|
||||
scr = scriptGetNextSpatialScript();
|
||||
}
|
||||
|
||||
obj = objectFindFirstAtElevation(elevation);
|
||||
while (obj != NULL) {
|
||||
if (obj->tile == tile && buildFid(OBJ_TYPE_INTERFACE, 3, 0, 0, 0) == obj->fid) {
|
||||
objectDestroy(obj, &rect);
|
||||
tileWindowRefreshRect(&rect, elevation);
|
||||
|
||||
obj = objectFindFirstAtElevation(elevation);
|
||||
continue;
|
||||
}
|
||||
|
||||
obj = objectFindNextAtElevation();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49B214
|
||||
int map_scr_remove_all_spatials()
|
||||
{
|
||||
int elevation;
|
||||
Script* scr;
|
||||
Object* obj;
|
||||
int sid;
|
||||
|
||||
for (elevation = 0; elevation < ELEVATION_COUNT; elevation++) {
|
||||
scr = scriptGetFirstSpatialScript(elevation);
|
||||
while (scr != NULL) {
|
||||
scriptRemove(scr->sid);
|
||||
|
||||
scr = scriptGetFirstSpatialScript(elevation);
|
||||
}
|
||||
|
||||
obj = objectFindFirstAtElevation(elevation);
|
||||
while (obj != NULL) {
|
||||
if (buildFid(OBJ_TYPE_INTERFACE, 3, 0, 0, 0) == obj->fid) {
|
||||
objectDestroy(obj, NULL);
|
||||
|
||||
obj = objectFindFirstAtElevation(elevation);
|
||||
continue;
|
||||
}
|
||||
|
||||
obj = objectFindNextAtElevation();
|
||||
}
|
||||
}
|
||||
|
||||
tileWindowRefresh();
|
||||
|
||||
for (sid = 0; sid < 15000; sid++) {
|
||||
if (scriptGetScript(sid, &scr) != -1) {
|
||||
if (scr->owner != NULL) {
|
||||
if (scr->owner->pid == 0x500000C) {
|
||||
scr->owner->sid = -1;
|
||||
scriptRemove(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef FALLOUT_MAPPER_MP_SCRPTR_H_
|
||||
#define FALLOUT_MAPPER_MP_SCRPTR_H_
|
||||
|
||||
namespace fallout {
|
||||
|
||||
int map_scr_remove_spatial(int tile, int elevation);
|
||||
int map_scr_remove_all_spatials();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_MAPPER_MP_SCRPTR_H_ */
|
|
@ -0,0 +1,531 @@
|
|||
#include "mapper/mp_targt.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "art.h"
|
||||
#include "game.h"
|
||||
#include "map.h"
|
||||
#include "mapper/mp_proto.h"
|
||||
#include "memory.h"
|
||||
#include "proto.h"
|
||||
#include "window_manager_private.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define TARGET_DAT "target.dat"
|
||||
|
||||
typedef struct TargetNode {
|
||||
TargetSubNode subnode;
|
||||
struct TargetNode* next;
|
||||
} TargetNode;
|
||||
|
||||
typedef struct TargetList {
|
||||
TargetNode* tail;
|
||||
int count;
|
||||
int next_tid;
|
||||
} TargetList;
|
||||
|
||||
// 0x53F354
|
||||
static char default_target_path_base[] = "\\fallout2\\dev\\proto\\";
|
||||
|
||||
// 0x559CC4
|
||||
static TargetList targetlist = { NULL, 0, 0 };
|
||||
|
||||
// 0x559CD0
|
||||
static char* target_path_base = default_target_path_base;
|
||||
|
||||
// 0x559DBC
|
||||
static bool tgt_overriden = false;
|
||||
|
||||
// 0x49B2F0
|
||||
void target_override_protection()
|
||||
{
|
||||
char* name;
|
||||
|
||||
tgt_overriden = true;
|
||||
|
||||
name = getenv("MAIL_NAME");
|
||||
if (name != NULL) {
|
||||
// NOTE: Unsafe, backing buffer is 32 byte max.
|
||||
strcpy(proto_builder_name, strupr(name));
|
||||
} else {
|
||||
strcpy(proto_builder_name, "UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
// 0x49B2F0
|
||||
bool target_overriden()
|
||||
{
|
||||
return tgt_overriden;
|
||||
}
|
||||
|
||||
// 0x49B34C
|
||||
void target_make_path(char* path, int pid)
|
||||
{
|
||||
if (_cd_path_base[0] != '\0' && _cd_path_base[1] == ':') {
|
||||
strncpy(path, _cd_path_base, 2);
|
||||
strcat(path, target_path_base);
|
||||
} else {
|
||||
strcpy(path, target_path_base);
|
||||
}
|
||||
|
||||
if (pid != -1) {
|
||||
strcat(path, artGetObjectTypeName(PID_TYPE(pid)));
|
||||
}
|
||||
}
|
||||
|
||||
// 0x49B424
|
||||
int target_init()
|
||||
{
|
||||
target_remove_all();
|
||||
target_header_load();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49B434
|
||||
int target_exit()
|
||||
{
|
||||
if (can_modify_protos) {
|
||||
target_header_save();
|
||||
target_remove_all();
|
||||
} else {
|
||||
target_remove_all();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49B454
|
||||
int target_header_save()
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
FILE* stream;
|
||||
|
||||
target_make_path(path, -1);
|
||||
strcat(path, TARGET_DAT);
|
||||
|
||||
stream = fopen(path, "wb");
|
||||
if (stream == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(&targetlist, sizeof(targetlist), 1, stream) != 1) {
|
||||
// FIXME: Leaking `stream`.
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49B4E8
|
||||
int target_header_load()
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
FILE* stream;
|
||||
|
||||
target_make_path(path, -1);
|
||||
strcat(path, TARGET_DAT);
|
||||
|
||||
stream = fopen(path, "rb");
|
||||
if (stream == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fread(&targetlist, sizeof(targetlist), 1, stream) != 1) {
|
||||
// FIXME: Leaking `stream`.
|
||||
return -1;
|
||||
}
|
||||
|
||||
targetlist.tail = NULL;
|
||||
targetlist.count = 0;
|
||||
|
||||
fclose(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49B58C
|
||||
int target_save(int pid)
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
size_t len;
|
||||
char* extension;
|
||||
FILE* stream;
|
||||
TargetSubNode* subnode;
|
||||
|
||||
if (target_ptr(pid, &subnode) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
target_make_path(path, pid);
|
||||
|
||||
len = strlen(path);
|
||||
path[len] = '\\';
|
||||
_proto_list_str(pid, path + len + 1);
|
||||
|
||||
extension = strchr(path + len + 1, '.');
|
||||
if (extension != NULL) {
|
||||
strcpy(extension + 1, "tgt");
|
||||
} else {
|
||||
strcat(path, ".tgt");
|
||||
}
|
||||
|
||||
stream = fopen(path, "wb");
|
||||
if (stream == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (subnode != NULL) {
|
||||
fwrite(subnode, sizeof(TargetSubNode), 1, stream);
|
||||
subnode = subnode->next;
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49B6BC
|
||||
int target_load(int pid, TargetSubNode** subnode_ptr)
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
size_t len;
|
||||
char* extension;
|
||||
FILE* stream;
|
||||
TargetSubNode* subnode;
|
||||
|
||||
target_make_path(path, pid);
|
||||
|
||||
len = strlen(path);
|
||||
path[len] = '\\';
|
||||
_proto_list_str(pid, path + len + 1);
|
||||
|
||||
extension = strchr(path + len + 1, '.');
|
||||
if (extension != NULL) {
|
||||
strcpy(extension + 1, "tgt");
|
||||
} else {
|
||||
strcat(path, ".tgt");
|
||||
}
|
||||
|
||||
stream = fopen(path, "rb");
|
||||
if (stream == NULL) {
|
||||
*subnode_ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (target_find_free_subnode(&subnode) == -1) {
|
||||
*subnode_ptr = NULL;
|
||||
// FIXME: Leaks `stream`.
|
||||
return -1;
|
||||
}
|
||||
|
||||
fread(subnode, sizeof(TargetSubNode), 1, stream);
|
||||
|
||||
*subnode_ptr = subnode;
|
||||
|
||||
while (subnode->next != NULL) {
|
||||
subnode->next = (TargetSubNode*)internal_malloc(sizeof(TargetSubNode));
|
||||
if (subnode->next == NULL) {
|
||||
// FIXME: Leaks `stream`.
|
||||
return -1;
|
||||
}
|
||||
|
||||
subnode = subnode->next;
|
||||
fread(subnode, sizeof(TargetSubNode), 1, stream);
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49B9C0
|
||||
int target_find_free_subnode(TargetSubNode** subnode_ptr)
|
||||
{
|
||||
TargetNode* node = (TargetNode*)internal_malloc(sizeof(TargetNode));
|
||||
if (node == NULL) {
|
||||
*subnode_ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*subnode_ptr = &(node->subnode);
|
||||
|
||||
node->subnode.pid = -1;
|
||||
node->subnode.next = NULL;
|
||||
node->next = targetlist.tail;
|
||||
|
||||
targetlist.tail = node;
|
||||
targetlist.count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49BA10
|
||||
int target_new(int pid, int* tid_ptr)
|
||||
{
|
||||
TargetSubNode* subnode;
|
||||
TargetSubNode* new_subnode;
|
||||
|
||||
if (target_ptr(pid, &subnode) == -1) {
|
||||
if (target_find_free_subnode(&subnode) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
new_subnode = (TargetSubNode*)internal_malloc(sizeof(TargetSubNode));
|
||||
if (new_subnode == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
new_subnode->next = NULL;
|
||||
|
||||
while (subnode->next != NULL) {
|
||||
subnode = subnode->next;
|
||||
}
|
||||
|
||||
subnode->next = new_subnode;
|
||||
|
||||
new_subnode->pid = pid;
|
||||
new_subnode->tid = targetlist.next_tid;
|
||||
|
||||
*tid_ptr = targetlist.next_tid;
|
||||
|
||||
targetlist.count++;
|
||||
targetlist.next_tid++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49BBD4
|
||||
int target_remove(int pid)
|
||||
{
|
||||
TargetNode* node;
|
||||
TargetSubNode* subnode;
|
||||
TargetSubNode* subnode_next;
|
||||
|
||||
node = targetlist.tail;
|
||||
while (node != NULL) {
|
||||
if (node->subnode.pid == pid) {
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (node == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
subnode = node->subnode.next;
|
||||
|
||||
if (node == targetlist.tail) {
|
||||
targetlist.tail = targetlist.tail->next;
|
||||
}
|
||||
|
||||
internal_free(node);
|
||||
|
||||
while (subnode != NULL) {
|
||||
subnode_next = subnode->next;
|
||||
internal_free(subnode);
|
||||
subnode = subnode_next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49BC3C
|
||||
int target_remove_tid(int pid, int tid)
|
||||
{
|
||||
TargetNode* node;
|
||||
TargetSubNode* subnode;
|
||||
TargetSubNode* subnode_next;
|
||||
|
||||
node = targetlist.tail;
|
||||
while (node != NULL) {
|
||||
if (node->subnode.pid == pid) {
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (node == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->subnode.tid == tid) {
|
||||
subnode_next = node->subnode.next;
|
||||
if (subnode_next != NULL) {
|
||||
memcpy(&(node->subnode), subnode_next, sizeof(TargetSubNode));
|
||||
internal_free(subnode_next);
|
||||
} else {
|
||||
target_remove(pid);
|
||||
}
|
||||
|
||||
// FIXME: Should probably return 0 here.
|
||||
} else {
|
||||
subnode = &(node->subnode);
|
||||
while (subnode != NULL) {
|
||||
subnode_next = subnode->next;
|
||||
if (subnode_next->tid == tid) {
|
||||
subnode->next = subnode_next->next;
|
||||
internal_free(subnode_next);
|
||||
return 0;
|
||||
}
|
||||
|
||||
subnode = subnode_next;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x49BCBC
|
||||
int target_remove_all()
|
||||
{
|
||||
TargetNode* node;
|
||||
TargetNode* node_next;
|
||||
TargetSubNode* subnode;
|
||||
TargetSubNode* subnode_next;
|
||||
|
||||
node = targetlist.tail;
|
||||
targetlist.tail = NULL;
|
||||
|
||||
while (node != NULL) {
|
||||
node_next = node->next;
|
||||
|
||||
subnode = node->subnode.next;
|
||||
while (subnode != NULL) {
|
||||
subnode_next = subnode->next;
|
||||
internal_free(subnode);
|
||||
subnode = subnode_next;
|
||||
}
|
||||
|
||||
internal_free(node);
|
||||
node = node_next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49BD00
|
||||
int target_ptr(int pid, TargetSubNode** subnode_ptr)
|
||||
{
|
||||
TargetNode* node = targetlist.tail;
|
||||
while (node != NULL) {
|
||||
if (node->subnode.pid == pid) {
|
||||
*subnode_ptr = &(node->subnode);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return target_load(pid, subnode_ptr);
|
||||
}
|
||||
|
||||
// 0x49BD38
|
||||
int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr)
|
||||
{
|
||||
TargetSubNode* subnode;
|
||||
if (target_ptr(pid, &subnode) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (subnode != NULL) {
|
||||
if (subnode->tid == tid) {
|
||||
*subnode_ptr = subnode;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x49BD98
|
||||
int pick_rot()
|
||||
{
|
||||
int value;
|
||||
win_get_num_i(&value,
|
||||
-1,
|
||||
5,
|
||||
false,
|
||||
"Rotation",
|
||||
100,
|
||||
100);
|
||||
return value;
|
||||
}
|
||||
|
||||
// 0x49BDD0
|
||||
int target_pick_global_var(int* value_ptr)
|
||||
{
|
||||
int value;
|
||||
int rc;
|
||||
|
||||
if (gGameGlobalVarsLength == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = win_get_num_i(&value,
|
||||
0,
|
||||
gGameGlobalVarsLength - 1,
|
||||
false,
|
||||
"Global Variable Index #:",
|
||||
100,
|
||||
100);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value_ptr = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49BE20
|
||||
int target_pick_map_var(int* value_ptr)
|
||||
{
|
||||
int value;
|
||||
int rc;
|
||||
|
||||
if (gMapGlobalVarsLength == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = win_get_num_i(&value,
|
||||
0,
|
||||
gMapGlobalVarsLength - 1,
|
||||
false,
|
||||
"Map Variable Index #:",
|
||||
100,
|
||||
100);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value_ptr = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49BE70
|
||||
int target_pick_local_var(int* value_ptr)
|
||||
{
|
||||
int value;
|
||||
int rc;
|
||||
|
||||
if (gMapLocalVarsLength == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = win_get_num_i(&value,
|
||||
0,
|
||||
gMapLocalVarsLength - 1,
|
||||
false,
|
||||
"Local Variable Index #:",
|
||||
100,
|
||||
100);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value_ptr = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef FALLOUT_MAPPER_MP_TARGT_H_
|
||||
#define FALLOUT_MAPPER_MP_TARGT_H_
|
||||
|
||||
namespace fallout {
|
||||
|
||||
typedef struct TargetSubNode {
|
||||
int pid;
|
||||
int tid;
|
||||
int field_8;
|
||||
int field_C;
|
||||
int field_10;
|
||||
int field_14;
|
||||
int field_18;
|
||||
int field_1C;
|
||||
int field_20;
|
||||
int field_24;
|
||||
struct TargetSubNode* next;
|
||||
int field_2C;
|
||||
int field_30;
|
||||
int field_34;
|
||||
int field_38;
|
||||
int field_3C;
|
||||
int field_40;
|
||||
int field_44;
|
||||
int field_48;
|
||||
int field_4C;
|
||||
int field_50;
|
||||
int field_54;
|
||||
int field_58;
|
||||
int field_5C;
|
||||
int field_60;
|
||||
int field_64;
|
||||
int field_68;
|
||||
int field_6C;
|
||||
int field_70;
|
||||
int field_74;
|
||||
int field_78;
|
||||
int field_7C;
|
||||
int field_80;
|
||||
int field_84;
|
||||
int field_88;
|
||||
int field_8C;
|
||||
int field_90;
|
||||
int field_94;
|
||||
int field_98;
|
||||
int field_9C;
|
||||
} TargetSubNode;
|
||||
|
||||
void target_override_protection();
|
||||
bool target_overriden();
|
||||
void target_make_path(char* path, int pid);
|
||||
int target_init();
|
||||
int target_exit();
|
||||
int target_header_save();
|
||||
int target_header_load();
|
||||
int target_save(int pid);
|
||||
int target_load(int pid, TargetSubNode** subnode_ptr);
|
||||
int target_find_free_subnode(TargetSubNode** subnode_ptr);
|
||||
int target_new(int pid, int* tid_ptr);
|
||||
int target_remove(int pid);
|
||||
int target_remove_tid(int pid, int tid);
|
||||
int target_remove_all();
|
||||
int target_ptr(int pid, TargetSubNode** subnode_ptr);
|
||||
int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr);
|
||||
int pick_rot();
|
||||
int target_pick_global_var(int* value_ptr);
|
||||
int target_pick_map_var(int* value_ptr);
|
||||
int target_pick_local_var(int* value_ptr);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_MAPPER_MP_TARGT_H_ */
|
|
@ -0,0 +1,13 @@
|
|||
#include "mapper/mp_text.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// 0x49DAC4
|
||||
int proto_build_all_texts()
|
||||
{
|
||||
// TODO: Incomplete.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef FALLOUT_MAPPER_MP_TEXT_H_
|
||||
#define FALLOUT_MAPPER_MP_TEXT_H_
|
||||
|
||||
namespace fallout {
|
||||
|
||||
int proto_build_all_texts();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_MAPPER_MP_TEXT_H_ */
|
|
@ -32,7 +32,6 @@ typedef struct MemoryBlockFooter {
|
|||
static void* memoryBlockMallocImpl(size_t size);
|
||||
static void* memoryBlockReallocImpl(void* ptr, size_t size);
|
||||
static void memoryBlockFreeImpl(void* ptr);
|
||||
static void memoryBlockPrintStats();
|
||||
static void* mem_prep_block(void* block, size_t size);
|
||||
static void memoryBlockValidate(void* block);
|
||||
|
||||
|
@ -176,10 +175,8 @@ static void memoryBlockFreeImpl(void* ptr)
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: Not used.
|
||||
//
|
||||
// 0x4C5C5C
|
||||
static void memoryBlockPrintStats()
|
||||
void mem_check()
|
||||
{
|
||||
if (gMallocProc == memoryBlockMallocImpl) {
|
||||
debugPrint("Current memory allocated: %6d blocks, %9u bytes total\n", gMemoryBlocksCurrentCount, gMemoryBlocksCurrentSize);
|
||||
|
|
|
@ -9,6 +9,7 @@ char* internal_strdup(const char* string);
|
|||
void* internal_malloc(size_t size);
|
||||
void* internal_realloc(void* ptr, size_t size);
|
||||
void internal_free(void* ptr);
|
||||
void mem_check();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "audio_engine.h"
|
||||
#include "delay.h"
|
||||
#include "platform_compat.h"
|
||||
|
||||
namespace fallout {
|
||||
|
@ -794,6 +795,8 @@ static int _syncWait()
|
|||
if (_sync_active) {
|
||||
if (((_sync_time + 1000 * compat_timeGetTime()) & 0x80000000) != 0) {
|
||||
result = 1;
|
||||
|
||||
delay_ms(-(_sync_time + 1000 * compat_timeGetTime()) / 1000 - 3);
|
||||
while (((_sync_time + 1000 * compat_timeGetTime()) & 0x80000000) != 0)
|
||||
;
|
||||
}
|
||||
|
@ -1148,6 +1151,7 @@ static int _MVE_sndConfigure(int a1, int a2, int a3, int a4, int a5, int a6)
|
|||
}
|
||||
|
||||
// 0x4F56C0
|
||||
// Looks like this function is not used
|
||||
static void _MVE_syncSync()
|
||||
{
|
||||
if (_sync_active) {
|
||||
|
@ -1294,6 +1298,10 @@ static void _MVE_sndSync()
|
|||
break;
|
||||
}
|
||||
v0 = true;
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
delay_ms(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dword_6B3660 != dword_6B3AE4) {
|
||||
|
@ -1318,6 +1326,10 @@ static int _syncWaitLevel(int a1)
|
|||
v2 = _sync_time + a1;
|
||||
do {
|
||||
result = v2 + 1000 * compat_timeGetTime();
|
||||
if (result < 0) {
|
||||
delay_ms(-result / 1000 - 3);
|
||||
}
|
||||
result = v2 + 1000 * compat_timeGetTime();
|
||||
} while (result < 0);
|
||||
|
||||
_sync_time += _sync_wait_quanta;
|
||||
|
|
|
@ -2375,10 +2375,10 @@ bool _obj_occupied(int tile, int elevation)
|
|||
}
|
||||
|
||||
// 0x48B848
|
||||
Object* _obj_blocking_at(Object* a1, int tile, int elev)
|
||||
Object* _obj_blocking_at(Object* excludeObj, int tile, int elev)
|
||||
{
|
||||
ObjectListNode* objectListNode;
|
||||
Object* v7;
|
||||
Object* obj;
|
||||
int type;
|
||||
|
||||
if (!hexGridTileIsValid(tile)) {
|
||||
|
@ -2387,14 +2387,14 @@ Object* _obj_blocking_at(Object* a1, int tile, int elev)
|
|||
|
||||
objectListNode = gObjectListHeadByTile[tile];
|
||||
while (objectListNode != NULL) {
|
||||
v7 = objectListNode->obj;
|
||||
if (v7->elevation == elev) {
|
||||
if ((v7->flags & OBJECT_HIDDEN) == 0 && (v7->flags & OBJECT_NO_BLOCK) == 0 && v7 != a1) {
|
||||
type = FID_TYPE(v7->fid);
|
||||
obj = objectListNode->obj;
|
||||
if (obj->elevation == elev) {
|
||||
if ((obj->flags & OBJECT_HIDDEN) == 0 && (obj->flags & OBJECT_NO_BLOCK) == 0 && obj != excludeObj) {
|
||||
type = FID_TYPE(obj->fid);
|
||||
if (type == OBJ_TYPE_CRITTER
|
||||
|| type == OBJ_TYPE_SCENERY
|
||||
|| type == OBJ_TYPE_WALL) {
|
||||
return v7;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2406,15 +2406,15 @@ Object* _obj_blocking_at(Object* a1, int tile, int elev)
|
|||
if (hexGridTileIsValid(neighboor)) {
|
||||
objectListNode = gObjectListHeadByTile[neighboor];
|
||||
while (objectListNode != NULL) {
|
||||
v7 = objectListNode->obj;
|
||||
if ((v7->flags & OBJECT_MULTIHEX) != 0) {
|
||||
if (v7->elevation == elev) {
|
||||
if ((v7->flags & OBJECT_HIDDEN) == 0 && (v7->flags & OBJECT_NO_BLOCK) == 0 && v7 != a1) {
|
||||
type = FID_TYPE(v7->fid);
|
||||
obj = objectListNode->obj;
|
||||
if ((obj->flags & OBJECT_MULTIHEX) != 0) {
|
||||
if (obj->elevation == elev) {
|
||||
if ((obj->flags & OBJECT_HIDDEN) == 0 && (obj->flags & OBJECT_NO_BLOCK) == 0 && obj != excludeObj) {
|
||||
type = FID_TYPE(obj->fid);
|
||||
if (type == OBJ_TYPE_CRITTER
|
||||
|| type == OBJ_TYPE_SCENERY
|
||||
|| type == OBJ_TYPE_WALL) {
|
||||
return v7;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2428,7 +2428,7 @@ Object* _obj_blocking_at(Object* a1, int tile, int elev)
|
|||
}
|
||||
|
||||
// 0x48B930
|
||||
Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev)
|
||||
Object* _obj_shoot_blocking_at(Object* excludeObj, int tile, int elev)
|
||||
{
|
||||
if (!hexGridTileIsValid(tile)) {
|
||||
return NULL;
|
||||
|
@ -2439,7 +2439,7 @@ Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev)
|
|||
Object* candidate = objectListItem->obj;
|
||||
if (candidate->elevation == elev) {
|
||||
unsigned int flags = candidate->flags;
|
||||
if ((flags & OBJECT_HIDDEN) == 0 && ((flags & OBJECT_NO_BLOCK) == 0 || (flags & OBJECT_SHOOT_THRU) == 0) && candidate != obj) {
|
||||
if ((flags & OBJECT_HIDDEN) == 0 && ((flags & OBJECT_NO_BLOCK) == 0 || (flags & OBJECT_SHOOT_THRU) == 0) && candidate != excludeObj) {
|
||||
int type = FID_TYPE(candidate->fid);
|
||||
// SFALL: Fix to prevent corpses from blocking line of fire.
|
||||
if ((type == OBJ_TYPE_CRITTER && !critterIsDead(candidate))
|
||||
|
@ -2464,7 +2464,7 @@ Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev)
|
|||
unsigned int flags = candidate->flags;
|
||||
if ((flags & OBJECT_MULTIHEX) != 0) {
|
||||
if (candidate->elevation == elev) {
|
||||
if ((flags & OBJECT_HIDDEN) == 0 && (flags & OBJECT_NO_BLOCK) == 0 && candidate != obj) {
|
||||
if ((flags & OBJECT_HIDDEN) == 0 && (flags & OBJECT_NO_BLOCK) == 0 && candidate != excludeObj) {
|
||||
int type = FID_TYPE(candidate->fid);
|
||||
// SFALL: Fix to prevent corpses from blocking line of
|
||||
// fire.
|
||||
|
@ -2484,7 +2484,7 @@ Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev)
|
|||
}
|
||||
|
||||
// 0x48BA20
|
||||
Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation)
|
||||
Object* _obj_ai_blocking_at(Object* excludeObj, int tile, int elevation)
|
||||
{
|
||||
if (!hexGridTileIsValid(tile)) {
|
||||
return NULL;
|
||||
|
@ -2496,7 +2496,7 @@ Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation)
|
|||
if (object->elevation == elevation) {
|
||||
if ((object->flags & OBJECT_HIDDEN) == 0
|
||||
&& (object->flags & OBJECT_NO_BLOCK) == 0
|
||||
&& object != a1) {
|
||||
&& object != excludeObj) {
|
||||
int objectType = FID_TYPE(object->fid);
|
||||
if (objectType == OBJ_TYPE_CRITTER
|
||||
|| objectType == OBJ_TYPE_SCENERY
|
||||
|
@ -2525,7 +2525,7 @@ Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation)
|
|||
if (object->elevation == elevation) {
|
||||
if ((object->flags & OBJECT_HIDDEN) == 0
|
||||
&& (object->flags & OBJECT_NO_BLOCK) == 0
|
||||
&& object != a1) {
|
||||
&& object != excludeObj) {
|
||||
int objectType = FID_TYPE(object->fid);
|
||||
if (objectType == OBJ_TYPE_CRITTER
|
||||
|| objectType == OBJ_TYPE_SCENERY
|
||||
|
@ -2571,7 +2571,7 @@ int _obj_scroll_blocking_at(int tile, int elev)
|
|||
}
|
||||
|
||||
// 0x48BB88
|
||||
Object* _obj_sight_blocking_at(Object* a1, int tile, int elevation)
|
||||
Object* _obj_sight_blocking_at(Object* excludeObj, int tile, int elevation)
|
||||
{
|
||||
ObjectListNode* objectListNode = gObjectListHeadByTile[tile];
|
||||
while (objectListNode != NULL) {
|
||||
|
@ -2579,7 +2579,7 @@ Object* _obj_sight_blocking_at(Object* a1, int tile, int elevation)
|
|||
if (object->elevation == elevation
|
||||
&& (object->flags & OBJECT_HIDDEN) == 0
|
||||
&& (object->flags & OBJECT_LIGHT_THRU) == 0
|
||||
&& object != a1) {
|
||||
&& object != excludeObj) {
|
||||
int objectType = FID_TYPE(object->fid);
|
||||
if (objectType == OBJ_TYPE_SCENERY || objectType == OBJ_TYPE_WALL) {
|
||||
return object;
|
||||
|
|
|
@ -71,11 +71,11 @@ Object* objectFindFirstAtLocation(int elevation, int tile);
|
|||
Object* objectFindNextAtLocation();
|
||||
void objectGetRect(Object* obj, Rect* rect);
|
||||
bool _obj_occupied(int tile_num, int elev);
|
||||
Object* _obj_blocking_at(Object* a1, int tile_num, int elev);
|
||||
Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev);
|
||||
Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation);
|
||||
Object* _obj_blocking_at(Object* excludeObj, int tile_num, int elev);
|
||||
Object* _obj_shoot_blocking_at(Object* excludeObj, int tile, int elev);
|
||||
Object* _obj_ai_blocking_at(Object* excludeObj, int tile, int elevation);
|
||||
int _obj_scroll_blocking_at(int tile_num, int elev);
|
||||
Object* _obj_sight_blocking_at(Object* a1, int tile_num, int elev);
|
||||
Object* _obj_sight_blocking_at(Object* excludeObj, int tile_num, int elev);
|
||||
int objectGetDistanceBetween(Object* object1, Object* object2);
|
||||
int objectGetDistanceBetweenTiles(Object* object1, int tile1, Object* object2, int tile2);
|
||||
int objectListCreate(int tile, int elevation, int objectType, Object*** objectsPtr);
|
||||
|
|
|
@ -64,22 +64,22 @@ typedef struct STRU_519DBC {
|
|||
int field_8; // early what?
|
||||
} STRU_519DBC;
|
||||
|
||||
typedef struct STRUCT_519DA8 {
|
||||
typedef struct PartyMemberListItem {
|
||||
Object* object;
|
||||
Script* script;
|
||||
int* vars;
|
||||
struct STRUCT_519DA8* next;
|
||||
} STRUCT_519DA8;
|
||||
struct PartyMemberListItem* next;
|
||||
} PartyMemberListItem;
|
||||
|
||||
static int partyMemberGetDescription(Object* object, PartyMemberDescription** partyMemberDescriptionPtr);
|
||||
static void partyMemberDescriptionInit(PartyMemberDescription* partyMemberDescription);
|
||||
static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1);
|
||||
static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1);
|
||||
static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1);
|
||||
static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1);
|
||||
static int _partyMemberNewObjID();
|
||||
static int _partyMemberNewObjIDRecurseFind(Object* object, int objectId);
|
||||
static int _partyMemberPrepItemSave(Object* object);
|
||||
static int _partyMemberItemSave(Object* object);
|
||||
static int _partyMemberItemRecover(STRUCT_519DA8* a1);
|
||||
static int _partyMemberItemRecover(PartyMemberListItem* a1);
|
||||
static int _partyMemberClearItemList();
|
||||
static int partyFixMultipleMembers();
|
||||
static int _partyMemberCopyLevelInfo(Object* object, int a2);
|
||||
|
@ -91,12 +91,12 @@ int gPartyMemberDescriptionsLength = 0;
|
|||
int* gPartyMemberPids = NULL;
|
||||
|
||||
//
|
||||
static STRUCT_519DA8* _itemSaveListHead = NULL;
|
||||
static PartyMemberListItem* _itemSaveListHead = NULL;
|
||||
|
||||
// List of party members, it's length is [gPartyMemberDescriptionsLength] + 20.
|
||||
//
|
||||
// 0x519DA8
|
||||
static STRUCT_519DA8* gPartyMembers = NULL;
|
||||
PartyMemberListItem* gPartyMembers = NULL;
|
||||
|
||||
// Number of critters added to party.
|
||||
//
|
||||
|
@ -150,7 +150,7 @@ int partyMembersInit()
|
|||
|
||||
memset(gPartyMemberPids, 0, sizeof(*gPartyMemberPids) * gPartyMemberDescriptionsLength);
|
||||
|
||||
gPartyMembers = (STRUCT_519DA8*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20));
|
||||
gPartyMembers = (PartyMemberListItem*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20));
|
||||
if (gPartyMembers == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
@ -379,7 +379,7 @@ int partyMemberAdd(Object* object)
|
|||
}
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (partyMember->object == object || partyMember->object->pid == object->pid) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ int partyMemberAdd(Object* object)
|
|||
return -1;
|
||||
}
|
||||
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[gPartyMembersLength]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[gPartyMembersLength]);
|
||||
partyMember->object = object;
|
||||
partyMember->script = NULL;
|
||||
partyMember->vars = NULL;
|
||||
|
@ -435,7 +435,7 @@ int partyMemberRemove(Object* object)
|
|||
|
||||
int index;
|
||||
for (index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (partyMember->object == object) {
|
||||
break;
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ int _partyMemberPrepSave()
|
|||
_partyStatePrepped = 1;
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
|
||||
if (index > 0) {
|
||||
ptr->object->flags &= ~(OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
||||
|
@ -499,7 +499,7 @@ int _partyMemberPrepSave()
|
|||
int _partyMemberUnPrepSave()
|
||||
{
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
|
||||
if (index > 0) {
|
||||
ptr->object->flags |= (OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
||||
|
@ -523,7 +523,7 @@ int partyMembersSave(File* stream)
|
|||
if (fileWriteInt32(stream, _partyMemberItemCount) == -1) return -1;
|
||||
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (fileWriteInt32(stream, partyMember->object->id) == -1) return -1;
|
||||
}
|
||||
|
||||
|
@ -547,7 +547,7 @@ int _partyMemberPrepLoad()
|
|||
_partyStatePrepped = 1;
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr_519DA8 = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr_519DA8 = &(gPartyMembers[index]);
|
||||
if (_partyMemberPrepLoadInstance(ptr_519DA8) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -558,7 +558,7 @@ int _partyMemberPrepLoad()
|
|||
|
||||
// partyMemberPrepLoadInstance
|
||||
// 0x49480C
|
||||
static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1)
|
||||
static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1)
|
||||
{
|
||||
Object* obj = a1->object;
|
||||
|
||||
|
@ -643,7 +643,7 @@ int _partyMemberRecoverLoad()
|
|||
debugPrint("[Party Member %d]: %s\n", index, critterGetName(gPartyMembers[index].object));
|
||||
}
|
||||
|
||||
STRUCT_519DA8* v6 = _itemSaveListHead;
|
||||
PartyMemberListItem* v6 = _itemSaveListHead;
|
||||
while (v6 != NULL) {
|
||||
_itemSaveListHead = v6->next;
|
||||
|
||||
|
@ -664,7 +664,7 @@ int _partyMemberRecoverLoad()
|
|||
|
||||
// partyMemberRecoverLoadInstance
|
||||
// 0x494A88
|
||||
static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1)
|
||||
static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1)
|
||||
{
|
||||
if (a1->script == NULL) {
|
||||
showMesageBox("\n Error!: partyMemberRecoverLoadInstance: No script!");
|
||||
|
@ -801,7 +801,7 @@ int _partyMemberSyncPosition()
|
|||
int n = 0;
|
||||
int distance = 2;
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
Object* partyMemberObj = partyMember->object;
|
||||
if ((partyMemberObj->flags & OBJECT_HIDDEN) == 0 && PID_TYPE(partyMemberObj->pid) == OBJ_TYPE_CRITTER) {
|
||||
int rotation;
|
||||
|
@ -833,7 +833,7 @@ int _partyMemberRestingHeal(int a1)
|
|||
}
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (PID_TYPE(partyMember->object->pid) == OBJ_TYPE_CRITTER) {
|
||||
int healingRate = critterGetStat(partyMember->object, STAT_HEALING_RATE);
|
||||
critterAdjustHitPoints(partyMember->object, v1 * healingRate);
|
||||
|
@ -860,7 +860,7 @@ Object* partyMemberFindByPid(int pid)
|
|||
bool _isPotentialPartyMember(Object* object)
|
||||
{
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (partyMember->object->pid == gPartyMemberPids[index]) {
|
||||
return true;
|
||||
}
|
||||
|
@ -976,7 +976,7 @@ static int _partyMemberNewObjIDRecurseFind(Object* obj, int objectId)
|
|||
int _partyMemberPrepItemSaveAll()
|
||||
{
|
||||
for (int partyMemberIndex = 0; partyMemberIndex < gPartyMembersLength; partyMemberIndex++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[partyMemberIndex]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[partyMemberIndex]);
|
||||
|
||||
Inventory* inventory = &(partyMember->object->data.inventory);
|
||||
for (int inventoryItemIndex = 0; inventoryItemIndex < inventory->length; inventoryItemIndex++) {
|
||||
|
@ -1025,7 +1025,7 @@ static int _partyMemberItemSave(Object* object)
|
|||
object->id = script->field_1C;
|
||||
}
|
||||
|
||||
STRUCT_519DA8* node = (STRUCT_519DA8*)internal_malloc(sizeof(*node));
|
||||
PartyMemberListItem* node = (PartyMemberListItem*)internal_malloc(sizeof(*node));
|
||||
if (node == NULL) {
|
||||
showMesageBox("\n Error!: partyMemberItemSave: Out of memory!");
|
||||
exit(1);
|
||||
|
@ -1053,7 +1053,7 @@ static int _partyMemberItemSave(Object* object)
|
|||
node->vars = NULL;
|
||||
}
|
||||
|
||||
STRUCT_519DA8* temp = _itemSaveListHead;
|
||||
PartyMemberListItem* temp = _itemSaveListHead;
|
||||
_itemSaveListHead = node;
|
||||
node->next = temp;
|
||||
}
|
||||
|
@ -1069,7 +1069,7 @@ static int _partyMemberItemSave(Object* object)
|
|||
|
||||
// partyMemberItemRecover
|
||||
// 0x495388
|
||||
static int _partyMemberItemRecover(STRUCT_519DA8* a1)
|
||||
static int _partyMemberItemRecover(PartyMemberListItem* a1)
|
||||
{
|
||||
int sid = -1;
|
||||
if (scriptAdd(&sid, SCRIPT_TYPE_ITEM) == -1) {
|
||||
|
@ -1108,7 +1108,7 @@ static int _partyMemberItemRecover(STRUCT_519DA8* a1)
|
|||
static int _partyMemberClearItemList()
|
||||
{
|
||||
while (_itemSaveListHead != NULL) {
|
||||
STRUCT_519DA8* node = _itemSaveListHead;
|
||||
PartyMemberListItem* node = _itemSaveListHead;
|
||||
_itemSaveListHead = _itemSaveListHead->next;
|
||||
|
||||
if (node->script != NULL) {
|
||||
|
@ -1262,7 +1262,7 @@ static int partyFixMultipleMembers()
|
|||
}
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
|
||||
Script* script;
|
||||
if (scriptGetScript(partyMember->object->sid, &script) != -1) {
|
||||
|
@ -1454,7 +1454,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse)
|
|||
int _partyMemberIncLevels()
|
||||
{
|
||||
int i;
|
||||
STRUCT_519DA8* ptr;
|
||||
PartyMemberListItem* ptr;
|
||||
Object* obj;
|
||||
PartyMemberDescription* party_member;
|
||||
const char* name;
|
||||
|
@ -1622,7 +1622,7 @@ static int _partyMemberCopyLevelInfo(Object* critter, int a2)
|
|||
bool partyIsAnyoneCanBeHealedByRest()
|
||||
{
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
Object* object = ptr->object;
|
||||
|
||||
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
||||
|
@ -1649,7 +1649,7 @@ int partyGetMaxWoundToHealByRest()
|
|||
int maxWound = 0;
|
||||
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
Object* object = ptr->object;
|
||||
|
||||
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
||||
|
@ -1670,4 +1670,20 @@ int partyGetMaxWoundToHealByRest()
|
|||
return maxWound;
|
||||
}
|
||||
|
||||
std::vector<Object*> get_all_party_members_objects(bool include_hidden)
|
||||
{
|
||||
std::vector<Object*> value;
|
||||
value.reserve(gPartyMembersLength);
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
auto object = gPartyMembers[index].object;
|
||||
if (include_hidden
|
||||
|| (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER
|
||||
&& !critterIsDead(object)
|
||||
&& (object->flags & OBJECT_HIDDEN) == 0)) {
|
||||
value.push_back(object);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#ifndef PARTY_MEMBER_H
|
||||
#define PARTY_MEMBER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "db.h"
|
||||
#include "obj_types.h"
|
||||
#include "scripts.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
|
@ -42,6 +45,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse);
|
|||
int _partyMemberIncLevels();
|
||||
bool partyIsAnyoneCanBeHealedByRest();
|
||||
int partyGetMaxWoundToHealByRest();
|
||||
std::vector<Object*> get_all_party_members_objects(bool include_hidden);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "cycle.h"
|
||||
#include "dbox.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_mouse.h"
|
||||
|
@ -1962,7 +1963,7 @@ static bool pipboyRest(int hours, int minutes, int duration)
|
|||
double v2 = v1 * (1.0 / 1440.0) * 3.5 + 0.25;
|
||||
double v3 = (double)minutes / v1 * v2;
|
||||
if (minutes != 0) {
|
||||
int gameTime = gameTimeGetTime();
|
||||
unsigned int gameTime = gameTimeGetTime();
|
||||
|
||||
double v4 = v3 * 20.0;
|
||||
int v5 = 0;
|
||||
|
@ -2001,8 +2002,7 @@ static bool pipboyRest(int hours, int minutes, int duration)
|
|||
pipboyDrawDate();
|
||||
windowRefresh(gPipboyWindow);
|
||||
|
||||
while (getTicksSince(start) < 50) {
|
||||
}
|
||||
delay_ms(50 - (getTicks() - start));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
@ -2025,7 +2025,7 @@ static bool pipboyRest(int hours, int minutes, int duration)
|
|||
}
|
||||
|
||||
if (hours != 0 && !rc) {
|
||||
int gameTime = gameTimeGetTime();
|
||||
unsigned int gameTime = gameTimeGetTime();
|
||||
double v7 = (v2 - v3) * 20.0;
|
||||
|
||||
for (int hour = 0; hour < (int)v7; hour++) {
|
||||
|
@ -2072,8 +2072,7 @@ static bool pipboyRest(int hours, int minutes, int duration)
|
|||
pipboyDrawHitPoints();
|
||||
windowRefresh(gPipboyWindow);
|
||||
|
||||
while (getTicksSince(start) < 50) {
|
||||
}
|
||||
delay_ms(50 - (getTicks() - start));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
@ -2143,9 +2142,7 @@ static bool pipboyRest(int hours, int minutes, int duration)
|
|||
}
|
||||
}
|
||||
|
||||
int gameTime = gameTimeGetTime();
|
||||
int nextEventGameTime = queueGetNextEventTime();
|
||||
if (gameTime > nextEventGameTime) {
|
||||
if (gameTimeGetTime() > queueGetNextEventTime()) {
|
||||
if (queueProcessEvents()) {
|
||||
debugPrint("PIPBOY: Returning from Queue trigger...\n");
|
||||
_proc_bail_flag = 1;
|
||||
|
@ -2368,8 +2365,7 @@ static int pipboyRenderScreensaver()
|
|||
v31 -= 1;
|
||||
} else {
|
||||
windowRefreshRect(gPipboyWindow, &gPipboyWindowContentRect);
|
||||
while (getTicksSince(time) < 50) {
|
||||
}
|
||||
delay_ms(50 - (getTicks() - time));
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "combat.h"
|
||||
#include "combat_ai.h"
|
||||
#include "debug.h"
|
||||
#include "delay.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_mouse.h"
|
||||
|
@ -1570,8 +1571,7 @@ static void _DoThing(int eventCode)
|
|||
blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_ON].getData(), 21, 12, 21, gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * meta->knobY + v31, PREFERENCES_WINDOW_WIDTH);
|
||||
windowRefresh(gPreferencesWindow);
|
||||
|
||||
while (getTicksSince(tick) < 35)
|
||||
;
|
||||
delay_ms(35 - (getTicks() - tick));
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
|
|
378
src/proto.cc
378
src/proto.cc
|
@ -24,7 +24,6 @@
|
|||
|
||||
namespace fallout {
|
||||
|
||||
static int _proto_critter_init(Proto* a1, int a2);
|
||||
static int objectCritterCombatDataRead(CritterCombatData* data, File* stream);
|
||||
static int objectCritterCombatDataWrite(CritterCombatData* data, File* stream);
|
||||
static int _proto_update_gen(Object* obj);
|
||||
|
@ -39,8 +38,7 @@ static int _proto_load_pid(int pid, Proto** out_proto);
|
|||
static int _proto_find_free_subnode(int type, Proto** out_ptr);
|
||||
static void _proto_remove_some_list(int type);
|
||||
static void _proto_remove_list(int type);
|
||||
static int _proto_new_id(int a1);
|
||||
static int _proto_max_id(int a1);
|
||||
static int _proto_new_id(int type);
|
||||
|
||||
// 0x50CF3C
|
||||
static char _aProto_0[] = "proto\\";
|
||||
|
@ -169,7 +167,7 @@ char* _proto_none_str;
|
|||
static char* gBodyTypeNames[BODY_TYPE_COUNT];
|
||||
|
||||
// 0x664834
|
||||
static char* gItemTypeNames[ITEM_TYPE_COUNT];
|
||||
char* gItemTypeNames[ITEM_TYPE_COUNT];
|
||||
|
||||
// 0x66484C
|
||||
static char* gDamageTypeNames[DAMAGE_TYPE_COUNT];
|
||||
|
@ -187,8 +185,8 @@ static char** _perk_code_strs;
|
|||
// 0x6648BC
|
||||
static char** _critter_stats_list;
|
||||
|
||||
// NOTE: Inlined.
|
||||
void _proto_make_path(char* path, int pid)
|
||||
// 0x49E270
|
||||
void proto_make_path(char* path, int pid)
|
||||
{
|
||||
strcpy(path, _cd_path_base);
|
||||
strcat(path, _proto_path_base);
|
||||
|
@ -211,7 +209,7 @@ int _proto_list_str(int pid, char* proto_path)
|
|||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
_proto_make_path(path, pid);
|
||||
proto_make_path(path, pid);
|
||||
strcat(path, "\\");
|
||||
strcat(path, artGetObjectTypeName(PID_TYPE(pid)));
|
||||
strcat(path, ".lst");
|
||||
|
@ -370,32 +368,143 @@ char* protoGetDescription(int pid)
|
|||
return protoGetMessage(pid, PROTOTYPE_MESSAGE_DESCRIPTION);
|
||||
}
|
||||
|
||||
// 0x49EB2C
|
||||
int proto_item_init(Proto* proto, int a2)
|
||||
{
|
||||
int v1 = a2 & 0xFFFFFF;
|
||||
|
||||
proto->item.pid = -1;
|
||||
proto->item.messageId = 100 * v1;
|
||||
proto->item.fid = buildFid(OBJ_TYPE_ITEM, v1 - 1, 0, 0, 0);
|
||||
if (!artExists(proto->item.fid)) {
|
||||
proto->item.fid = buildFid(OBJ_TYPE_ITEM, 0, 0, 0, 0);
|
||||
}
|
||||
proto->item.lightDistance = 0;
|
||||
proto->item.lightIntensity = 0;
|
||||
proto->item.flags = 0xA0000008;
|
||||
proto->item.extendedFlags = 0xA000;
|
||||
proto->item.sid = -1;
|
||||
proto->item.type = ITEM_TYPE_MISC;
|
||||
proto_item_subdata_init(proto, proto->item.type);
|
||||
proto->item.material = 1;
|
||||
proto->item.size = 1;
|
||||
proto->item.weight = 10;
|
||||
proto->item.cost = 0;
|
||||
proto->item.inventoryFid = -1;
|
||||
proto->item.field_80 = '0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49EBFC
|
||||
int proto_item_subdata_init(Proto* proto, int type)
|
||||
{
|
||||
int index;
|
||||
|
||||
switch (type) {
|
||||
case ITEM_TYPE_ARMOR:
|
||||
proto->item.data.armor.armorClass = 0;
|
||||
|
||||
for (index = 0; index < DAMAGE_TYPE_COUNT; index++) {
|
||||
proto->item.data.armor.damageResistance[index] = 0;
|
||||
proto->item.data.armor.damageThreshold[index] = 0;
|
||||
}
|
||||
|
||||
proto->item.data.armor.perk = -1;
|
||||
proto->item.data.armor.maleFid = -1;
|
||||
proto->item.data.armor.femaleFid = -1;
|
||||
break;
|
||||
case ITEM_TYPE_CONTAINER:
|
||||
proto->item.data.container.openFlags = 0;
|
||||
proto->item.data.container.maxSize = 250;
|
||||
proto->item.extendedFlags |= 0x800;
|
||||
break;
|
||||
case ITEM_TYPE_DRUG:
|
||||
proto->item.data.drug.stat[0] = STAT_STRENGTH;
|
||||
proto->item.data.drug.stat[1] = -1;
|
||||
proto->item.data.drug.stat[2] = -1;
|
||||
proto->item.data.drug.amount[0] = 0;
|
||||
proto->item.data.drug.amount[1] = 0;
|
||||
proto->item.data.drug.amount[2] = 0;
|
||||
proto->item.data.drug.duration1 = 0;
|
||||
proto->item.data.drug.amount1[0] = 0;
|
||||
proto->item.data.drug.amount1[1] = 0;
|
||||
proto->item.data.drug.amount1[2] = 0;
|
||||
proto->item.data.drug.duration2 = 0;
|
||||
proto->item.data.drug.amount2[0] = 0;
|
||||
proto->item.data.drug.amount2[1] = 0;
|
||||
proto->item.data.drug.amount2[2] = 0;
|
||||
proto->item.data.drug.addictionChance = 0;
|
||||
proto->item.data.drug.withdrawalEffect = 0;
|
||||
proto->item.data.drug.withdrawalOnset = 0;
|
||||
proto->item.extendedFlags |= 0x1000;
|
||||
break;
|
||||
case ITEM_TYPE_WEAPON:
|
||||
proto->item.data.weapon.animationCode = 0;
|
||||
proto->item.data.weapon.minDamage = 0;
|
||||
proto->item.data.weapon.maxDamage = 0;
|
||||
proto->item.data.weapon.damageType = 0;
|
||||
proto->item.data.weapon.maxRange1 = 0;
|
||||
proto->item.data.weapon.maxRange2 = 0;
|
||||
proto->item.data.weapon.projectilePid = -1;
|
||||
proto->item.data.weapon.minStrength = 0;
|
||||
proto->item.data.weapon.actionPointCost1 = 0;
|
||||
proto->item.data.weapon.actionPointCost2 = 0;
|
||||
proto->item.data.weapon.criticalFailureType = 0;
|
||||
proto->item.data.weapon.perk = -1;
|
||||
proto->item.data.weapon.rounds = 0;
|
||||
proto->item.data.weapon.caliber = 0;
|
||||
proto->item.data.weapon.ammoTypePid = -1;
|
||||
proto->item.data.weapon.ammoCapacity = 0;
|
||||
proto->item.data.weapon.soundCode = 0;
|
||||
break;
|
||||
case ITEM_TYPE_AMMO:
|
||||
proto->item.data.ammo.caliber = 0;
|
||||
proto->item.data.ammo.quantity = 20;
|
||||
proto->item.data.ammo.armorClassModifier = 0;
|
||||
proto->item.data.ammo.damageResistanceModifier = 0;
|
||||
proto->item.data.ammo.damageMultiplier = 1;
|
||||
proto->item.data.ammo.damageDivisor = 1;
|
||||
break;
|
||||
case ITEM_TYPE_MISC:
|
||||
proto->item.data.misc.powerTypePid = -1;
|
||||
proto->item.data.misc.powerType = 20;
|
||||
break;
|
||||
case ITEM_TYPE_KEY:
|
||||
proto->item.data.key.keyCode = -1;
|
||||
proto->item.extendedFlags |= 0x1000;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49EDB4
|
||||
static int _proto_critter_init(Proto* a1, int a2)
|
||||
int proto_critter_init(Proto* proto, int pid)
|
||||
{
|
||||
if (!_protos_been_initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int v1 = a2 & 0xFFFFFF;
|
||||
int num = pid & 0xFFFFFF;
|
||||
|
||||
a1->pid = -1;
|
||||
a1->messageId = 100 * v1;
|
||||
a1->fid = buildFid(OBJ_TYPE_CRITTER, v1 - 1, 0, 0, 0);
|
||||
a1->critter.lightDistance = 0;
|
||||
a1->critter.lightIntensity = 0;
|
||||
a1->critter.flags = 0x20000000;
|
||||
a1->critter.extendedFlags = 0x6000;
|
||||
a1->critter.sid = -1;
|
||||
a1->critter.data.flags = 0;
|
||||
a1->critter.data.bodyType = 0;
|
||||
a1->critter.headFid = -1;
|
||||
a1->critter.aiPacket = 1;
|
||||
if (!artExists(a1->fid)) {
|
||||
a1->fid = buildFid(OBJ_TYPE_CRITTER, 0, 0, 0, 0);
|
||||
proto->pid = -1;
|
||||
proto->messageId = 100 * num;
|
||||
proto->fid = buildFid(OBJ_TYPE_CRITTER, num - 1, 0, 0, 0);
|
||||
proto->critter.lightDistance = 0;
|
||||
proto->critter.lightIntensity = 0;
|
||||
proto->critter.flags = 0x20000000;
|
||||
proto->critter.extendedFlags = 0x6000;
|
||||
proto->critter.sid = -1;
|
||||
proto->critter.data.flags = 0;
|
||||
proto->critter.data.bodyType = 0;
|
||||
proto->critter.headFid = -1;
|
||||
proto->critter.aiPacket = 1;
|
||||
if (!artExists(proto->fid)) {
|
||||
proto->fid = buildFid(OBJ_TYPE_CRITTER, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
CritterProtoData* data = &(a1->critter.data);
|
||||
CritterProtoData* data = &(proto->critter.data);
|
||||
data->experience = 60;
|
||||
data->killType = 0;
|
||||
data->damageType = 0;
|
||||
|
@ -828,6 +937,165 @@ int _proto_dude_init(const char* path)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// 0x49FBBC
|
||||
int proto_scenery_init(Proto* proto, int pid)
|
||||
{
|
||||
int num = pid & 0xFFFFFF;
|
||||
|
||||
proto->scenery.pid = -1;
|
||||
proto->scenery.messageId = 100 * num;
|
||||
proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, num - 1, 0, 0, 0);
|
||||
if (!artExists(proto->scenery.fid)) {
|
||||
proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, 0, 0, 0, 0);
|
||||
}
|
||||
proto->scenery.lightDistance = 0;
|
||||
proto->scenery.lightIntensity = 0;
|
||||
proto->scenery.flags = 0;
|
||||
proto->scenery.extendedFlags = 0x2000;
|
||||
proto->scenery.sid = -1;
|
||||
proto->scenery.type = SCENERY_TYPE_GENERIC;
|
||||
proto_scenery_subdata_init(proto, proto->scenery.type);
|
||||
proto->scenery.field_2C = -1;
|
||||
proto->scenery.field_34 = '0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49FC74
|
||||
int proto_scenery_subdata_init(Proto* proto, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case SCENERY_TYPE_DOOR:
|
||||
proto->scenery.data.door.openFlags = 0;
|
||||
proto->scenery.extendedFlags |= 0x800;
|
||||
break;
|
||||
case SCENERY_TYPE_STAIRS:
|
||||
proto->scenery.data.stairs.field_0 = -1;
|
||||
proto->scenery.data.stairs.field_4 = -1;
|
||||
proto->scenery.extendedFlags |= 0x800;
|
||||
break;
|
||||
case SCENERY_TYPE_ELEVATOR:
|
||||
proto->scenery.data.elevator.type = -1;
|
||||
proto->scenery.data.elevator.level = -1;
|
||||
proto->scenery.extendedFlags |= 0x800;
|
||||
break;
|
||||
case SCENERY_TYPE_LADDER_UP:
|
||||
proto->scenery.data.ladder.field_0 = -1;
|
||||
proto->scenery.extendedFlags |= 0x800;
|
||||
break;
|
||||
case SCENERY_TYPE_LADDER_DOWN:
|
||||
proto->scenery.data.ladder.field_0 = -1;
|
||||
proto->scenery.extendedFlags |= 0x800;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49FCFC
|
||||
int proto_wall_init(Proto* proto, int pid)
|
||||
{
|
||||
int num = pid & 0xFFFFFF;
|
||||
|
||||
proto->wall.pid = -1;
|
||||
proto->wall.messageId = 100 * num;
|
||||
proto->wall.fid = buildFid(OBJ_TYPE_WALL, num - 1, 0, 0, 0);
|
||||
if (!artExists(proto->wall.fid)) {
|
||||
proto->wall.fid = buildFid(OBJ_TYPE_WALL, 0, 0, 0, 0);
|
||||
}
|
||||
proto->wall.lightDistance = 0;
|
||||
proto->wall.lightIntensity = 0;
|
||||
proto->wall.flags = 0;
|
||||
proto->wall.extendedFlags = 0x2000;
|
||||
proto->wall.sid = -1;
|
||||
proto->wall.material = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49FD84
|
||||
int proto_tile_init(Proto* proto, int pid)
|
||||
{
|
||||
int num = pid & 0xFFFFFF;
|
||||
|
||||
proto->tile.pid = -1;
|
||||
proto->tile.messageId = 100 * num;
|
||||
proto->tile.fid = buildFid(OBJ_TYPE_TILE, num - 1, 0, 0, 0);
|
||||
if (!artExists(proto->tile.fid)) {
|
||||
proto->tile.fid = buildFid(OBJ_TYPE_TILE, 0, 0, 0, 0);
|
||||
}
|
||||
proto->tile.flags = 0;
|
||||
proto->tile.extendedFlags = 0x2000;
|
||||
proto->tile.sid = -1;
|
||||
proto->tile.material = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49FDFC
|
||||
int proto_misc_init(Proto* proto, int pid)
|
||||
{
|
||||
int num = pid & 0xFFFFFF;
|
||||
|
||||
proto->misc.pid = -1;
|
||||
proto->misc.messageId = 100 * num;
|
||||
proto->misc.fid = buildFid(OBJ_TYPE_MISC, num - 1, 0, 0, 0);
|
||||
if (!artExists(proto->misc.fid)) {
|
||||
proto->misc.fid = buildFid(OBJ_TYPE_MISC, 0, 0, 0, 0);
|
||||
}
|
||||
proto->misc.lightDistance = 0;
|
||||
proto->misc.lightIntensity = 0;
|
||||
proto->misc.flags = 0;
|
||||
proto->misc.extendedFlags = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49FE74
|
||||
int proto_copy_proto(int srcPid, int dstPid)
|
||||
{
|
||||
int srcType;
|
||||
int dstType;
|
||||
Proto* src;
|
||||
Proto* dst;
|
||||
|
||||
srcType = PID_TYPE(srcPid);
|
||||
dstType = PID_TYPE(dstPid);
|
||||
if (srcType != dstType) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (protoGetProto(srcPid, &src) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (protoGetProto(dstPid, &dst) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(dst, src, _proto_sizes[srcType]);
|
||||
dst->pid = dstPid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49FEDC
|
||||
bool proto_is_subtype(Proto* proto, int subtype)
|
||||
{
|
||||
if (subtype == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (PID_TYPE(proto->pid)) {
|
||||
case OBJ_TYPE_ITEM:
|
||||
return proto->item.type == subtype;
|
||||
case OBJ_TYPE_SCENERY:
|
||||
return proto->scenery.type == subtype;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// proto_data_member
|
||||
// 0x49FFD8
|
||||
int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value)
|
||||
|
@ -1083,7 +1351,7 @@ int protoInit()
|
|||
compat_mkdir(path);
|
||||
|
||||
// TODO: Get rid of cast.
|
||||
_proto_critter_init((Proto*)&gDudeProto, 0x1000000);
|
||||
proto_critter_init((Proto*)&gDudeProto, 0x1000000);
|
||||
|
||||
gDudeProto.pid = 0x1000000;
|
||||
gDudeProto.fid = buildFid(OBJ_TYPE_CRITTER, 1, 0, 0, 0);
|
||||
|
@ -1202,7 +1470,7 @@ void protoReset()
|
|||
int i;
|
||||
|
||||
// TODO: Get rid of cast.
|
||||
_proto_critter_init((Proto*)&gDudeProto, 0x1000000);
|
||||
proto_critter_init((Proto*)&gDudeProto, 0x1000000);
|
||||
gDudeProto.pid = 0x1000000;
|
||||
gDudeProto.fid = buildFid(OBJ_TYPE_CRITTER, 1, 0, 0, 0);
|
||||
|
||||
|
@ -1251,7 +1519,7 @@ static int _proto_header_load()
|
|||
ptr->max_entries_num = 1;
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
_proto_make_path(path, index << 24);
|
||||
proto_make_path(path, index << 24);
|
||||
strcat(path, "\\");
|
||||
strcat(path, artGetObjectTypeName(index));
|
||||
strcat(path, ".lst");
|
||||
|
@ -1661,7 +1929,7 @@ int _proto_save_pid(int pid)
|
|||
}
|
||||
|
||||
char path[260];
|
||||
_proto_make_path(path, pid);
|
||||
proto_make_path(path, pid);
|
||||
strcat(path, "\\");
|
||||
|
||||
_proto_list_str(pid, path + strlen(path));
|
||||
|
@ -1682,7 +1950,7 @@ int _proto_save_pid(int pid)
|
|||
static int _proto_load_pid(int pid, Proto** protoPtr)
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
_proto_make_path(path, pid);
|
||||
proto_make_path(path, pid);
|
||||
strcat(path, "\\");
|
||||
|
||||
if (_proto_list_str(pid, path + strlen(path)) == -1) {
|
||||
|
@ -1761,6 +2029,48 @@ static int _proto_find_free_subnode(int type, Proto** protoPtr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// 0x4A1E90
|
||||
int proto_new(int* pid, int type)
|
||||
{
|
||||
Proto* proto;
|
||||
|
||||
if (_proto_find_free_subnode(type, &proto) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pid = _proto_new_id(type) | (type << 24);
|
||||
switch (type) {
|
||||
case OBJ_TYPE_ITEM:
|
||||
proto_item_init(proto, *pid);
|
||||
proto->item.pid = *pid;
|
||||
break;
|
||||
case OBJ_TYPE_CRITTER:
|
||||
proto_critter_init(proto, *pid);
|
||||
proto->critter.pid = *pid;
|
||||
break;
|
||||
case OBJ_TYPE_SCENERY:
|
||||
proto_scenery_init(proto, *pid);
|
||||
proto->scenery.pid = *pid;
|
||||
break;
|
||||
case OBJ_TYPE_WALL:
|
||||
proto_wall_init(proto, *pid);
|
||||
proto->wall.pid = *pid;
|
||||
break;
|
||||
case OBJ_TYPE_TILE:
|
||||
proto_tile_init(proto, *pid);
|
||||
proto->tile.pid = *pid;
|
||||
break;
|
||||
case OBJ_TYPE_MISC:
|
||||
proto_misc_init(proto, *pid);
|
||||
proto->misc.pid = *pid;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Evict top most proto cache block.
|
||||
//
|
||||
// 0x4A2040
|
||||
|
@ -1850,18 +2160,18 @@ int protoGetProto(int pid, Proto** protoPtr)
|
|||
}
|
||||
|
||||
// 0x4A21DC
|
||||
static int _proto_new_id(int a1)
|
||||
static int _proto_new_id(int type)
|
||||
{
|
||||
int result = _protoLists[a1].max_entries_num;
|
||||
_protoLists[a1].max_entries_num = result + 1;
|
||||
int result = _protoLists[type].max_entries_num;
|
||||
_protoLists[type].max_entries_num = result + 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 0x4A2214
|
||||
static int _proto_max_id(int a1)
|
||||
int proto_max_id(int type)
|
||||
{
|
||||
return _protoLists[a1].max_entries_num;
|
||||
return _protoLists[type].max_entries_num;
|
||||
}
|
||||
|
||||
// 0x4A22C0
|
||||
|
|
17
src/proto.h
17
src/proto.h
|
@ -101,8 +101,9 @@ extern char _cd_path_base[COMPAT_MAX_PATH];
|
|||
|
||||
extern MessageList gProtoMessageList;
|
||||
extern char* _proto_none_str;
|
||||
extern char* gItemTypeNames[ITEM_TYPE_COUNT];
|
||||
|
||||
void _proto_make_path(char* path, int pid);
|
||||
void proto_make_path(char* path, int pid);
|
||||
int _proto_list_str(int pid, char* proto_path);
|
||||
size_t proto_size(int type);
|
||||
bool _proto_action_can_use(int pid);
|
||||
|
@ -112,20 +113,32 @@ int _proto_action_can_pickup(int pid);
|
|||
char* protoGetMessage(int pid, int message);
|
||||
char* protoGetName(int pid);
|
||||
char* protoGetDescription(int pid);
|
||||
int proto_item_init(Proto* proto, int pid);
|
||||
int proto_item_subdata_init(Proto* proto, int type);
|
||||
int proto_critter_init(Proto* proto, int pid);
|
||||
void objectDataReset(Object* obj);
|
||||
int objectDataRead(Object* obj, File* stream);
|
||||
int objectDataWrite(Object* obj, File* stream);
|
||||
int _proto_update_init(Object* obj);
|
||||
int _proto_dude_update_gender();
|
||||
int _proto_dude_init(const char* path);
|
||||
int proto_scenery_init(Proto* proto, int pid);
|
||||
int proto_scenery_subdata_init(Proto* proto, int type);
|
||||
int proto_wall_init(Proto* proto, int pid);
|
||||
int proto_tile_init(Proto* proto, int pid);
|
||||
int proto_misc_init(Proto* proto, int pid);
|
||||
int proto_copy_proto(int srcPid, int dstPid);
|
||||
bool proto_is_subtype(Proto* proto, int subtype);
|
||||
int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value);
|
||||
int protoInit();
|
||||
void protoReset();
|
||||
void protoExit();
|
||||
int _proto_save_pid(int pid);
|
||||
int proto_new(int* pid, int type);
|
||||
void _proto_remove_all();
|
||||
int protoGetProto(int pid, Proto** out_proto);
|
||||
int protoGetProto(int pid, Proto** protoPtr);
|
||||
int _ResetPlayer();
|
||||
int proto_max_id(int type);
|
||||
|
||||
static bool isExitGridPid(int pid)
|
||||
{
|
||||
|
|
79
src/queue.cc
79
src/queue.cc
|
@ -18,8 +18,7 @@
|
|||
namespace fallout {
|
||||
|
||||
typedef struct QueueListNode {
|
||||
// TODO: Make unsigned.
|
||||
int time;
|
||||
unsigned int time;
|
||||
int type;
|
||||
Object* owner;
|
||||
void* data;
|
||||
|
@ -102,7 +101,7 @@ int queueLoad(File* stream)
|
|||
break;
|
||||
}
|
||||
|
||||
if (fileReadInt32(stream, &(queueListNode->time)) == -1) {
|
||||
if (fileReadUInt32(stream, &(queueListNode->time)) == -1) {
|
||||
internal_free(queueListNode);
|
||||
rc = -1;
|
||||
break;
|
||||
|
@ -169,27 +168,20 @@ int queueLoad(File* stream)
|
|||
}
|
||||
}
|
||||
|
||||
if (oldListHead != NULL) {
|
||||
QueueListNode** v13 = &gQueueListHead;
|
||||
QueueListNode* v15;
|
||||
do {
|
||||
while (true) {
|
||||
QueueListNode* v14 = *v13;
|
||||
if (v14 == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (v14->time > oldListHead->time) {
|
||||
break;
|
||||
}
|
||||
|
||||
v13 = &(v14->next);
|
||||
while (oldListHead != NULL) {
|
||||
QueueListNode** queueListNodePtr = &gQueueListHead;
|
||||
while (*queueListNodePtr != NULL) {
|
||||
if ((*queueListNodePtr)->time > oldListHead->time) {
|
||||
break;
|
||||
}
|
||||
v15 = oldListHead->next;
|
||||
oldListHead->next = *v13;
|
||||
*v13 = oldListHead;
|
||||
oldListHead = v15;
|
||||
} while (v15 != NULL);
|
||||
|
||||
queueListNodePtr = &((*queueListNodePtr)->next);
|
||||
}
|
||||
|
||||
QueueListNode* next = oldListHead->next;
|
||||
oldListHead->next = *queueListNodePtr;
|
||||
*queueListNodePtr = oldListHead;
|
||||
oldListHead = next;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -217,7 +209,7 @@ int queueSave(File* stream)
|
|||
Object* object = queueListNode->owner;
|
||||
int objectId = object != NULL ? object->id : -2;
|
||||
|
||||
if (fileWriteInt32(stream, queueListNode->time) == -1) {
|
||||
if (fileWriteUInt32(stream, queueListNode->time) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -250,9 +242,7 @@ int queueAddEvent(int delay, Object* obj, void* data, int eventType)
|
|||
return -1;
|
||||
}
|
||||
|
||||
int v1 = gameTimeGetTime();
|
||||
int v2 = v1 + delay;
|
||||
newQueueListNode->time = v2;
|
||||
newQueueListNode->time = gameTimeGetTime() + delay;
|
||||
newQueueListNode->type = eventType;
|
||||
newQueueListNode->owner = obj;
|
||||
newQueueListNode->data = data;
|
||||
|
@ -261,22 +251,18 @@ int queueAddEvent(int delay, Object* obj, void* data, int eventType)
|
|||
obj->flags |= OBJECT_QUEUED;
|
||||
}
|
||||
|
||||
QueueListNode** v3 = &gQueueListHead;
|
||||
QueueListNode** queueListNodePtr = &gQueueListHead;
|
||||
|
||||
if (gQueueListHead != NULL) {
|
||||
QueueListNode* v4;
|
||||
while (*queueListNodePtr != NULL) {
|
||||
if (newQueueListNode->time < (*queueListNodePtr)->time) {
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
v4 = *v3;
|
||||
if (v2 < v4->time) {
|
||||
break;
|
||||
}
|
||||
v3 = &(v4->next);
|
||||
} while (v4->next != NULL);
|
||||
queueListNodePtr = &((*queueListNodePtr)->next);
|
||||
}
|
||||
|
||||
newQueueListNode->next = *v3;
|
||||
*v3 = newQueueListNode;
|
||||
newQueueListNode->next = *queueListNodePtr;
|
||||
*queueListNodePtr = newQueueListNode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -357,19 +343,20 @@ bool queueHasEvent(Object* owner, int eventType)
|
|||
// 0x4A26D0
|
||||
int queueProcessEvents()
|
||||
{
|
||||
int time = gameTimeGetTime();
|
||||
int v1 = 0;
|
||||
unsigned int time = gameTimeGetTime();
|
||||
// TODO: this is 0 or 1, but in some cases -1. Probably needs to be bool.
|
||||
int stopProcess = 0;
|
||||
|
||||
while (gQueueListHead != NULL) {
|
||||
QueueListNode* queueListNode = gQueueListHead;
|
||||
if (time < queueListNode->time || v1 != 0) {
|
||||
if (time < queueListNode->time || stopProcess != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
gQueueListHead = queueListNode->next;
|
||||
|
||||
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[queueListNode->type]);
|
||||
v1 = eventTypeDescription->handlerProc(queueListNode->owner, queueListNode->data);
|
||||
stopProcess = eventTypeDescription->handlerProc(queueListNode->owner, queueListNode->data);
|
||||
|
||||
if (eventTypeDescription->freeProc != NULL) {
|
||||
eventTypeDescription->freeProc(queueListNode->data);
|
||||
|
@ -378,7 +365,7 @@ int queueProcessEvents()
|
|||
internal_free(queueListNode);
|
||||
}
|
||||
|
||||
return v1;
|
||||
return stopProcess;
|
||||
}
|
||||
|
||||
// 0x4A2748
|
||||
|
@ -437,10 +424,8 @@ void _queue_clear_type(int eventType, QueueEventHandler* fn)
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Make unsigned.
|
||||
//
|
||||
// 0x4A2808
|
||||
int queueGetNextEventTime()
|
||||
unsigned int queueGetNextEventTime()
|
||||
{
|
||||
if (gQueueListHead == NULL) {
|
||||
return 0;
|
||||
|
|
|
@ -66,7 +66,7 @@ bool queueHasEvent(Object* owner, int eventType);
|
|||
int queueProcessEvents();
|
||||
void queueClear();
|
||||
void _queue_clear_type(int eventType, QueueEventHandler* fn);
|
||||
int queueGetNextEventTime();
|
||||
unsigned int queueGetNextEventTime();
|
||||
void _queue_leaving_map();
|
||||
bool queueIsEmpty();
|
||||
void* queueFindFirstEvent(Object* owner, int eventType);
|
||||
|
|
|
@ -100,7 +100,7 @@ int randomRoll(int difficulty, int criticalSuccessModifier, int* howMuchPtr)
|
|||
// 0x4A3030
|
||||
static int randomTranslateRoll(int delta, int criticalSuccessModifier)
|
||||
{
|
||||
int gameTime = gameTimeGetTime();
|
||||
unsigned int gameTime = gameTimeGetTime();
|
||||
|
||||
// SFALL: Remove criticals time limits.
|
||||
bool criticalsTimeLimitsRemoved = false;
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
#include "proto.h"
|
||||
#include "proto_instance.h"
|
||||
#include "queue.h"
|
||||
#include "sfall_arrays.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "tile.h"
|
||||
|
@ -125,7 +127,7 @@ static int _script_engine_game_mode = 0;
|
|||
// Game time in ticks (1/10 second).
|
||||
//
|
||||
// 0x51C720
|
||||
static int gGameTime = 302400;
|
||||
static unsigned int gGameTime = 302400;
|
||||
|
||||
// 0x51C724
|
||||
static const int gGameTimeDaysPerMonth[12] = {
|
||||
|
@ -144,7 +146,7 @@ static const int gGameTimeDaysPerMonth[12] = {
|
|||
};
|
||||
|
||||
// 0x51C758
|
||||
static const char* gScriptProcNames[28] = {
|
||||
const char* gScriptProcNames[SCRIPT_PROC_COUNT] = {
|
||||
"no_p_proc",
|
||||
"start",
|
||||
"spatial_p_proc",
|
||||
|
@ -275,12 +277,10 @@ static int gMovieTimerArtimer2;
|
|||
static int gMovieTimerArtimer3;
|
||||
static int gMovieTimerArtimer4;
|
||||
|
||||
// TODO: Make unsigned.
|
||||
//
|
||||
// Returns game time in ticks (1/10 second).
|
||||
//
|
||||
// 0x4A3330
|
||||
int gameTimeGetTime()
|
||||
unsigned int gameTimeGetTime()
|
||||
{
|
||||
return gGameTime;
|
||||
}
|
||||
|
@ -349,7 +349,7 @@ char* gameTimeGetTimeString()
|
|||
// TODO: Make unsigned.
|
||||
//
|
||||
// 0x4A347C
|
||||
void gameTimeSetTime(int time)
|
||||
void gameTimeSetTime(unsigned int time)
|
||||
{
|
||||
if (time == 0) {
|
||||
time = 1;
|
||||
|
@ -405,7 +405,7 @@ int gameTimeScheduleUpdateEvent()
|
|||
int gameTimeEventProcess(Object* obj, void* data)
|
||||
{
|
||||
int movie_index;
|
||||
int v4;
|
||||
int stopProcess;
|
||||
|
||||
movie_index = -1;
|
||||
|
||||
|
@ -421,17 +421,17 @@ int gameTimeEventProcess(Object* obj, void* data)
|
|||
_scriptsCheckGameEvents(&movie_index, -1);
|
||||
}
|
||||
|
||||
v4 = _critter_check_rads(gDude);
|
||||
stopProcess = _critter_check_rads(gDude);
|
||||
|
||||
_queue_clear_type(4, 0);
|
||||
|
||||
gameTimeScheduleUpdateEvent();
|
||||
|
||||
if (movie_index != -1) {
|
||||
v4 = 1;
|
||||
stopProcess = 1;
|
||||
}
|
||||
|
||||
return v4;
|
||||
return stopProcess;
|
||||
}
|
||||
|
||||
// 0x4A3690
|
||||
|
@ -773,9 +773,7 @@ static void _script_chk_timed_events()
|
|||
|
||||
if (v1) {
|
||||
while (!queueIsEmpty()) {
|
||||
int time = gameTimeGetTime();
|
||||
int v2 = queueGetNextEventTime();
|
||||
if (time < v2) {
|
||||
if (gameTimeGetTime() < queueGetNextEventTime()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -915,8 +913,8 @@ int scriptsHandleRequests()
|
|||
}
|
||||
}
|
||||
|
||||
if ((gScriptsRequests & SCRIPT_REQUEST_0x02) != 0) {
|
||||
gScriptsRequests &= ~SCRIPT_REQUEST_0x02;
|
||||
if ((gScriptsRequests & SCRIPT_REQUEST_TOWN_MAP) != 0) {
|
||||
gScriptsRequests &= ~SCRIPT_REQUEST_TOWN_MAP;
|
||||
wmTownMap();
|
||||
}
|
||||
|
||||
|
@ -1027,6 +1025,8 @@ int scriptsHandleRequests()
|
|||
inventoryOpenStealing(gScriptsRequestedStealingBy, gScriptsRequestedStealingFrom);
|
||||
}
|
||||
|
||||
DeleteAllTempArrays();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1128,6 +1128,16 @@ void _scripts_request_combat_locked(STRUCT_664980* a1)
|
|||
gScriptsRequests |= (SCRIPT_REQUEST_0x0400 | SCRIPT_REQUEST_COMBAT);
|
||||
}
|
||||
|
||||
// 0x4A461C
|
||||
void scripts_request_townmap()
|
||||
{
|
||||
if (isInCombat()) {
|
||||
_game_user_wants_to_quit = 1;
|
||||
}
|
||||
|
||||
gScriptsRequests |= SCRIPT_REQUEST_TOWN_MAP;
|
||||
}
|
||||
|
||||
// request_world_map()
|
||||
// 0x4A4644
|
||||
void scriptsRequestWorldMap()
|
||||
|
@ -1988,6 +1998,8 @@ static int scriptRead(Script* scr, File* stream)
|
|||
scr->localVarsCount = 0;
|
||||
}
|
||||
|
||||
scr->overriddenSelf = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2204,6 +2216,8 @@ int scriptAdd(int* sidPtr, int scriptType)
|
|||
scr->procs[index] = SCRIPT_PROC_NO_PROC;
|
||||
}
|
||||
|
||||
scr->overriddenSelf = nullptr;
|
||||
|
||||
scriptListExtent->length++;
|
||||
|
||||
return 0;
|
||||
|
@ -2586,6 +2600,9 @@ void scriptsExecMapUpdateProc()
|
|||
// 0x4A67EC
|
||||
void scriptsExecMapUpdateScripts(int proc)
|
||||
{
|
||||
// SFALL: Run global scripts.
|
||||
sfall_gl_scr_exec_map_update_scripts(proc);
|
||||
|
||||
_scr_SpatialsEnabled = false;
|
||||
|
||||
int fixedParam = 0;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fallout {
|
|||
|
||||
typedef enum ScriptRequests {
|
||||
SCRIPT_REQUEST_COMBAT = 0x01,
|
||||
SCRIPT_REQUEST_0x02 = 0x02,
|
||||
SCRIPT_REQUEST_TOWN_MAP = 0x02,
|
||||
SCRIPT_REQUEST_WORLD_MAP = 0x04,
|
||||
SCRIPT_REQUEST_ELEVATOR = 0x08,
|
||||
SCRIPT_REQUEST_EXPLOSION = 0x10,
|
||||
|
@ -143,15 +143,19 @@ typedef struct Script {
|
|||
int field_D4;
|
||||
int field_D8;
|
||||
int field_DC;
|
||||
|
||||
Object* overriddenSelf;
|
||||
} Script;
|
||||
|
||||
int gameTimeGetTime();
|
||||
extern const char* gScriptProcNames[SCRIPT_PROC_COUNT];
|
||||
|
||||
unsigned int gameTimeGetTime();
|
||||
void gameTimeGetDate(int* monthPtr, int* dayPtr, int* yearPtr);
|
||||
int gameTimeGetHour();
|
||||
char* gameTimeGetTimeString();
|
||||
void gameTimeAddTicks(int a1);
|
||||
void gameTimeAddSeconds(int a1);
|
||||
void gameTimeSetTime(int time);
|
||||
void gameTimeSetTime(unsigned int time);
|
||||
int gameTimeScheduleUpdateEvent();
|
||||
int gameTimeEventProcess(Object* obj, void* data);
|
||||
int _scriptsCheckGameEvents(int* moviePtr, int window);
|
||||
|
@ -173,6 +177,7 @@ int scriptsHandleRequests();
|
|||
int _scripts_check_state_in_combat();
|
||||
int scriptsRequestCombat(STRUCT_664980* a1);
|
||||
void _scripts_request_combat_locked(STRUCT_664980* ptr);
|
||||
void scripts_request_townmap();
|
||||
void scriptsRequestWorldMap();
|
||||
int scriptsRequestElevator(Object* a1, int a2);
|
||||
int scriptsRequestExplosion(int tile, int elevation, int minDamage, int maxDamage);
|
||||
|
|
|
@ -0,0 +1,717 @@
|
|||
#include "sfall_arrays.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "interpreter.h"
|
||||
#include "sfall_lists.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
static constexpr ArrayId kInitialArrayId = 1;
|
||||
|
||||
#define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value
|
||||
#define ARRAY_MAX_SIZE (100000) // maximum number of array elements,
|
||||
|
||||
// special actions for arrays using array_resize operator
|
||||
#define ARRAY_ACTION_SORT (-2)
|
||||
#define ARRAY_ACTION_RSORT (-3)
|
||||
#define ARRAY_ACTION_REVERSE (-4)
|
||||
#define ARRAY_ACTION_SHUFFLE (-5)
|
||||
|
||||
template <class T, typename Compare>
|
||||
static void ListSort(std::vector<T>& arr, int type, Compare cmp)
|
||||
{
|
||||
switch (type) {
|
||||
case ARRAY_ACTION_SORT: // sort ascending
|
||||
std::sort(arr.begin(), arr.end(), cmp);
|
||||
break;
|
||||
case ARRAY_ACTION_RSORT: // sort descending
|
||||
std::sort(arr.rbegin(), arr.rend(), cmp);
|
||||
break;
|
||||
case ARRAY_ACTION_REVERSE: // reverse elements
|
||||
std::reverse(arr.rbegin(), arr.rend());
|
||||
break;
|
||||
case ARRAY_ACTION_SHUFFLE: // shuffle elements
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
std::shuffle(arr.begin(), arr.end(), g);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum class ArrayElementType {
|
||||
INT,
|
||||
FLOAT,
|
||||
STRING,
|
||||
POINTER
|
||||
};
|
||||
|
||||
/**
|
||||
* This is mostly the same as ProgramValue but it owns strings.
|
||||
*
|
||||
* This is done because when we pop dynamic string element from
|
||||
* the stack we decrease ref count for this string and it memory
|
||||
* can be freed.
|
||||
*
|
||||
* In theory arrays can be shared between programs so we also
|
||||
* have to copy static strings.
|
||||
*
|
||||
*/
|
||||
class ArrayElement {
|
||||
public:
|
||||
ArrayElement()
|
||||
: type(ArrayElementType::INT)
|
||||
, value({ 0 })
|
||||
{
|
||||
}
|
||||
|
||||
ArrayElement(const ArrayElement& other) = delete;
|
||||
|
||||
ArrayElement& operator=(const ArrayElement& rhs) = delete;
|
||||
|
||||
ArrayElement(ArrayElement&& other) noexcept
|
||||
: type(ArrayElementType::INT)
|
||||
, value({ 0 })
|
||||
{
|
||||
std::swap(type, other.type);
|
||||
std::swap(value, other.value);
|
||||
}
|
||||
|
||||
ArrayElement& operator=(ArrayElement&& other) noexcept
|
||||
{
|
||||
std::swap(type, other.type);
|
||||
std::swap(value, other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ArrayElement(ProgramValue programValue, Program* program)
|
||||
{
|
||||
switch (programValue.opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
type = ArrayElementType::INT;
|
||||
value.integerValue = programValue.integerValue;
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
type = ArrayElementType::FLOAT;
|
||||
value.floatValue = programValue.floatValue;
|
||||
break;
|
||||
case VALUE_TYPE_PTR:
|
||||
type = ArrayElementType::POINTER;
|
||||
value.pointerValue = programValue.pointerValue;
|
||||
break;
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
setString(programGetString(program, programValue.opcode, programValue.integerValue), -1);
|
||||
break;
|
||||
default:
|
||||
type = ArrayElementType::INT;
|
||||
value.integerValue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayElement(const char* str)
|
||||
{
|
||||
setString(str, -1);
|
||||
}
|
||||
|
||||
ArrayElement(const char* str, size_t sLen)
|
||||
{
|
||||
setString(str, sLen);
|
||||
}
|
||||
|
||||
ProgramValue toValue(Program* program) const
|
||||
{
|
||||
ProgramValue out;
|
||||
switch (type) {
|
||||
case ArrayElementType::INT:
|
||||
out.opcode = VALUE_TYPE_INT;
|
||||
out.integerValue = value.integerValue;
|
||||
break;
|
||||
case ArrayElementType::FLOAT:
|
||||
out.opcode = VALUE_TYPE_FLOAT;
|
||||
out.floatValue = value.floatValue;
|
||||
break;
|
||||
case ArrayElementType::POINTER:
|
||||
out.opcode = VALUE_TYPE_PTR;
|
||||
out.pointerValue = value.pointerValue;
|
||||
break;
|
||||
case ArrayElementType::STRING:
|
||||
out.opcode = VALUE_TYPE_DYNAMIC_STRING;
|
||||
out.integerValue = programPushString(program, value.stringValue);
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool operator<(ArrayElement const& other) const
|
||||
{
|
||||
if (type != other.type) {
|
||||
return type < other.type;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case ArrayElementType::INT:
|
||||
return value.integerValue < other.value.integerValue;
|
||||
case ArrayElementType::FLOAT:
|
||||
return value.floatValue < other.value.floatValue;
|
||||
case ArrayElementType::POINTER:
|
||||
return value.pointerValue < other.value.pointerValue;
|
||||
case ArrayElementType::STRING:
|
||||
return strcmp(value.stringValue, other.value.stringValue) < 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(ArrayElement const& other) const
|
||||
{
|
||||
if (type != other.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case ArrayElementType::INT:
|
||||
return value.integerValue == other.value.integerValue;
|
||||
case ArrayElementType::FLOAT:
|
||||
return value.floatValue == other.value.floatValue;
|
||||
case ArrayElementType::POINTER:
|
||||
return value.pointerValue == other.value.pointerValue;
|
||||
case ArrayElementType::STRING:
|
||||
return strcmp(value.stringValue, other.value.stringValue) == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
~ArrayElement()
|
||||
{
|
||||
if (type == ArrayElementType::STRING) {
|
||||
free(value.stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setString(const char* str, size_t sLen)
|
||||
{
|
||||
type = ArrayElementType::STRING;
|
||||
|
||||
if (sLen == -1) {
|
||||
sLen = strlen(str);
|
||||
}
|
||||
|
||||
if (sLen >= ARRAY_MAX_STRING) {
|
||||
sLen = ARRAY_MAX_STRING - 1; // memory safety
|
||||
}
|
||||
|
||||
value.stringValue = (char*)malloc(sLen + 1);
|
||||
memcpy(value.stringValue, str, sLen);
|
||||
value.stringValue[sLen] = '\0';
|
||||
}
|
||||
|
||||
ArrayElementType type;
|
||||
union {
|
||||
int integerValue;
|
||||
float floatValue;
|
||||
char* stringValue;
|
||||
void* pointerValue;
|
||||
} value;
|
||||
};
|
||||
|
||||
class SFallArray {
|
||||
public:
|
||||
SFallArray(unsigned int flags)
|
||||
: flags(flags)
|
||||
{
|
||||
}
|
||||
virtual ~SFallArray() = default;
|
||||
virtual ProgramValue GetArrayKey(int index, Program* program) = 0;
|
||||
virtual ProgramValue GetArray(const ProgramValue& key, Program* program) = 0;
|
||||
virtual void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) = 0;
|
||||
virtual void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) = 0;
|
||||
virtual void ResizeArray(int newLen) = 0;
|
||||
virtual ProgramValue ScanArray(const ProgramValue& value, Program* program) = 0;
|
||||
virtual int size() = 0;
|
||||
|
||||
bool isReadOnly() const
|
||||
{
|
||||
return (flags & SFALL_ARRAYFLAG_CONSTVAL) != 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
class SFallArrayList : public SFallArray {
|
||||
public:
|
||||
SFallArrayList() = delete;
|
||||
|
||||
SFallArrayList(unsigned int len, unsigned int flags)
|
||||
: SFallArray(flags)
|
||||
{
|
||||
values.resize(len);
|
||||
}
|
||||
|
||||
int size()
|
||||
{
|
||||
return static_cast<int>(values.size());
|
||||
}
|
||||
|
||||
ProgramValue GetArrayKey(int index, Program* program)
|
||||
{
|
||||
if (index < -1 || index > size()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
if (index == -1) { // special index to indicate if array is associative
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return ProgramValue(index);
|
||||
}
|
||||
|
||||
ProgramValue GetArray(const ProgramValue& key, Program* program)
|
||||
{
|
||||
auto index = key.asInt();
|
||||
if (index < 0 || index >= size()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return values[index].toValue(program);
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||
{
|
||||
SetArray(key, ArrayElement { val, program }, allowUnset);
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
|
||||
{
|
||||
if (key.isInt()) {
|
||||
auto index = key.asInt();
|
||||
if (index >= 0 && index < size()) {
|
||||
std::swap(values[index], val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeArray(int newLen)
|
||||
{
|
||||
if (newLen == -1 || size() == newLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLen == 0) {
|
||||
values.clear();
|
||||
} else if (newLen > 0) {
|
||||
if (newLen > ARRAY_MAX_SIZE) {
|
||||
newLen = ARRAY_MAX_SIZE; // safety
|
||||
}
|
||||
|
||||
values.resize(newLen);
|
||||
} else if (newLen >= ARRAY_ACTION_SHUFFLE) {
|
||||
ListSort(values, newLen, std::less<ArrayElement>());
|
||||
}
|
||||
}
|
||||
|
||||
ProgramValue ScanArray(const ProgramValue& value, Program* program)
|
||||
{
|
||||
auto element = ArrayElement { value, program };
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (element == values[i]) {
|
||||
return ProgramValue(i);
|
||||
}
|
||||
}
|
||||
return ProgramValue(-1);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ArrayElement> values;
|
||||
};
|
||||
|
||||
class SFallArrayAssoc : public SFallArray {
|
||||
public:
|
||||
SFallArrayAssoc() = delete;
|
||||
|
||||
SFallArrayAssoc(unsigned int flags)
|
||||
: SFallArray(flags)
|
||||
{
|
||||
}
|
||||
|
||||
int size()
|
||||
{
|
||||
return static_cast<int>(pairs.size());
|
||||
}
|
||||
|
||||
ProgramValue GetArrayKey(int index, Program* program)
|
||||
{
|
||||
if (index < -1 || index > size()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
if (index == -1) { // special index to indicate if array is associative
|
||||
return ProgramValue(1);
|
||||
}
|
||||
|
||||
return pairs[index].key.toValue(program);
|
||||
}
|
||||
|
||||
ProgramValue GetArray(const ProgramValue& key, Program* program)
|
||||
{
|
||||
auto keyEl = ArrayElement { key, program };
|
||||
auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
|
||||
return pair.key == keyEl;
|
||||
});
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
auto index = it - pairs.begin();
|
||||
return pairs[index].value.toValue(program);
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||
{
|
||||
auto keyEl = ArrayElement { key, program };
|
||||
auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
|
||||
return pair.key == keyEl;
|
||||
});
|
||||
|
||||
if (it != pairs.end() && isReadOnly()) {
|
||||
// don't update value of key
|
||||
return;
|
||||
}
|
||||
|
||||
if (allowUnset && !isReadOnly() && val.isInt() && val.asInt() == 0) {
|
||||
// after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset
|
||||
if (it != pairs.end()) {
|
||||
pairs.erase(it);
|
||||
}
|
||||
} else {
|
||||
if (it == pairs.end()) {
|
||||
// size check
|
||||
if (size() >= ARRAY_MAX_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
pairs.push_back(KeyValuePair { std::move(keyEl), ArrayElement { val, program } });
|
||||
} else {
|
||||
auto index = it - pairs.begin();
|
||||
pairs[index].value = ArrayElement { val, program };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
|
||||
{
|
||||
assert(false && "This method is not used for associative arrays thus it is not implemented");
|
||||
}
|
||||
|
||||
void ResizeArray(int newLen)
|
||||
{
|
||||
if (newLen == -1 || size() == newLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only allow to reduce number of elements (adding range of elements is meaningless for maps)
|
||||
if (newLen >= 0 && newLen < size()) {
|
||||
pairs.resize(newLen);
|
||||
} else if (newLen < 0) {
|
||||
if (newLen < (ARRAY_ACTION_SHUFFLE - 2)) return;
|
||||
MapSort(newLen);
|
||||
}
|
||||
}
|
||||
|
||||
ProgramValue ScanArray(const ProgramValue& value, Program* program)
|
||||
{
|
||||
auto valueEl = ArrayElement { value, program };
|
||||
auto it = std::find_if(pairs.begin(), pairs.end(), [&valueEl](const KeyValuePair& pair) {
|
||||
return pair.value == valueEl;
|
||||
});
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return ProgramValue(-1);
|
||||
}
|
||||
|
||||
return it->key.toValue(program);
|
||||
}
|
||||
|
||||
private:
|
||||
struct KeyValuePair {
|
||||
ArrayElement key;
|
||||
ArrayElement value;
|
||||
};
|
||||
|
||||
void MapSort(int type)
|
||||
{
|
||||
bool sortByValue = false;
|
||||
if (type < ARRAY_ACTION_SHUFFLE) {
|
||||
type += 4;
|
||||
sortByValue = true;
|
||||
}
|
||||
|
||||
if (sortByValue) {
|
||||
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
|
||||
return a.value < b.value;
|
||||
});
|
||||
} else {
|
||||
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
|
||||
return a.key < b.key;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<KeyValuePair> pairs;
|
||||
};
|
||||
|
||||
struct SfallArraysState {
|
||||
std::unordered_map<ArrayId, std::unique_ptr<SFallArray>> arrays;
|
||||
std::unordered_set<ArrayId> temporaryArrayIds;
|
||||
int nextArrayId = kInitialArrayId;
|
||||
};
|
||||
|
||||
static SfallArraysState* _state = nullptr;
|
||||
|
||||
bool sfallArraysInit()
|
||||
{
|
||||
_state = new (std::nothrow) SfallArraysState();
|
||||
if (_state == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfallArraysReset()
|
||||
{
|
||||
if (_state != nullptr) {
|
||||
_state->arrays.clear();
|
||||
_state->temporaryArrayIds.clear();
|
||||
_state->nextArrayId = kInitialArrayId;
|
||||
}
|
||||
}
|
||||
|
||||
void sfallArraysExit()
|
||||
{
|
||||
if (_state != nullptr) {
|
||||
delete _state;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayId CreateArray(int len, unsigned int flags)
|
||||
{
|
||||
flags = (flags & ~1); // reset 1 bit
|
||||
|
||||
if (len < 0) {
|
||||
flags |= SFALL_ARRAYFLAG_ASSOC;
|
||||
} else if (len > ARRAY_MAX_SIZE) {
|
||||
len = ARRAY_MAX_SIZE; // safecheck
|
||||
}
|
||||
|
||||
ArrayId arrayId = _state->nextArrayId++;
|
||||
|
||||
if (flags & SFALL_ARRAYFLAG_ASSOC) {
|
||||
_state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayAssoc>(flags)));
|
||||
} else {
|
||||
_state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayList>(len, flags)));
|
||||
}
|
||||
|
||||
return arrayId;
|
||||
}
|
||||
|
||||
ArrayId CreateTempArray(int len, unsigned int flags)
|
||||
{
|
||||
ArrayId arrayId = CreateArray(len, flags);
|
||||
_state->temporaryArrayIds.insert(arrayId);
|
||||
return arrayId;
|
||||
}
|
||||
|
||||
SFallArray* get_array_by_id(ArrayId arrayId)
|
||||
{
|
||||
auto it = _state->arrays.find(arrayId);
|
||||
if (it == _state->arrays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return arr->GetArrayKey(index, program);
|
||||
}
|
||||
|
||||
int LenArray(ArrayId arrayId)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return arr->size();
|
||||
}
|
||||
|
||||
ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return arr->GetArray(key, program);
|
||||
}
|
||||
|
||||
void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
arr->SetArray(key, val, allowUnset, program);
|
||||
}
|
||||
|
||||
void FreeArray(ArrayId arrayId)
|
||||
{
|
||||
// TODO: remove from saved_arrays
|
||||
_state->arrays.erase(arrayId);
|
||||
}
|
||||
|
||||
void FixArray(ArrayId arrayId)
|
||||
{
|
||||
_state->temporaryArrayIds.erase(arrayId);
|
||||
}
|
||||
|
||||
void DeleteAllTempArrays()
|
||||
{
|
||||
for (auto it = _state->temporaryArrayIds.begin(); it != _state->temporaryArrayIds.end(); ++it) {
|
||||
FreeArray(*it);
|
||||
}
|
||||
_state->temporaryArrayIds.clear();
|
||||
}
|
||||
|
||||
void ResizeArray(ArrayId arrayId, int newLen)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
arr->ResizeArray(newLen);
|
||||
}
|
||||
|
||||
int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program)
|
||||
{
|
||||
// CE: Sfall uses eponymous global variable which is always the id of the
|
||||
// last created array.
|
||||
ArrayId stackArrayId = _state->nextArrayId - 1;
|
||||
|
||||
auto arr = get_array_by_id(stackArrayId);
|
||||
if (arr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto size = arr->size();
|
||||
if (size >= ARRAY_MAX_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (key.asInt() >= size) {
|
||||
arr->ResizeArray(size + 1);
|
||||
}
|
||||
|
||||
SetArray(stackArrayId, key, val, false, program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return ProgramValue(-1);
|
||||
}
|
||||
|
||||
return arr->ScanArray(val, program);
|
||||
}
|
||||
|
||||
ArrayId ListAsArray(int type)
|
||||
{
|
||||
std::vector<Object*> objects;
|
||||
sfall_lists_fill(type, objects);
|
||||
|
||||
int count = static_cast<int>(objects.size());
|
||||
ArrayId arrayId = CreateTempArray(count, 0);
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
|
||||
// A little bit ugly and likely inefficient.
|
||||
for (int index = 0; index < count; index++) {
|
||||
arr->SetArray(ProgramValue { index },
|
||||
ArrayElement { ProgramValue { objects[index] }, nullptr },
|
||||
false);
|
||||
}
|
||||
|
||||
return arrayId;
|
||||
}
|
||||
|
||||
ArrayId StringSplit(const char* str, const char* split)
|
||||
{
|
||||
size_t splitLen = strlen(split);
|
||||
|
||||
ArrayId arrayId = CreateTempArray(0, 0);
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
|
||||
if (splitLen == 0) {
|
||||
int count = static_cast<int>(strlen(str));
|
||||
|
||||
arr->ResizeArray(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
arr->SetArray(ProgramValue { i }, ArrayElement { &str[i], 1 }, false);
|
||||
}
|
||||
} else {
|
||||
int count = 1;
|
||||
const char* ptr = str;
|
||||
while (true) {
|
||||
const char* newptr = strstr(ptr, split);
|
||||
if (newptr == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
ptr = newptr + splitLen;
|
||||
}
|
||||
arr->ResizeArray(count);
|
||||
|
||||
count = 0;
|
||||
ptr = str;
|
||||
while (true) {
|
||||
const char* newptr = strstr(ptr, split);
|
||||
size_t len = (newptr != nullptr) ? newptr - ptr : strlen(ptr);
|
||||
|
||||
arr->SetArray(ProgramValue { count }, ArrayElement { ptr, len }, false);
|
||||
|
||||
if (newptr == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
ptr = newptr + splitLen;
|
||||
}
|
||||
}
|
||||
return arrayId;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef FALLOUT_SFALL_ARRAYS_H_
|
||||
#define FALLOUT_SFALL_ARRAYS_H_
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define SFALL_ARRAYFLAG_ASSOC (1) // is map
|
||||
#define SFALL_ARRAYFLAG_CONSTVAL (2) // don't update value of key if the key exists in map
|
||||
#define SFALL_ARRAYFLAG_RESERVED (4)
|
||||
|
||||
using ArrayId = unsigned int;
|
||||
|
||||
bool sfallArraysInit();
|
||||
void sfallArraysReset();
|
||||
void sfallArraysExit();
|
||||
ArrayId CreateArray(int len, unsigned int flags);
|
||||
ArrayId CreateTempArray(int len, unsigned int flags);
|
||||
ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program);
|
||||
int LenArray(ArrayId arrayId);
|
||||
ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program);
|
||||
void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program);
|
||||
void FreeArray(ArrayId arrayId);
|
||||
void FixArray(ArrayId id);
|
||||
void ResizeArray(ArrayId arrayId, int newLen);
|
||||
void DeleteAllTempArrays();
|
||||
int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program);
|
||||
ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program);
|
||||
ArrayId ListAsArray(int type);
|
||||
|
||||
ArrayId StringSplit(const char* str, const char* split);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_SFALL_ARRAYS_H_ */
|
|
@ -57,6 +57,10 @@ bool sfallConfigInit(int argc, char** argv)
|
|||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, 180);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, 270);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, 360);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_AUTO_QUICK_SAVE, 0);
|
||||
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, "");
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, "");
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
char* executable = argv[0];
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace fallout {
|
|||
#define SFALL_CONFIG_FILE_NAME "ddraw.ini"
|
||||
|
||||
#define SFALL_CONFIG_MISC_KEY "Misc"
|
||||
#define SFALL_CONFIG_SCRIPTS_KEY "Scripts"
|
||||
|
||||
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_MALE_KEY "MaleDefaultModel"
|
||||
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY "FemaleDefaultModel"
|
||||
|
@ -68,6 +69,9 @@ namespace fallout {
|
|||
#define SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX_KEY "TownMapHotkeysFix"
|
||||
#define SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY "ExtraGameMsgFileList"
|
||||
#define SFALL_CONFIG_NUMBERS_IS_DIALOG_KEY "NumbersInDialogue"
|
||||
#define SFALL_CONFIG_INI_CONFIG_FOLDER "IniConfigFolder"
|
||||
#define SFALL_CONFIG_GLOBAL_SCRIPT_PATHS "GlobalScriptPaths"
|
||||
#define SFALL_CONFIG_AUTO_QUICK_SAVE "AutoQuickSave"
|
||||
|
||||
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1
|
||||
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
#include "sfall_global_scripts.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db.h"
|
||||
#include "input.h"
|
||||
#include "platform_compat.h"
|
||||
#include "scripts.h"
|
||||
#include "sfall_config.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
struct GlobalScript {
|
||||
Program* program = nullptr;
|
||||
int procs[SCRIPT_PROC_COUNT] = { 0 };
|
||||
int repeat = 0;
|
||||
int count = 0;
|
||||
int mode = 0;
|
||||
bool once = true;
|
||||
};
|
||||
|
||||
struct GlobalScriptsState {
|
||||
std::vector<std::string> paths;
|
||||
std::vector<GlobalScript> globalScripts;
|
||||
};
|
||||
|
||||
static GlobalScriptsState* state = nullptr;
|
||||
|
||||
bool sfall_gl_scr_init()
|
||||
{
|
||||
state = new (std::nothrow) GlobalScriptsState();
|
||||
if (state == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* paths;
|
||||
configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, &paths);
|
||||
|
||||
char* curr = paths;
|
||||
while (curr != nullptr && *curr != '\0') {
|
||||
char* end = strchr(curr, ',');
|
||||
if (end != nullptr) {
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
char drive[COMPAT_MAX_DRIVE];
|
||||
char dir[COMPAT_MAX_DIR];
|
||||
compat_splitpath(curr, drive, dir, nullptr, nullptr);
|
||||
|
||||
char** files;
|
||||
int filesLength = fileNameListInit(curr, &files, 0, 0);
|
||||
if (filesLength != 0) {
|
||||
for (int index = 0; index < filesLength; index++) {
|
||||
char path[COMPAT_MAX_PATH];
|
||||
compat_makepath(path, drive, dir, files[index], nullptr);
|
||||
|
||||
state->paths.push_back(std::string { path });
|
||||
}
|
||||
|
||||
fileNameListFree(&files, 0);
|
||||
}
|
||||
|
||||
if (end != nullptr) {
|
||||
*end = ',';
|
||||
curr = end + 1;
|
||||
} else {
|
||||
curr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(state->paths.begin(), state->paths.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfall_gl_scr_reset()
|
||||
{
|
||||
if (state != nullptr) {
|
||||
sfall_gl_scr_remove_all();
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_exit()
|
||||
{
|
||||
if (state != nullptr) {
|
||||
sfall_gl_scr_remove_all();
|
||||
|
||||
delete state;
|
||||
state = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_exec_start_proc()
|
||||
{
|
||||
for (auto& path : state->paths) {
|
||||
Program* program = programCreateByPath(path.c_str());
|
||||
if (program != nullptr) {
|
||||
GlobalScript scr;
|
||||
scr.program = program;
|
||||
|
||||
for (int action = 0; action < SCRIPT_PROC_COUNT; action++) {
|
||||
scr.procs[action] = programFindProcedure(program, gScriptProcNames[action]);
|
||||
}
|
||||
|
||||
state->globalScripts.push_back(std::move(scr));
|
||||
|
||||
_interpret(program, -1);
|
||||
}
|
||||
}
|
||||
|
||||
tickersAdd(sfall_gl_scr_process_input);
|
||||
}
|
||||
|
||||
void sfall_gl_scr_remove_all()
|
||||
{
|
||||
tickersRemove(sfall_gl_scr_process_input);
|
||||
|
||||
for (auto& scr : state->globalScripts) {
|
||||
programFree(scr.program);
|
||||
}
|
||||
|
||||
state->globalScripts.clear();
|
||||
}
|
||||
|
||||
void sfall_gl_scr_exec_map_update_scripts(int action)
|
||||
{
|
||||
for (auto& scr : state->globalScripts) {
|
||||
if (scr.mode == 0 || scr.mode == 3) {
|
||||
if (scr.procs[action] != -1) {
|
||||
_executeProcedure(scr.program, scr.procs[action]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sfall_gl_scr_process_simple(int mode1, int mode2)
|
||||
{
|
||||
for (auto& scr : state->globalScripts) {
|
||||
if (scr.repeat != 0 && (scr.mode == mode1 || scr.mode == mode2)) {
|
||||
scr.count++;
|
||||
if (scr.count >= scr.repeat) {
|
||||
_executeProcedure(scr.program, scr.procs[SCRIPT_PROC_START]);
|
||||
scr.count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_process_main()
|
||||
{
|
||||
sfall_gl_scr_process_simple(0, 3);
|
||||
}
|
||||
|
||||
void sfall_gl_scr_process_input()
|
||||
{
|
||||
sfall_gl_scr_process_simple(1, 1);
|
||||
}
|
||||
|
||||
void sfall_gl_scr_process_worldmap()
|
||||
{
|
||||
sfall_gl_scr_process_simple(2, 3);
|
||||
}
|
||||
|
||||
static GlobalScript* sfall_gl_scr_map_program_to_scr(Program* program)
|
||||
{
|
||||
auto it = std::find_if(state->globalScripts.begin(),
|
||||
state->globalScripts.end(),
|
||||
[&program](const GlobalScript& scr) {
|
||||
return scr.program == program;
|
||||
});
|
||||
return it != state->globalScripts.end() ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
void sfall_gl_scr_set_repeat(Program* program, int frames)
|
||||
{
|
||||
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
||||
if (scr != nullptr) {
|
||||
scr->repeat = frames;
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_set_type(Program* program, int type)
|
||||
{
|
||||
if (type < 0 || type > 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
||||
if (scr != nullptr) {
|
||||
scr->mode = type;
|
||||
}
|
||||
}
|
||||
|
||||
bool sfall_gl_scr_is_loaded(Program* program)
|
||||
{
|
||||
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
||||
if (scr != nullptr) {
|
||||
if (scr->once) {
|
||||
scr->once = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not a global script.
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfall_gl_scr_update(int burstSize)
|
||||
{
|
||||
for (auto& scr : state->globalScripts) {
|
||||
_interpret(scr.program, burstSize);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef FALLOUT_SFALL_GLOBAL_SCRIPTS_H_
|
||||
#define FALLOUT_SFALL_GLOBAL_SCRIPTS_H_
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
bool sfall_gl_scr_init();
|
||||
void sfall_gl_scr_reset();
|
||||
void sfall_gl_scr_exit();
|
||||
void sfall_gl_scr_exec_start_proc();
|
||||
void sfall_gl_scr_remove_all();
|
||||
void sfall_gl_scr_exec_map_update_scripts(int action);
|
||||
void sfall_gl_scr_process_main();
|
||||
void sfall_gl_scr_process_input();
|
||||
void sfall_gl_scr_process_worldmap();
|
||||
void sfall_gl_scr_set_repeat(Program* program, int frames);
|
||||
void sfall_gl_scr_set_type(Program* program, int type);
|
||||
bool sfall_gl_scr_is_loaded(Program* program);
|
||||
void sfall_gl_scr_update(int burstSize);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_SFALL_GLOBAL_SCRIPTS_H_ */
|
|
@ -10,72 +10,127 @@ struct SfallGlobalVarsState {
|
|||
std::unordered_map<uint64_t, int> vars;
|
||||
};
|
||||
|
||||
static bool sfallGlobalVarsStore(uint64_t key, int value);
|
||||
static bool sfallGlobalVarsFetch(uint64_t key, int& value);
|
||||
#pragma pack(push)
|
||||
#pragma pack(8)
|
||||
|
||||
static SfallGlobalVarsState* _state;
|
||||
/// Matches Sfall's `GlobalVar` to maintain binary compatibility.
|
||||
struct GlobalVarEntry {
|
||||
uint64_t key;
|
||||
int32_t value;
|
||||
int32_t unused;
|
||||
};
|
||||
|
||||
bool sfallGlobalVarsInit()
|
||||
#pragma pack(pop)
|
||||
|
||||
static bool sfall_gl_vars_store(uint64_t key, int value);
|
||||
static bool sfall_gl_vars_fetch(uint64_t key, int& value);
|
||||
|
||||
static SfallGlobalVarsState* sfall_gl_vars_state = nullptr;
|
||||
|
||||
bool sfall_gl_vars_init()
|
||||
{
|
||||
_state = new (std::nothrow) SfallGlobalVarsState();
|
||||
if (_state == nullptr) {
|
||||
sfall_gl_vars_state = new (std::nothrow) SfallGlobalVarsState();
|
||||
if (sfall_gl_vars_state == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfallGlobalVarsReset()
|
||||
void sfall_gl_vars_reset()
|
||||
{
|
||||
_state->vars.clear();
|
||||
sfall_gl_vars_state->vars.clear();
|
||||
}
|
||||
|
||||
void sfallGlobalVarsExit()
|
||||
void sfall_gl_vars_exit()
|
||||
{
|
||||
if (_state != nullptr) {
|
||||
delete _state;
|
||||
_state = nullptr;
|
||||
if (sfall_gl_vars_state != nullptr) {
|
||||
delete sfall_gl_vars_state;
|
||||
sfall_gl_vars_state = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsStore(const char* key, int value)
|
||||
bool sfall_gl_vars_save(File* stream)
|
||||
{
|
||||
int count = static_cast<int>(sfall_gl_vars_state->vars.size());
|
||||
if (fileWrite(&count, sizeof(count), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GlobalVarEntry entry = { 0 };
|
||||
for (auto& pair : sfall_gl_vars_state->vars) {
|
||||
entry.key = pair.first;
|
||||
entry.value = pair.second;
|
||||
|
||||
if (fileWrite(&entry, sizeof(entry), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sfall_gl_vars_load(File* stream)
|
||||
{
|
||||
int count;
|
||||
if (fileRead(&count, sizeof(count), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sfall_gl_vars_state->vars.reserve(count);
|
||||
|
||||
GlobalVarEntry entry;
|
||||
while (count > 0) {
|
||||
if (fileRead(&entry, sizeof(entry), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sfall_gl_vars_state->vars[entry.key] = static_cast<int>(entry.value);
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sfall_gl_vars_store(const char* key, int value)
|
||||
{
|
||||
if (strlen(key) != 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key));
|
||||
return sfallGlobalVarsStore(numericKey, value);
|
||||
return sfall_gl_vars_store(numericKey, value);
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsStore(int key, int value)
|
||||
bool sfall_gl_vars_store(int key, int value)
|
||||
{
|
||||
return sfallGlobalVarsStore(static_cast<uint64_t>(key), value);
|
||||
return sfall_gl_vars_store(static_cast<uint64_t>(key), value);
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsFetch(const char* key, int& value)
|
||||
bool sfall_gl_vars_fetch(const char* key, int& value)
|
||||
{
|
||||
if (strlen(key) != 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key));
|
||||
return sfallGlobalVarsFetch(numericKey, value);
|
||||
return sfall_gl_vars_fetch(numericKey, value);
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsFetch(int key, int& value)
|
||||
bool sfall_gl_vars_fetch(int key, int& value)
|
||||
{
|
||||
return sfallGlobalVarsFetch(static_cast<uint64_t>(key), value);
|
||||
return sfall_gl_vars_fetch(static_cast<uint64_t>(key), value);
|
||||
}
|
||||
|
||||
static bool sfallGlobalVarsStore(uint64_t key, int value)
|
||||
static bool sfall_gl_vars_store(uint64_t key, int value)
|
||||
{
|
||||
auto it = _state->vars.find(key);
|
||||
if (it == _state->vars.end()) {
|
||||
_state->vars.emplace(key, value);
|
||||
auto it = sfall_gl_vars_state->vars.find(key);
|
||||
if (it == sfall_gl_vars_state->vars.end()) {
|
||||
sfall_gl_vars_state->vars.emplace(key, value);
|
||||
} else {
|
||||
if (value == 0) {
|
||||
_state->vars.erase(it);
|
||||
sfall_gl_vars_state->vars.erase(it);
|
||||
} else {
|
||||
it->second = value;
|
||||
}
|
||||
|
@ -84,10 +139,10 @@ static bool sfallGlobalVarsStore(uint64_t key, int value)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool sfallGlobalVarsFetch(uint64_t key, int& value)
|
||||
static bool sfall_gl_vars_fetch(uint64_t key, int& value)
|
||||
{
|
||||
auto it = _state->vars.find(key);
|
||||
if (it == _state->vars.end()) {
|
||||
auto it = sfall_gl_vars_state->vars.find(key);
|
||||
if (it == sfall_gl_vars_state->vars.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
#ifndef FALLOUT_SFALL_GLOBAL_VARS_H_
|
||||
#define FALLOUT_SFALL_GLOBAL_VARS_H_
|
||||
|
||||
#include "db.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
bool sfallGlobalVarsInit();
|
||||
void sfallGlobalVarsReset();
|
||||
void sfallGlobalVarsExit();
|
||||
bool sfallGlobalVarsStore(const char* key, int value);
|
||||
bool sfallGlobalVarsStore(int key, int value);
|
||||
bool sfallGlobalVarsFetch(const char* key, int& value);
|
||||
bool sfallGlobalVarsFetch(int key, int& value);
|
||||
bool sfall_gl_vars_init();
|
||||
void sfall_gl_vars_reset();
|
||||
void sfall_gl_vars_exit();
|
||||
bool sfall_gl_vars_save(File* stream);
|
||||
bool sfall_gl_vars_load(File* stream);
|
||||
bool sfall_gl_vars_store(const char* key, int value);
|
||||
bool sfall_gl_vars_store(int key, int value);
|
||||
bool sfall_gl_vars_fetch(const char* key, int& value);
|
||||
bool sfall_gl_vars_fetch(int key, int& value);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
#include "sfall_ini.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "config.h"
|
||||
#include "platform_compat.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
/// The max length of `fileName` chunk in the triplet.
|
||||
static constexpr size_t kFileNameMaxSize = 63;
|
||||
|
||||
/// The max length of `section` chunk in the triplet.
|
||||
static constexpr size_t kSectionMaxSize = 32;
|
||||
|
||||
/// Special .ini file names which are accessed without adding base path.
|
||||
static constexpr const char* kSystemConfigFileNames[] = {
|
||||
"ddraw.ini",
|
||||
"f2_res.ini",
|
||||
};
|
||||
|
||||
static char basePath[COMPAT_MAX_PATH];
|
||||
|
||||
/// Parses "fileName|section|key" triplet into parts. `fileName` and `section`
|
||||
/// chunks are copied into appropriate variables. Returns the pointer to `key`,
|
||||
/// or `nullptr` on any error.
|
||||
static const char* parse_ini_triplet(const char* triplet, char* fileName, char* section)
|
||||
{
|
||||
const char* fileNameSectionSep = strchr(triplet, '|');
|
||||
if (fileNameSectionSep == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t fileNameLength = fileNameSectionSep - triplet;
|
||||
if (fileNameLength > kFileNameMaxSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* sectionKeySep = strchr(fileNameSectionSep + 1, '|');
|
||||
if (sectionKeySep == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t sectionLength = sectionKeySep - fileNameSectionSep - 1;
|
||||
if (sectionLength > kSectionMaxSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
strncpy(fileName, triplet, fileNameLength);
|
||||
fileName[fileNameLength] = '\0';
|
||||
|
||||
strncpy(section, fileNameSectionSep + 1, sectionLength);
|
||||
section[sectionLength] = '\0';
|
||||
|
||||
return sectionKeySep + 1;
|
||||
}
|
||||
|
||||
/// Returns `true` if given `fileName` is a special system .ini file name.
|
||||
static bool is_system_file_name(const char* fileName)
|
||||
{
|
||||
for (auto& systemFileName : kSystemConfigFileNames) {
|
||||
if (compat_stricmp(systemFileName, fileName) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void sfall_ini_set_base_path(const char* path)
|
||||
{
|
||||
if (path != nullptr) {
|
||||
strcpy(basePath, path);
|
||||
|
||||
size_t length = strlen(basePath);
|
||||
if (length > 0) {
|
||||
if (basePath[length - 1] == '\\' || basePath[length - 1] == '/') {
|
||||
basePath[length - 1] = '\0';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
basePath[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool sfall_ini_get_int(const char* triplet, int* value)
|
||||
{
|
||||
char string[20];
|
||||
if (!sfall_ini_get_string(triplet, string, sizeof(string))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = atol(string);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sfall_ini_get_string(const char* triplet, char* value, size_t size)
|
||||
{
|
||||
char fileName[kFileNameMaxSize];
|
||||
char section[kSectionMaxSize];
|
||||
|
||||
const char* key = parse_ini_triplet(triplet, fileName, section);
|
||||
if (key == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Config config;
|
||||
if (!configInit(&config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
bool loaded = false;
|
||||
|
||||
if (basePath[0] != '\0' && !is_system_file_name(fileName)) {
|
||||
// Attempt to load requested file in base directory.
|
||||
snprintf(path, sizeof(path), "%s\\%s", basePath, fileName);
|
||||
loaded = configRead(&config, path, false);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
// There was no base path set, requested file is a system config, or
|
||||
// non-system config file was not found the base path - attempt to load
|
||||
// from current working directory.
|
||||
strcpy(path, fileName);
|
||||
loaded = configRead(&config, path, false);
|
||||
}
|
||||
|
||||
// NOTE: Sfall's `GetIniSetting` returns error code (-1) only when it cannot
|
||||
// parse triplet. Otherwise the default for string settings is empty string.
|
||||
value[0] = '\0';
|
||||
|
||||
if (loaded) {
|
||||
char* stringValue;
|
||||
if (configGetString(&config, section, key, &stringValue)) {
|
||||
strncpy(value, stringValue, size - 1);
|
||||
value[size - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
configFree(&config);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sfall_ini_set_int(const char* triplet, int value)
|
||||
{
|
||||
char stringValue[20];
|
||||
compat_itoa(value, stringValue, 10);
|
||||
|
||||
return sfall_ini_set_string(triplet, stringValue);
|
||||
}
|
||||
|
||||
bool sfall_ini_set_string(const char* triplet, const char* value)
|
||||
{
|
||||
char fileName[kFileNameMaxSize];
|
||||
char section[kSectionMaxSize];
|
||||
|
||||
const char* key = parse_ini_triplet(triplet, fileName, section);
|
||||
if (key == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Config config;
|
||||
if (!configInit(&config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
bool loaded = false;
|
||||
|
||||
if (basePath[0] != '\0' && !is_system_file_name(fileName)) {
|
||||
// Attempt to load requested file in base directory.
|
||||
snprintf(path, sizeof(path), "%s\\%s", basePath, fileName);
|
||||
loaded = configRead(&config, path, false);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
// There was no base path set, requested file is a system config, or
|
||||
// non-system config file was not found the base path - attempt to load
|
||||
// from current working directory.
|
||||
strcpy(path, fileName);
|
||||
loaded = configRead(&config, path, false);
|
||||
}
|
||||
|
||||
configSetString(&config, section, key, value);
|
||||
|
||||
bool saved = configWrite(&config, path, false);
|
||||
|
||||
configFree(&config);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef FALLOUT_SFALL_INI_H_
|
||||
#define FALLOUT_SFALL_INI_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace fallout {
|
||||
|
||||
/// Sets base directory to lookup .ini files.
|
||||
void sfall_ini_set_base_path(const char* path);
|
||||
|
||||
/// Reads integer key identified by "fileName|section|key" triplet into `value`.
|
||||
bool sfall_ini_get_int(const char* triplet, int* value);
|
||||
|
||||
/// Reads string key identified by "fileName|section|key" triplet into `value`.
|
||||
bool sfall_ini_get_string(const char* triplet, char* value, size_t size);
|
||||
|
||||
/// Writes integer key identified by "fileName|section|key" triplet.
|
||||
bool sfall_ini_set_int(const char* triplet, int value);
|
||||
|
||||
/// Writes string key identified by "fileName|section|key" triplet.
|
||||
bool sfall_ini_set_string(const char* triplet, const char* value);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_SFALL_INI_H_ */
|
|
@ -0,0 +1,297 @@
|
|||
#include "sfall_kb_helpers.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
/// Maps DirectInput DIK constants to SDL scancodes.
|
||||
static constexpr SDL_Scancode kDiks[256] = {
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_ESCAPE, // DIK_ESCAPE
|
||||
SDL_SCANCODE_1, // DIK_1
|
||||
SDL_SCANCODE_2, // DIK_2
|
||||
SDL_SCANCODE_3, // DIK_3
|
||||
SDL_SCANCODE_4, // DIK_4
|
||||
SDL_SCANCODE_5, // DIK_5
|
||||
SDL_SCANCODE_6, // DIK_6
|
||||
SDL_SCANCODE_7, // DIK_7
|
||||
SDL_SCANCODE_8, // DIK_8
|
||||
SDL_SCANCODE_9, // DIK_9
|
||||
SDL_SCANCODE_0, // DIK_0
|
||||
SDL_SCANCODE_MINUS, // DIK_MINUS
|
||||
SDL_SCANCODE_EQUALS, // DIK_EQUALS
|
||||
SDL_SCANCODE_BACKSPACE, // DIK_BACK
|
||||
SDL_SCANCODE_TAB, // DIK_TAB
|
||||
SDL_SCANCODE_Q, // DIK_Q
|
||||
SDL_SCANCODE_W, // DIK_W
|
||||
SDL_SCANCODE_E, // DIK_E
|
||||
SDL_SCANCODE_R, // DIK_R
|
||||
SDL_SCANCODE_T, // DIK_T
|
||||
SDL_SCANCODE_Y, // DIK_Y
|
||||
SDL_SCANCODE_U, // DIK_U
|
||||
SDL_SCANCODE_I, // DIK_I
|
||||
SDL_SCANCODE_O, // DIK_O
|
||||
SDL_SCANCODE_P, // DIK_P
|
||||
SDL_SCANCODE_LEFTBRACKET, // DIK_LBRACKET
|
||||
SDL_SCANCODE_RIGHTBRACKET, // DIK_RBRACKET
|
||||
SDL_SCANCODE_RETURN, // DIK_RETURN
|
||||
SDL_SCANCODE_LCTRL, // DIK_LCONTROL
|
||||
SDL_SCANCODE_A, // DIK_A
|
||||
SDL_SCANCODE_S, // DIK_S
|
||||
SDL_SCANCODE_D, // DIK_D
|
||||
SDL_SCANCODE_F, // DIK_F
|
||||
SDL_SCANCODE_G, // DIK_G
|
||||
SDL_SCANCODE_H, // DIK_H
|
||||
SDL_SCANCODE_J, // DIK_J
|
||||
SDL_SCANCODE_K, // DIK_K
|
||||
SDL_SCANCODE_L, // DIK_L
|
||||
SDL_SCANCODE_SEMICOLON, // DIK_SEMICOLON
|
||||
SDL_SCANCODE_APOSTROPHE, // DIK_APOSTROPHE
|
||||
SDL_SCANCODE_GRAVE, // DIK_GRAVE
|
||||
SDL_SCANCODE_LSHIFT, // DIK_LSHIFT
|
||||
SDL_SCANCODE_BACKSLASH, // DIK_BACKSLASH
|
||||
SDL_SCANCODE_Z, // DIK_Z
|
||||
SDL_SCANCODE_X, // DIK_X
|
||||
SDL_SCANCODE_C, // DIK_C
|
||||
SDL_SCANCODE_V, // DIK_V
|
||||
SDL_SCANCODE_B, // DIK_B
|
||||
SDL_SCANCODE_N, // DIK_N
|
||||
SDL_SCANCODE_M, // DIK_M
|
||||
SDL_SCANCODE_COMMA, // DIK_COMMA
|
||||
SDL_SCANCODE_PERIOD, // DIK_PERIOD
|
||||
SDL_SCANCODE_SLASH, // DIK_SLASH
|
||||
SDL_SCANCODE_RSHIFT, // DIK_RSHIFT
|
||||
SDL_SCANCODE_KP_MULTIPLY, // DIK_MULTIPLY
|
||||
SDL_SCANCODE_LALT, // DIK_LMENU
|
||||
SDL_SCANCODE_SPACE, // DIK_SPACE
|
||||
SDL_SCANCODE_CAPSLOCK, // DIK_CAPITAL
|
||||
SDL_SCANCODE_F1, // DIK_F1
|
||||
SDL_SCANCODE_F2, // DIK_F2
|
||||
SDL_SCANCODE_F3, // DIK_F3
|
||||
SDL_SCANCODE_F4, // DIK_F4
|
||||
SDL_SCANCODE_F5, // DIK_F5
|
||||
SDL_SCANCODE_F6, // DIK_F6
|
||||
SDL_SCANCODE_F7, // DIK_F7
|
||||
SDL_SCANCODE_F8, // DIK_F8
|
||||
SDL_SCANCODE_F9, // DIK_F9
|
||||
SDL_SCANCODE_F10, // DIK_F10
|
||||
SDL_SCANCODE_NUMLOCKCLEAR, // DIK_NUMLOCK
|
||||
SDL_SCANCODE_SCROLLLOCK, // DIK_SCROLL
|
||||
SDL_SCANCODE_KP_7, // DIK_NUMPAD7
|
||||
SDL_SCANCODE_KP_8, // DIK_NUMPAD8
|
||||
SDL_SCANCODE_KP_9, // DIK_NUMPAD9
|
||||
SDL_SCANCODE_KP_MINUS, // DIK_SUBTRACT
|
||||
SDL_SCANCODE_KP_4, // DIK_NUMPAD4
|
||||
SDL_SCANCODE_KP_5, // DIK_NUMPAD5
|
||||
SDL_SCANCODE_KP_6, // DIK_NUMPAD6
|
||||
SDL_SCANCODE_KP_PLUS, // DIK_ADD
|
||||
SDL_SCANCODE_KP_1, // DIK_NUMPAD1
|
||||
SDL_SCANCODE_KP_2, // DIK_NUMPAD2
|
||||
SDL_SCANCODE_KP_3, // DIK_NUMPAD3
|
||||
SDL_SCANCODE_KP_0, // DIK_NUMPAD0
|
||||
SDL_SCANCODE_KP_PERIOD, // DIK_DECIMAL
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_F11, // DIK_F11
|
||||
SDL_SCANCODE_F12, // DIK_F12
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_KP_EQUALS, // DIK_NUMPADEQUALS
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_AT
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_COLON
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_UNDERLINE
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_STOP
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_AX
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_UNLABELED
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_KP_ENTER, // DIK_NUMPADENTER
|
||||
SDL_SCANCODE_RCTRL, // DIK_RCONTROL
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_KP_COMMA, // DIK_NUMPADCOMMA
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_SLASH, // DIK_DIVIDE
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_SYSREQ, // DIK_SYSRQ
|
||||
SDL_SCANCODE_RALT, // DIK_RMENU
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_HOME, // DIK_HOME
|
||||
SDL_SCANCODE_UP, // DIK_UP
|
||||
SDL_SCANCODE_PAGEUP, // DIK_PRIOR
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_LEFT, // DIK_LEFT
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_RIGHT, // DIK_RIGHT
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_END, // DIK_END
|
||||
SDL_SCANCODE_DOWN, // DIK_DOWN
|
||||
SDL_SCANCODE_PAGEDOWN, // DIK_NEXT
|
||||
SDL_SCANCODE_INSERT, // DIK_INSERT
|
||||
SDL_SCANCODE_DELETE, // DIK_DELETE
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_LGUI, // DIK_LWIN
|
||||
SDL_SCANCODE_RGUI, // DIK_RWIN
|
||||
SDL_SCANCODE_APPLICATION, // DIK_APPS
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
};
|
||||
|
||||
/// Translates Sfall key code (DIK or VK constant) to SDL scancode.
|
||||
static SDL_Scancode get_scancode_from_key(int key)
|
||||
{
|
||||
return kDiks[key & 0xFF];
|
||||
}
|
||||
|
||||
bool sfall_kb_is_key_pressed(int key)
|
||||
{
|
||||
SDL_Scancode scancode = get_scancode_from_key(key);
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Uint8* state = SDL_GetKeyboardState(nullptr);
|
||||
return state[scancode] != 0;
|
||||
}
|
||||
|
||||
void sfall_kb_press_key(int key)
|
||||
{
|
||||
SDL_Scancode scancode = get_scancode_from_key(key);
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
event.key.keysym.scancode = scancode;
|
||||
|
||||
event.type = SDL_KEYDOWN;
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
event.type = SDL_KEYUP;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef FALLOUT_SFALL_KB_HELPERS_H_
|
||||
#define FALLOUT_SFALL_KB_HELPERS_H_
|
||||
|
||||
/// Returns `true` if given key is pressed.
|
||||
bool sfall_kb_is_key_pressed(int key);
|
||||
|
||||
/// Simulates pressing `key`.
|
||||
void sfall_kb_press_key(int key);
|
||||
|
||||
#endif /* FALLOUT_SFALL_KB_HELPERS_H_ */
|
|
@ -1,7 +1,6 @@
|
|||
#include "sfall_lists.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "object.h"
|
||||
#include "scripts.h"
|
||||
|
@ -66,46 +65,7 @@ int sfallListsCreate(int listType)
|
|||
int listId = _state->nextListId++;
|
||||
List& list = _state->lists[listId];
|
||||
|
||||
if (listType == LIST_TILES) {
|
||||
// For unknown reason this list type is not implemented in Sfall.
|
||||
} else if (listType == LIST_SPATIAL) {
|
||||
for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) {
|
||||
Script* script = scriptGetFirstSpatialScript(elevation);
|
||||
while (script != nullptr) {
|
||||
Object* obj = script->owner;
|
||||
if (obj == nullptr) {
|
||||
obj = scriptGetSelf(script->program);
|
||||
}
|
||||
list.objects.push_back(obj);
|
||||
script = scriptGetNextSpatialScript();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// CE: Implementation is slightly different. Sfall manually loops thru
|
||||
// elevations (3) and hexes (40000) and use |objectFindFirstAtLocation|
|
||||
// (originally |obj_find_first_at_tile|) to obtain next object. This
|
||||
// functionality is already implemented in |objectFindFirst| and
|
||||
// |objectFindNext|.
|
||||
//
|
||||
// As a small optimization |LIST_ALL| is handled separately since there
|
||||
// is no need to check object type.
|
||||
if (listType == LIST_ALL) {
|
||||
Object* obj = objectFindFirst();
|
||||
while (obj != nullptr) {
|
||||
list.objects.push_back(obj);
|
||||
obj = objectFindNext();
|
||||
}
|
||||
} else {
|
||||
Object* obj = objectFindFirst();
|
||||
while (obj != nullptr) {
|
||||
int objectType = PID_TYPE(obj->pid);
|
||||
if (objectType < kObjectTypeToListTypeSize && kObjectTypeToListType[objectType] == listType) {
|
||||
list.objects.push_back(obj);
|
||||
}
|
||||
obj = objectFindNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
sfall_lists_fill(listType, list.objects);
|
||||
|
||||
return listId;
|
||||
}
|
||||
|
@ -131,4 +91,54 @@ void sfallListsDestroy(int listId)
|
|||
}
|
||||
}
|
||||
|
||||
void sfall_lists_fill(int type, std::vector<Object*>& objects)
|
||||
{
|
||||
if (type == LIST_TILES) {
|
||||
// For unknown reason this list type is not implemented in Sfall.
|
||||
return;
|
||||
}
|
||||
|
||||
objects.reserve(100);
|
||||
|
||||
if (type == LIST_SPATIAL) {
|
||||
for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) {
|
||||
Script* script = scriptGetFirstSpatialScript(elevation);
|
||||
while (script != nullptr) {
|
||||
Object* obj = script->owner;
|
||||
if (obj == nullptr) {
|
||||
obj = scriptGetSelf(script->program);
|
||||
}
|
||||
objects.push_back(obj);
|
||||
script = scriptGetNextSpatialScript();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// CE: Implementation is slightly different. Sfall manually loops thru
|
||||
// elevations (3) and hexes (40000) and use |objectFindFirstAtLocation|
|
||||
// (originally |obj_find_first_at_tile|) to obtain next object. This
|
||||
// functionality is already implemented in |objectFindFirst| and
|
||||
// |objectFindNext|.
|
||||
//
|
||||
// As a small optimization |LIST_ALL| is handled separately since there
|
||||
// is no need to check object type.
|
||||
if (type == LIST_ALL) {
|
||||
Object* obj = objectFindFirst();
|
||||
while (obj != nullptr) {
|
||||
objects.push_back(obj);
|
||||
obj = objectFindNext();
|
||||
}
|
||||
} else {
|
||||
Object* obj = objectFindFirst();
|
||||
while (obj != nullptr) {
|
||||
int objectType = PID_TYPE(obj->pid);
|
||||
if (objectType < kObjectTypeToListTypeSize
|
||||
&& kObjectTypeToListType[objectType] == type) {
|
||||
objects.push_back(obj);
|
||||
}
|
||||
obj = objectFindNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef FALLOUT_SFALL_LISTS_H_
|
||||
#define FALLOUT_SFALL_LISTS_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "obj_types.h"
|
||||
|
||||
namespace fallout {
|
||||
|
@ -22,6 +24,7 @@ void sfallListsExit();
|
|||
int sfallListsCreate(int listType);
|
||||
Object* sfallListsGetNext(int listId);
|
||||
void sfallListsDestroy(int listId);
|
||||
void sfall_lists_fill(int type, std::vector<Object*>& objects);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
#include "sfall_metarules.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "combat.h"
|
||||
#include "debug.h"
|
||||
#include "game.h"
|
||||
#include "game_dialog.h"
|
||||
#include "game_mouse.h"
|
||||
#include "interface.h"
|
||||
#include "inventory.h"
|
||||
#include "object.h"
|
||||
#include "sfall_ini.h"
|
||||
#include "text_font.h"
|
||||
#include "tile.h"
|
||||
#include "window.h"
|
||||
#include "worldmap.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
typedef void(MetaruleHandler)(Program* program, int args);
|
||||
|
||||
// Simplified cousin of `SfallMetarule` from Sfall.
|
||||
typedef struct MetaruleInfo {
|
||||
const char* name;
|
||||
MetaruleHandler* handler;
|
||||
int minArgs;
|
||||
int maxArgs;
|
||||
} MetaruleInfo;
|
||||
|
||||
static void mf_car_gas_amount(Program* program, int args);
|
||||
static void mf_combat_data(Program* program, int args);
|
||||
static void mf_critter_inven_obj2(Program* program, int args);
|
||||
static void mf_dialog_obj(Program* program, int args);
|
||||
static void mf_get_cursor_mode(Program* program, int args);
|
||||
static void mf_get_flags(Program* program, int args);
|
||||
static void mf_get_object_data(Program* program, int args);
|
||||
static void mf_get_text_width(Program* program, int args);
|
||||
static void mf_intface_redraw(Program* program, int args);
|
||||
static void mf_loot_obj(Program* program, int args);
|
||||
static void mf_metarule_exist(Program* program, int args);
|
||||
static void mf_outlined_object(Program* program, int args);
|
||||
static void mf_set_cursor_mode(Program* program, int args);
|
||||
static void mf_set_flags(Program* program, int args);
|
||||
static void mf_set_ini_setting(Program* program, int args);
|
||||
static void mf_set_outline(Program* program, int args);
|
||||
static void mf_show_window(Program* program, int args);
|
||||
static void mf_tile_refresh_display(Program* program, int args);
|
||||
|
||||
constexpr MetaruleInfo kMetarules[] = {
|
||||
{ "car_gas_amount", mf_car_gas_amount, 0, 0 },
|
||||
{ "combat_data", mf_combat_data, 0, 0 },
|
||||
{ "critter_inven_obj2", mf_critter_inven_obj2, 2, 2 },
|
||||
{ "dialog_obj", mf_dialog_obj, 0, 0 },
|
||||
{ "get_cursor_mode", mf_get_cursor_mode, 0, 0 },
|
||||
{ "get_flags", mf_get_flags, 1, 1 },
|
||||
{ "get_object_data", mf_get_object_data, 2, 2 },
|
||||
{ "get_text_width", mf_get_text_width, 1, 1 },
|
||||
{ "intface_redraw", mf_intface_redraw, 0, 1 },
|
||||
{ "loot_obj", mf_loot_obj, 0, 0 },
|
||||
{ "metarule_exist", mf_metarule_exist, 1, 1 },
|
||||
{ "outlined_object", mf_outlined_object, 0, 0 },
|
||||
{ "set_cursor_mode", mf_set_cursor_mode, 1, 1 },
|
||||
{ "set_flags", mf_set_flags, 2, 2 },
|
||||
{ "set_ini_setting", mf_set_ini_setting, 2, 2 },
|
||||
{ "set_outline", mf_set_outline, 2, 2 },
|
||||
{ "show_window", mf_show_window, 0, 1 },
|
||||
{ "tile_refresh_display", mf_tile_refresh_display, 0, 0 },
|
||||
};
|
||||
|
||||
constexpr int kMetarulesMax = sizeof(kMetarules) / sizeof(kMetarules[0]);
|
||||
|
||||
void mf_car_gas_amount(Program* program, int args)
|
||||
{
|
||||
programStackPushInteger(program, wmCarGasAmount());
|
||||
}
|
||||
|
||||
void mf_combat_data(Program* program, int args)
|
||||
{
|
||||
if (isInCombat()) {
|
||||
programStackPushPointer(program, combat_get_data());
|
||||
} else {
|
||||
programStackPushPointer(program, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void mf_critter_inven_obj2(Program* program, int args)
|
||||
{
|
||||
int slot = programStackPopInteger(program);
|
||||
Object* obj = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
switch (slot) {
|
||||
case 0:
|
||||
programStackPushPointer(program, critterGetArmor(obj));
|
||||
break;
|
||||
case 1:
|
||||
programStackPushPointer(program, critterGetItem2(obj));
|
||||
break;
|
||||
case 2:
|
||||
programStackPushPointer(program, critterGetItem1(obj));
|
||||
break;
|
||||
case -2:
|
||||
programStackPushInteger(program, obj->data.inventory.length);
|
||||
break;
|
||||
default:
|
||||
programFatalError("mf_critter_inven_obj2: invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
void mf_dialog_obj(Program* program, int args)
|
||||
{
|
||||
if (GameMode::isInGameMode(GameMode::kDialog)) {
|
||||
programStackPushPointer(program, gGameDialogSpeaker);
|
||||
} else {
|
||||
programStackPushPointer(program, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void mf_get_cursor_mode(Program* program, int args)
|
||||
{
|
||||
programStackPushInteger(program, gameMouseGetMode());
|
||||
}
|
||||
|
||||
void mf_get_flags(Program* program, int args)
|
||||
{
|
||||
Object* object = static_cast<Object*>(programStackPopPointer(program));
|
||||
programStackPushInteger(program, object->flags);
|
||||
}
|
||||
|
||||
void mf_get_object_data(Program* program, int args)
|
||||
{
|
||||
size_t offset = static_cast<size_t>(programStackPopInteger(program));
|
||||
void* ptr = programStackPopPointer(program);
|
||||
|
||||
if (offset % 4 != 0) {
|
||||
programFatalError("mf_get_object_data: bad offset %d", offset);
|
||||
}
|
||||
|
||||
int value = *reinterpret_cast<int*>(reinterpret_cast<unsigned char*>(ptr) + offset);
|
||||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
void mf_get_text_width(Program* program, int args)
|
||||
{
|
||||
const char* string = programStackPopString(program);
|
||||
programStackPushInteger(program, fontGetStringWidth(string));
|
||||
}
|
||||
|
||||
void mf_intface_redraw(Program* program, int args)
|
||||
{
|
||||
if (args == 0) {
|
||||
interfaceBarRefresh();
|
||||
} else {
|
||||
// TODO: Incomplete.
|
||||
programFatalError("mf_intface_redraw: not implemented");
|
||||
}
|
||||
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void mf_loot_obj(Program* program, int args)
|
||||
{
|
||||
if (GameMode::isInGameMode(GameMode::kInventory)) {
|
||||
programStackPushPointer(program, inven_get_current_target_obj());
|
||||
} else {
|
||||
programStackPushPointer(program, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void mf_metarule_exist(Program* program, int args)
|
||||
{
|
||||
const char* metarule = programStackPopString(program);
|
||||
|
||||
for (int index = 0; index < kMetarulesMax; index++) {
|
||||
if (strcmp(kMetarules[index].name, metarule) == 0) {
|
||||
programStackPushInteger(program, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
programStackPushInteger(program, 0);
|
||||
}
|
||||
|
||||
void mf_outlined_object(Program* program, int args)
|
||||
{
|
||||
programStackPushPointer(program, gmouse_get_outlined_object());
|
||||
}
|
||||
|
||||
void mf_set_cursor_mode(Program* program, int args)
|
||||
{
|
||||
int mode = programStackPopInteger(program);
|
||||
gameMouseSetMode(mode);
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void mf_set_flags(Program* program, int args)
|
||||
{
|
||||
int flags = programStackPopInteger(program);
|
||||
Object* object = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
object->flags = flags;
|
||||
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void mf_set_ini_setting(Program* program, int args)
|
||||
{
|
||||
ProgramValue value = programStackPopValue(program);
|
||||
const char* triplet = programStackPopString(program);
|
||||
|
||||
if (value.isString()) {
|
||||
const char* stringValue = programGetString(program, value.opcode, value.integerValue);
|
||||
if (!sfall_ini_set_string(triplet, stringValue)) {
|
||||
debugPrint("set_ini_setting: unable to write '%s' to '%s'",
|
||||
stringValue,
|
||||
triplet);
|
||||
}
|
||||
} else {
|
||||
int integerValue = value.asInt();
|
||||
if (!sfall_ini_set_int(triplet, integerValue)) {
|
||||
debugPrint("set_ini_setting: unable to write '%d' to '%s'",
|
||||
integerValue,
|
||||
triplet);
|
||||
}
|
||||
}
|
||||
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void mf_set_outline(Program* program, int args)
|
||||
{
|
||||
int outline = programStackPopInteger(program);
|
||||
Object* object = static_cast<Object*>(programStackPopPointer(program));
|
||||
object->outline = outline;
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void mf_show_window(Program* program, int args)
|
||||
{
|
||||
if (args == 0) {
|
||||
_windowShow();
|
||||
} else if (args == 1) {
|
||||
const char* windowName = programStackPopString(program);
|
||||
if (!_windowShowNamed(windowName)) {
|
||||
debugPrint("show_window: window '%s' is not found", windowName);
|
||||
}
|
||||
}
|
||||
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void mf_tile_refresh_display(Program* program, int args)
|
||||
{
|
||||
tileWindowRefresh();
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void sfall_metarule(Program* program, int args)
|
||||
{
|
||||
static ProgramValue values[6];
|
||||
|
||||
for (int index = 0; index < args; index++) {
|
||||
values[index] = programStackPopValue(program);
|
||||
}
|
||||
|
||||
const char* metarule = programStackPopString(program);
|
||||
|
||||
for (int index = 0; index < args; index++) {
|
||||
programStackPushValue(program, values[index]);
|
||||
}
|
||||
|
||||
int metaruleIndex = -1;
|
||||
for (int index = 0; index < kMetarulesMax; index++) {
|
||||
if (strcmp(kMetarules[index].name, metarule) == 0) {
|
||||
metaruleIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (metaruleIndex == -1) {
|
||||
programFatalError("op_sfall_func: '%s' is not implemented", metarule);
|
||||
}
|
||||
|
||||
if (args < kMetarules[metaruleIndex].minArgs || args > kMetarules[metaruleIndex].maxArgs) {
|
||||
programFatalError("op_sfall_func: '%s': invalid number of args", metarule);
|
||||
}
|
||||
|
||||
kMetarules[metaruleIndex].handler(program, args);
|
||||
}
|
||||
|
||||
} // namespace fallout
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef FALLOUT_SFALL_METARULES_H_
|
||||
#define FALLOUT_SFALL_METARULES_H_
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
void sfall_metarule(Program* program, int args);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_SFALL_METARULES_H_ */
|
|
@ -1,21 +1,32 @@
|
|||
#include "sfall_opcodes.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "animation.h"
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "dbox.h"
|
||||
#include "debug.h"
|
||||
#include "game.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "interpreter.h"
|
||||
#include "item.h"
|
||||
#include "memory.h"
|
||||
#include "message.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "party_member.h"
|
||||
#include "proto.h"
|
||||
#include "scripts.h"
|
||||
#include "sfall_arrays.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "sfall_global_vars.h"
|
||||
#include "sfall_ini.h"
|
||||
#include "sfall_kb_helpers.h"
|
||||
#include "sfall_lists.h"
|
||||
#include "sfall_metarules.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "tile.h"
|
||||
|
@ -23,6 +34,18 @@
|
|||
|
||||
namespace fallout {
|
||||
|
||||
typedef enum ExplosionMetarule {
|
||||
EXPL_FORCE_EXPLOSION_PATTERN = 1,
|
||||
EXPL_FORCE_EXPLOSION_ART = 2,
|
||||
EXPL_FORCE_EXPLOSION_RADIUS = 3,
|
||||
EXPL_FORCE_EXPLOSION_DMGTYPE = 4,
|
||||
EXPL_STATIC_EXPLOSION_RADIUS = 5,
|
||||
EXPL_GET_EXPLOSION_DAMAGE = 6,
|
||||
EXPL_SET_DYNAMITE_EXPLOSION_DAMAGE = 7,
|
||||
EXPL_SET_PLASTIC_EXPLOSION_DAMAGE = 8,
|
||||
EXPL_SET_EXPLOSION_MAX_TARGET = 9,
|
||||
} ExplosionMetarule;
|
||||
|
||||
static constexpr int kVersionMajor = 4;
|
||||
static constexpr int kVersionMinor = 3;
|
||||
static constexpr int kVersionPatch = 4;
|
||||
|
@ -85,6 +108,13 @@ static void opGetPcBonusStat(Program* program)
|
|||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
// tap_key
|
||||
static void op_tap_key(Program* program)
|
||||
{
|
||||
int key = programStackPopInteger(program);
|
||||
sfall_kb_press_key(key);
|
||||
}
|
||||
|
||||
// get_year
|
||||
static void op_get_year(Program* program)
|
||||
{
|
||||
|
@ -93,12 +123,41 @@ static void op_get_year(Program* program)
|
|||
programStackPushInteger(program, year);
|
||||
}
|
||||
|
||||
// game_loaded
|
||||
static void op_game_loaded(Program* program)
|
||||
{
|
||||
bool loaded = sfall_gl_scr_is_loaded(program);
|
||||
programStackPushInteger(program, loaded ? 1 : 0);
|
||||
}
|
||||
|
||||
// set_global_script_repeat
|
||||
static void op_set_global_script_repeat(Program* program)
|
||||
{
|
||||
int frames = programStackPopInteger(program);
|
||||
sfall_gl_scr_set_repeat(program, frames);
|
||||
}
|
||||
|
||||
// key_pressed
|
||||
static void op_key_pressed(Program* program)
|
||||
{
|
||||
int key = programStackPopInteger(program);
|
||||
bool pressed = sfall_kb_is_key_pressed(key);
|
||||
programStackPushInteger(program, pressed ? 1 : 0);
|
||||
}
|
||||
|
||||
// in_world_map
|
||||
static void op_in_world_map(Program* program)
|
||||
{
|
||||
programStackPushInteger(program, GameMode::isInGameMode(GameMode::kWorldmap) ? 1 : 0);
|
||||
}
|
||||
|
||||
// force_encounter
|
||||
static void op_force_encounter(Program* program)
|
||||
{
|
||||
int map = programStackPopInteger(program);
|
||||
wmForceEncounter(map, 0);
|
||||
}
|
||||
|
||||
// set_world_map_pos
|
||||
static void op_set_world_map_pos(Program* program)
|
||||
{
|
||||
|
@ -113,6 +172,13 @@ static void opGetCurrentHand(Program* program)
|
|||
programStackPushInteger(program, interfaceGetCurrentHand());
|
||||
}
|
||||
|
||||
// set_global_script_type
|
||||
static void op_set_global_script_type(Program* program)
|
||||
{
|
||||
int type = programStackPopInteger(program);
|
||||
sfall_gl_scr_set_type(program, type);
|
||||
}
|
||||
|
||||
// set_sfall_global
|
||||
static void opSetGlobalVar(Program* program)
|
||||
{
|
||||
|
@ -121,9 +187,9 @@ static void opSetGlobalVar(Program* program)
|
|||
|
||||
if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
||||
const char* key = programGetString(program, variable.opcode, variable.integerValue);
|
||||
sfallGlobalVarsStore(key, value.integerValue);
|
||||
sfall_gl_vars_store(key, value.integerValue);
|
||||
} else if (variable.opcode == VALUE_TYPE_INT) {
|
||||
sfallGlobalVarsStore(variable.integerValue, value.integerValue);
|
||||
sfall_gl_vars_store(variable.integerValue, value.integerValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,14 +201,27 @@ static void opGetGlobalInt(Program* program)
|
|||
int value = 0;
|
||||
if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
||||
const char* key = programGetString(program, variable.opcode, variable.integerValue);
|
||||
sfallGlobalVarsFetch(key, value);
|
||||
sfall_gl_vars_fetch(key, value);
|
||||
} else if (variable.opcode == VALUE_TYPE_INT) {
|
||||
sfallGlobalVarsFetch(variable.integerValue, value);
|
||||
sfall_gl_vars_fetch(variable.integerValue, value);
|
||||
}
|
||||
|
||||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
// get_ini_setting
|
||||
static void op_get_ini_setting(Program* program)
|
||||
{
|
||||
const char* string = programStackPopString(program);
|
||||
|
||||
int value;
|
||||
if (sfall_ini_get_int(string, &value)) {
|
||||
programStackPushInteger(program, value);
|
||||
} else {
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// get_game_mode
|
||||
static void opGetGameMode(Program* program)
|
||||
{
|
||||
|
@ -177,6 +256,19 @@ static void op_set_bodypart_hit_modifier(Program* program)
|
|||
combat_set_hit_location_penalty(hit_location, penalty);
|
||||
}
|
||||
|
||||
// get_ini_string
|
||||
static void op_get_ini_string(Program* program)
|
||||
{
|
||||
const char* string = programStackPopString(program);
|
||||
|
||||
char value[256];
|
||||
if (sfall_ini_get_string(string, value, sizeof(value))) {
|
||||
programStackPushString(program, value);
|
||||
} else {
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// sqrt
|
||||
static void op_sqrt(Program* program)
|
||||
{
|
||||
|
@ -196,6 +288,13 @@ static void op_abs(Program* program)
|
|||
}
|
||||
}
|
||||
|
||||
// get_script
|
||||
static void op_get_script(Program* program)
|
||||
{
|
||||
Object* obj = static_cast<Object*>(programStackPopPointer(program));
|
||||
programStackPushInteger(program, obj->field_80 + 1);
|
||||
}
|
||||
|
||||
// get_proto_data
|
||||
static void op_get_proto_data(Program* program)
|
||||
{
|
||||
|
@ -246,6 +345,19 @@ static void op_set_proto_data(Program* program)
|
|||
*reinterpret_cast<int*>(reinterpret_cast<unsigned char*>(proto) + offset) = value;
|
||||
}
|
||||
|
||||
// set_self
|
||||
static void op_set_self(Program* program)
|
||||
{
|
||||
Object* obj = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
int sid = scriptGetSid(program);
|
||||
|
||||
Script* scr;
|
||||
if (scriptGetScript(sid, &scr) == 0) {
|
||||
scr->overriddenSelf = obj;
|
||||
}
|
||||
}
|
||||
|
||||
// list_begin
|
||||
static void opListBegin(Program* program)
|
||||
{
|
||||
|
@ -417,6 +529,47 @@ static void opGetScreenHeight(Program* program)
|
|||
programStackPushInteger(program, screenGetHeight());
|
||||
}
|
||||
|
||||
// create_message_window
|
||||
static void op_create_message_window(Program* program)
|
||||
{
|
||||
static bool showing = false;
|
||||
|
||||
if (showing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* string = programStackPopString(program);
|
||||
if (string == nullptr || string[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
char* copy = internal_strdup(string);
|
||||
|
||||
const char* body[4];
|
||||
int count = 0;
|
||||
|
||||
char* pch = strchr(copy, '\n');
|
||||
while (pch != nullptr && count < 4) {
|
||||
*pch = '\0';
|
||||
body[count++] = pch + 1;
|
||||
pch = strchr(pch + 1, '\n');
|
||||
}
|
||||
|
||||
showing = true;
|
||||
showDialogBox(copy,
|
||||
body,
|
||||
count,
|
||||
192,
|
||||
116,
|
||||
_colorTable[32328],
|
||||
nullptr,
|
||||
_colorTable[32328],
|
||||
DIALOG_BOX_LARGE);
|
||||
showing = false;
|
||||
|
||||
internal_free(copy);
|
||||
}
|
||||
|
||||
// get_attack_type
|
||||
static void op_get_attack_type(Program* program)
|
||||
{
|
||||
|
@ -428,6 +581,22 @@ static void op_get_attack_type(Program* program)
|
|||
}
|
||||
}
|
||||
|
||||
// force_encounter_with_flags
|
||||
static void op_force_encounter_with_flags(Program* program)
|
||||
{
|
||||
unsigned int flags = programStackPopInteger(program);
|
||||
int map = programStackPopInteger(program);
|
||||
wmForceEncounter(map, flags);
|
||||
}
|
||||
|
||||
// list_as_array
|
||||
static void op_list_as_array(Program* program)
|
||||
{
|
||||
int type = programStackPopInteger(program);
|
||||
int arrayId = ListAsArray(type);
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// atoi
|
||||
static void opParseInt(Program* program)
|
||||
{
|
||||
|
@ -453,6 +622,53 @@ static void op_tile_under_cursor(Program* program)
|
|||
programStackPushInteger(program, tile);
|
||||
}
|
||||
|
||||
// substr
|
||||
static void opSubstr(Program* program)
|
||||
{
|
||||
auto length = programStackPopInteger(program);
|
||||
auto startPos = programStackPopInteger(program);
|
||||
const char* str = programStackPopString(program);
|
||||
|
||||
char buf[5120] = { 0 };
|
||||
|
||||
int len = strlen(str);
|
||||
|
||||
if (startPos < 0) {
|
||||
startPos += len; // start from end
|
||||
if (startPos < 0) {
|
||||
startPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (length < 0) {
|
||||
length += len - startPos; // cutoff at end
|
||||
if (length == 0) {
|
||||
programStackPushString(program, buf);
|
||||
return;
|
||||
}
|
||||
length = abs(length); // length can't be negative
|
||||
}
|
||||
|
||||
// check position
|
||||
if (startPos >= len) {
|
||||
// start position is out of string length, return empty string
|
||||
programStackPushString(program, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 0 || length + startPos > len) {
|
||||
length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string
|
||||
}
|
||||
|
||||
if (length > sizeof(buf) - 1) {
|
||||
length = sizeof(buf) - 1;
|
||||
}
|
||||
|
||||
memcpy(buf, &str[startPos], length);
|
||||
buf[length] = '\0';
|
||||
programStackPushString(program, buf);
|
||||
}
|
||||
|
||||
// strlen
|
||||
static void opGetStringLength(Program* program)
|
||||
{
|
||||
|
@ -460,6 +676,64 @@ static void opGetStringLength(Program* program)
|
|||
programStackPushInteger(program, static_cast<int>(strlen(string)));
|
||||
}
|
||||
|
||||
// metarule2_explosions
|
||||
static void op_explosions_metarule(Program* program)
|
||||
{
|
||||
int param2 = programStackPopInteger(program);
|
||||
int param1 = programStackPopInteger(program);
|
||||
int metarule = programStackPopInteger(program);
|
||||
|
||||
switch (metarule) {
|
||||
case EXPL_FORCE_EXPLOSION_PATTERN:
|
||||
if (param1 != 0) {
|
||||
explosionSetPattern(2, 4);
|
||||
} else {
|
||||
explosionSetPattern(0, 6);
|
||||
}
|
||||
programStackPushInteger(program, 0);
|
||||
break;
|
||||
case EXPL_FORCE_EXPLOSION_ART:
|
||||
explosionSetFrm(param1);
|
||||
programStackPushInteger(program, 0);
|
||||
break;
|
||||
case EXPL_FORCE_EXPLOSION_RADIUS:
|
||||
explosionSetRadius(param1);
|
||||
programStackPushInteger(program, 0);
|
||||
break;
|
||||
case EXPL_FORCE_EXPLOSION_DMGTYPE:
|
||||
explosionSetDamageType(param1);
|
||||
programStackPushInteger(program, 0);
|
||||
break;
|
||||
case EXPL_STATIC_EXPLOSION_RADIUS:
|
||||
weaponSetGrenadeExplosionRadius(param1);
|
||||
weaponSetRocketExplosionRadius(param2);
|
||||
programStackPushInteger(program, 0);
|
||||
break;
|
||||
case EXPL_GET_EXPLOSION_DAMAGE:
|
||||
if (1) {
|
||||
int minDamage;
|
||||
int maxDamage;
|
||||
explosiveGetDamage(param1, &minDamage, &maxDamage);
|
||||
|
||||
ArrayId arrayId = CreateTempArray(2, 0);
|
||||
SetArray(arrayId, ProgramValue { 0 }, ProgramValue { minDamage }, false, program);
|
||||
SetArray(arrayId, ProgramValue { 1 }, ProgramValue { maxDamage }, false, program);
|
||||
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
break;
|
||||
case EXPL_SET_DYNAMITE_EXPLOSION_DAMAGE:
|
||||
explosiveSetDamage(PROTO_ID_DYNAMITE_I, param1, param2);
|
||||
break;
|
||||
case EXPL_SET_PLASTIC_EXPLOSION_DAMAGE:
|
||||
explosiveSetDamage(PROTO_ID_PLASTIC_EXPLOSIVES_I, param1, param2);
|
||||
break;
|
||||
case EXPL_SET_EXPLOSION_MAX_TARGET:
|
||||
explosionSetMaxTargets(param1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// pow (^)
|
||||
static void op_power(Program* program)
|
||||
{
|
||||
|
@ -485,6 +759,146 @@ static void opGetMessage(Program* program)
|
|||
programStackPushString(program, text);
|
||||
}
|
||||
|
||||
// array_key
|
||||
static void opGetArrayKey(Program* program)
|
||||
{
|
||||
auto index = programStackPopInteger(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
auto value = GetArrayKey(arrayId, index, program);
|
||||
programStackPushValue(program, value);
|
||||
}
|
||||
|
||||
// create_array
|
||||
static void opCreateArray(Program* program)
|
||||
{
|
||||
auto flags = programStackPopInteger(program);
|
||||
auto len = programStackPopInteger(program);
|
||||
auto arrayId = CreateArray(len, flags);
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// temp_array
|
||||
static void opTempArray(Program* program)
|
||||
{
|
||||
auto flags = programStackPopInteger(program);
|
||||
auto len = programStackPopInteger(program);
|
||||
auto arrayId = CreateTempArray(len, flags);
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// fix_array
|
||||
static void opFixArray(Program* program)
|
||||
{
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
FixArray(arrayId);
|
||||
}
|
||||
|
||||
// string_split
|
||||
static void opStringSplit(Program* program)
|
||||
{
|
||||
auto split = programStackPopString(program);
|
||||
auto str = programStackPopString(program);
|
||||
auto arrayId = StringSplit(str, split);
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// set_array
|
||||
static void opSetArray(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
auto key = programStackPopValue(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
SetArray(arrayId, key, value, true, program);
|
||||
}
|
||||
|
||||
// arrayexpr
|
||||
static void opStackArray(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
auto key = programStackPopValue(program);
|
||||
auto returnValue = StackArray(key, value, program);
|
||||
programStackPushInteger(program, returnValue);
|
||||
}
|
||||
|
||||
// scan_array
|
||||
static void opScanArray(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
auto returnValue = ScanArray(arrayId, value, program);
|
||||
programStackPushValue(program, returnValue);
|
||||
}
|
||||
|
||||
// get_array
|
||||
static void opGetArray(Program* program)
|
||||
{
|
||||
auto key = programStackPopValue(program);
|
||||
auto arrayId = programStackPopValue(program);
|
||||
|
||||
if (arrayId.isInt()) {
|
||||
auto value = GetArray(arrayId.integerValue, key, program);
|
||||
programStackPushValue(program, value);
|
||||
} else if (arrayId.isString() && key.isInt()) {
|
||||
auto pos = key.asInt();
|
||||
auto str = programGetString(program, arrayId.opcode, arrayId.integerValue);
|
||||
|
||||
char buf[2] = { 0 };
|
||||
if (pos < strlen(str)) {
|
||||
buf[0] = str[pos];
|
||||
programStackPushString(program, buf);
|
||||
} else {
|
||||
programStackPushString(program, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free_array
|
||||
static void opFreeArray(Program* program)
|
||||
{
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
FreeArray(arrayId);
|
||||
}
|
||||
|
||||
// len_array
|
||||
static void opLenArray(Program* program)
|
||||
{
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
programStackPushInteger(program, LenArray(arrayId));
|
||||
}
|
||||
|
||||
// resize_array
|
||||
static void opResizeArray(Program* program)
|
||||
{
|
||||
auto newLen = programStackPopInteger(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
ResizeArray(arrayId, newLen);
|
||||
}
|
||||
|
||||
// party_member_list
|
||||
static void opPartyMemberList(Program* program)
|
||||
{
|
||||
auto includeHidden = programStackPopInteger(program);
|
||||
auto objects = get_all_party_members_objects(includeHidden);
|
||||
auto arrayId = CreateTempArray(objects.size(), SFALL_ARRAYFLAG_RESERVED);
|
||||
for (int i = 0; i < LenArray(arrayId); i++) {
|
||||
SetArray(arrayId, ProgramValue { i }, ProgramValue { objects[i] }, false, program);
|
||||
}
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// type_of
|
||||
static void opTypeOf(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
if (value.isInt()) {
|
||||
programStackPushInteger(program, 1);
|
||||
} else if (value.isFloat()) {
|
||||
programStackPushInteger(program, 2);
|
||||
} else {
|
||||
programStackPushInteger(program, 3);
|
||||
};
|
||||
}
|
||||
|
||||
// round
|
||||
static void opRound(Program* program)
|
||||
{
|
||||
|
@ -559,6 +973,48 @@ static void opArtExists(Program* program)
|
|||
programStackPushInteger(program, artExists(fid));
|
||||
}
|
||||
|
||||
// sfall_func0
|
||||
static void op_sfall_func0(Program* program)
|
||||
{
|
||||
sfall_metarule(program, 0);
|
||||
}
|
||||
|
||||
// sfall_func1
|
||||
static void op_sfall_func1(Program* program)
|
||||
{
|
||||
sfall_metarule(program, 1);
|
||||
}
|
||||
|
||||
// sfall_func2
|
||||
static void op_sfall_func2(Program* program)
|
||||
{
|
||||
sfall_metarule(program, 2);
|
||||
}
|
||||
|
||||
// sfall_func3
|
||||
static void op_sfall_func3(Program* program)
|
||||
{
|
||||
sfall_metarule(program, 3);
|
||||
}
|
||||
|
||||
// sfall_func4
|
||||
static void op_sfall_func4(Program* program)
|
||||
{
|
||||
sfall_metarule(program, 4);
|
||||
}
|
||||
|
||||
// sfall_func5
|
||||
static void op_sfall_func5(Program* program)
|
||||
{
|
||||
sfall_metarule(program, 5);
|
||||
}
|
||||
|
||||
// sfall_func6
|
||||
static void op_sfall_func6(Program* program)
|
||||
{
|
||||
sfall_metarule(program, 6);
|
||||
}
|
||||
|
||||
// div (/)
|
||||
static void op_div(Program* program)
|
||||
{
|
||||
|
@ -588,21 +1044,31 @@ void sfallOpcodesInit()
|
|||
interpreterRegisterOpcode(0x815B, opSetPcBonusStat);
|
||||
interpreterRegisterOpcode(0x815C, op_get_pc_base_stat);
|
||||
interpreterRegisterOpcode(0x815D, opGetPcBonusStat);
|
||||
interpreterRegisterOpcode(0x8162, op_tap_key);
|
||||
interpreterRegisterOpcode(0x8163, op_get_year);
|
||||
interpreterRegisterOpcode(0x8164, op_game_loaded);
|
||||
interpreterRegisterOpcode(0x816A, op_set_global_script_repeat);
|
||||
interpreterRegisterOpcode(0x816C, op_key_pressed);
|
||||
interpreterRegisterOpcode(0x8170, op_in_world_map);
|
||||
interpreterRegisterOpcode(0x8171, op_force_encounter);
|
||||
interpreterRegisterOpcode(0x8172, op_set_world_map_pos);
|
||||
interpreterRegisterOpcode(0x8193, opGetCurrentHand);
|
||||
interpreterRegisterOpcode(0x819B, op_set_global_script_type);
|
||||
interpreterRegisterOpcode(0x819D, opSetGlobalVar);
|
||||
interpreterRegisterOpcode(0x819E, opGetGlobalInt);
|
||||
interpreterRegisterOpcode(0x81AC, op_get_ini_setting);
|
||||
interpreterRegisterOpcode(0x81AF, opGetGameMode);
|
||||
interpreterRegisterOpcode(0x81B3, op_get_uptime);
|
||||
interpreterRegisterOpcode(0x81B6, op_set_car_current_town);
|
||||
interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier);
|
||||
interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier);
|
||||
interpreterRegisterOpcode(0x81EB, op_get_ini_string);
|
||||
interpreterRegisterOpcode(0x81EC, op_sqrt);
|
||||
interpreterRegisterOpcode(0x81ED, op_abs);
|
||||
interpreterRegisterOpcode(0x81F5, op_get_script);
|
||||
interpreterRegisterOpcode(0x8204, op_get_proto_data);
|
||||
interpreterRegisterOpcode(0x8205, op_set_proto_data);
|
||||
interpreterRegisterOpcode(0x8206, op_set_self);
|
||||
interpreterRegisterOpcode(0x820D, opListBegin);
|
||||
interpreterRegisterOpcode(0x820E, opListNext);
|
||||
interpreterRegisterOpcode(0x820F, opListEnd);
|
||||
|
@ -618,17 +1084,43 @@ void sfallOpcodesInit()
|
|||
interpreterRegisterOpcode(0x821E, op_get_mouse_buttons);
|
||||
interpreterRegisterOpcode(0x8220, opGetScreenWidth);
|
||||
interpreterRegisterOpcode(0x8221, opGetScreenHeight);
|
||||
interpreterRegisterOpcode(0x8224, op_create_message_window);
|
||||
interpreterRegisterOpcode(0x8228, op_get_attack_type);
|
||||
interpreterRegisterOpcode(0x8229, op_force_encounter_with_flags);
|
||||
interpreterRegisterOpcode(0x822D, opCreateArray);
|
||||
interpreterRegisterOpcode(0x822E, opSetArray);
|
||||
interpreterRegisterOpcode(0x822F, opGetArray);
|
||||
interpreterRegisterOpcode(0x8230, opFreeArray);
|
||||
interpreterRegisterOpcode(0x8231, opLenArray);
|
||||
interpreterRegisterOpcode(0x8232, opResizeArray);
|
||||
interpreterRegisterOpcode(0x8233, opTempArray);
|
||||
interpreterRegisterOpcode(0x8234, opFixArray);
|
||||
interpreterRegisterOpcode(0x8235, opStringSplit);
|
||||
interpreterRegisterOpcode(0x8236, op_list_as_array);
|
||||
interpreterRegisterOpcode(0x8237, opParseInt);
|
||||
interpreterRegisterOpcode(0x8238, op_atof);
|
||||
interpreterRegisterOpcode(0x8239, opScanArray);
|
||||
interpreterRegisterOpcode(0x824B, op_tile_under_cursor);
|
||||
interpreterRegisterOpcode(0x824E, opSubstr);
|
||||
interpreterRegisterOpcode(0x824F, opGetStringLength);
|
||||
interpreterRegisterOpcode(0x8253, opTypeOf);
|
||||
interpreterRegisterOpcode(0x8256, opGetArrayKey);
|
||||
interpreterRegisterOpcode(0x8257, opStackArray);
|
||||
interpreterRegisterOpcode(0x8261, op_explosions_metarule);
|
||||
interpreterRegisterOpcode(0x8263, op_power);
|
||||
interpreterRegisterOpcode(0x826B, opGetMessage);
|
||||
interpreterRegisterOpcode(0x8267, opRound);
|
||||
interpreterRegisterOpcode(0x826B, opGetMessage);
|
||||
interpreterRegisterOpcode(0x826E, op_make_straight_path);
|
||||
interpreterRegisterOpcode(0x826F, op_obj_blocking_at);
|
||||
interpreterRegisterOpcode(0x8271, opPartyMemberList);
|
||||
interpreterRegisterOpcode(0x8274, opArtExists);
|
||||
interpreterRegisterOpcode(0x8276, op_sfall_func0);
|
||||
interpreterRegisterOpcode(0x8277, op_sfall_func1);
|
||||
interpreterRegisterOpcode(0x8278, op_sfall_func2);
|
||||
interpreterRegisterOpcode(0x8279, op_sfall_func3);
|
||||
interpreterRegisterOpcode(0x827A, op_sfall_func4);
|
||||
interpreterRegisterOpcode(0x827B, op_sfall_func5);
|
||||
interpreterRegisterOpcode(0x827C, op_sfall_func6);
|
||||
interpreterRegisterOpcode(0x827F, op_div);
|
||||
}
|
||||
|
||||
|
|
|
@ -1151,7 +1151,7 @@ static int skillGetFreeUsageSlot(int skill)
|
|||
}
|
||||
}
|
||||
|
||||
int time = gameTimeGetTime();
|
||||
unsigned int time = gameTimeGetTime();
|
||||
int hoursSinceLastUsage = (time - _timesSkillUsed[skill][0]) / GAME_TIME_TICKS_PER_HOUR;
|
||||
if (hoursSinceLastUsage <= 24) {
|
||||
return -1;
|
||||
|
|
19
src/svga.cc
19
src/svga.cc
|
@ -102,6 +102,7 @@ void _zero_vid_mem()
|
|||
int _GNW95_init_mode_ex(int width, int height, int bpp)
|
||||
{
|
||||
bool fullscreen = true;
|
||||
int scale = 1;
|
||||
|
||||
Config resolutionConfig;
|
||||
if (configInit(&resolutionConfig)) {
|
||||
|
@ -121,6 +122,18 @@ int _GNW95_init_mode_ex(int width, int height, int bpp)
|
|||
fullscreen = !windowed;
|
||||
}
|
||||
|
||||
int scaleValue;
|
||||
if (configGetInt(&resolutionConfig, "MAIN", "SCALE_2X", &scaleValue)) {
|
||||
scale = scaleValue + 1; // 0 = 1x, 1 = 2x
|
||||
// Only allow scaling if resulting game resolution is >= 640x480
|
||||
if ((width / scale) < 640 || (height / scale) < 480) {
|
||||
scale = 1;
|
||||
} else {
|
||||
width /= scale;
|
||||
height /= scale;
|
||||
}
|
||||
}
|
||||
|
||||
configGetBool(&resolutionConfig, "IFACE", "IFACE_BAR_MODE", &gInterfaceBarMode);
|
||||
configGetInt(&resolutionConfig, "IFACE", "IFACE_BAR_WIDTH", &gInterfaceBarWidth);
|
||||
configGetInt(&resolutionConfig, "IFACE", "IFACE_BAR_SIDE_ART", &gInterfaceSidePanelsImageId);
|
||||
|
@ -129,7 +142,7 @@ int _GNW95_init_mode_ex(int width, int height, int bpp)
|
|||
configFree(&resolutionConfig);
|
||||
}
|
||||
|
||||
if (_GNW95_init_window(width, height, fullscreen) == -1) {
|
||||
if (_GNW95_init_window(width, height, fullscreen, scale) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -157,7 +170,7 @@ int _init_vesa_mode(int width, int height)
|
|||
}
|
||||
|
||||
// 0x4CAEDC
|
||||
int _GNW95_init_window(int width, int height, bool fullscreen)
|
||||
int _GNW95_init_window(int width, int height, bool fullscreen, int scale)
|
||||
{
|
||||
if (gSdlWindow == NULL) {
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||
|
@ -172,7 +185,7 @@ int _GNW95_init_window(int width, int height, bool fullscreen)
|
|||
windowFlags |= SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
|
||||
gSdlWindow = SDL_CreateWindow(gProgramWindowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, windowFlags);
|
||||
gSdlWindow = SDL_CreateWindow(gProgramWindowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * scale, height * scale, windowFlags);
|
||||
if (gSdlWindow == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ void _get_start_mode_();
|
|||
void _zero_vid_mem();
|
||||
int _GNW95_init_mode_ex(int width, int height, int bpp);
|
||||
int _init_vesa_mode(int width, int height);
|
||||
int _GNW95_init_window(int width, int height, bool fullscreen);
|
||||
int _GNW95_init_window(int width, int height, bool fullscreen, int scale);
|
||||
int directDrawInit(int width, int height, int bpp);
|
||||
void directDrawFree();
|
||||
void directDrawSetPaletteInRange(unsigned char* a1, int a2, int a3);
|
||||
|
|
11
src/tile.cc
11
src/tile.cc
|
@ -654,6 +654,17 @@ static void tileRefreshGame(Rect* rect, int elevation)
|
|||
gTileWindowRefreshProc(&rectToUpdate);
|
||||
}
|
||||
|
||||
// 0x4B1634
|
||||
void tile_toggle_roof(bool refresh)
|
||||
{
|
||||
gTileRoofIsVisible = !gTileRoofIsVisible;
|
||||
|
||||
if (refresh) {
|
||||
// NOTE: Uninline.
|
||||
tileWindowRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4B166C
|
||||
int tileRoofIsVisible()
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ void tileEnable();
|
|||
void tileWindowRefreshRect(Rect* rect, int elevation);
|
||||
void tileWindowRefresh();
|
||||
int tileSetCenter(int tile, int flags);
|
||||
void tile_toggle_roof(bool refresh);
|
||||
int tileRoofIsVisible();
|
||||
int tileToScreenXY(int tile, int* x, int* y, int elevation);
|
||||
int tileFromScreenXY(int x, int y, int elevation, bool ignoreBounds = false);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "delay.h"
|
||||
#include "input.h"
|
||||
#include "kb.h"
|
||||
#include "memory.h"
|
||||
|
@ -228,8 +229,7 @@ int vcrUpdate()
|
|||
* (vcrEntry->time - stru_6AD940.time)
|
||||
/ (vcrEntry->counter - stru_6AD940.counter);
|
||||
|
||||
while (getTicksSince(_vcr_start_time) < delay) {
|
||||
}
|
||||
delay_ms(delay - (getTicks() - _vcr_start_time));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,4 +438,4 @@ bool vcrReadEntry(VcrEntry* vcrEntry, File* stream)
|
|||
return false;
|
||||
}
|
||||
|
||||
} // fallout
|
||||
} // namespace fallout
|
||||
|
|
|
@ -70,6 +70,8 @@ typedef struct ManagedWindow {
|
|||
|
||||
typedef int (*INITVIDEOFN)();
|
||||
|
||||
static void redrawButton(ManagedButton* managedButton);
|
||||
|
||||
// 0x51DCAC
|
||||
static int _holdTime = 250;
|
||||
|
||||
|
@ -663,6 +665,38 @@ void _setButtonGFX(int width, int height, unsigned char* normal, unsigned char*
|
|||
}
|
||||
}
|
||||
|
||||
// 0x4B75F4
|
||||
static void redrawButton(ManagedButton* managedButton)
|
||||
{
|
||||
_win_register_button_image(managedButton->btn, managedButton->normal, managedButton->pressed, managedButton->hover, false);
|
||||
}
|
||||
|
||||
// 0x4B7610
|
||||
bool _windowHide()
|
||||
{
|
||||
ManagedWindow* managedWindow = &(gManagedWindows[gCurrentManagedWindowIndex]);
|
||||
if (managedWindow->window == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
windowHide(managedWindow->window);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x4B7648
|
||||
bool _windowShow()
|
||||
{
|
||||
ManagedWindow* managedWindow = &(gManagedWindows[gCurrentManagedWindowIndex]);
|
||||
if (managedWindow->window == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
windowShow(managedWindow->window);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x4B7734
|
||||
int _windowWidth()
|
||||
{
|
||||
|
@ -1714,7 +1748,8 @@ bool _windowAddButtonGfx(const char* buttonName, char* pressedFileName, char* no
|
|||
buttonSetMask(managedButton->btn, managedButton->normal);
|
||||
}
|
||||
|
||||
_win_register_button_image(managedButton->btn, managedButton->normal, managedButton->pressed, managedButton->hover, 0);
|
||||
// NOTE: Uninline.
|
||||
redrawButton(managedButton);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1952,7 +1987,8 @@ bool _windowAddButtonTextWithOffsets(const char* buttonName, const char* text, i
|
|||
buttonSetMask(managedButton->btn, managedButton->normal);
|
||||
}
|
||||
|
||||
_win_register_button_image(managedButton->btn, managedButton->normal, managedButton->pressed, managedButton->hover, 0);
|
||||
// NOTE: Uninline.
|
||||
redrawButton(managedButton);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2646,4 +2682,19 @@ void _fillBuf3x3(unsigned char* src, int srcWidth, int srcHeight, unsigned char*
|
|||
destWidth);
|
||||
}
|
||||
|
||||
bool _windowShowNamed(const char* windowName)
|
||||
{
|
||||
for (int index = 0; index < MANAGED_WINDOW_COUNT; index++) {
|
||||
ManagedWindow* managedWindow = &(gManagedWindows[index]);
|
||||
if (managedWindow->window != -1) {
|
||||
if (compat_stricmp(managedWindow->name, windowName) == 0) {
|
||||
windowShow(managedWindow->window);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
|
|
@ -63,6 +63,8 @@ void _doRightButtonPress(int btn, int keyCode);
|
|||
void sub_4B704C(int btn, int mouseEvent);
|
||||
void _doRightButtonRelease(int btn, int keyCode);
|
||||
void _setButtonGFX(int width, int height, unsigned char* normal, unsigned char* pressed, unsigned char* a5);
|
||||
bool _windowHide();
|
||||
bool _windowShow();
|
||||
int _windowWidth();
|
||||
int _windowHeight();
|
||||
bool _windowDraw();
|
||||
|
@ -127,6 +129,8 @@ void _drawScaledBuf(unsigned char* dest, int destWidth, int destHeight, unsigned
|
|||
void _alphaBltBuf(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* alphaWindowBuffer, unsigned char* alphaBuffer, unsigned char* dest, int destPitch);
|
||||
void _fillBuf3x3(unsigned char* src, int srcWidth, int srcHeight, unsigned char* dest, int destWidth, int destHeight);
|
||||
|
||||
bool _windowShowNamed(const char* name);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* WINDOW_H */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue