mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07939225c6 | ||
|
|
b10526330c | ||
|
|
4c1cad7122 | ||
|
|
b76ff8257e | ||
|
|
6edbd70d60 | ||
|
|
15917dccb3 | ||
|
|
33aae49066 | ||
|
|
bfd41bb40c | ||
|
|
0ccaef5ef0 |
+2
-3
@@ -66,9 +66,8 @@ Available commands:
|
||||
1. `./<BUILD_DIR>/ce-dat-tool <archive.dat> list [pattern]`
|
||||
2. `ce-dat-tool <archive.dat> info [pattern]`
|
||||
3. `ce-dat-tool <archive.dat> extract [--lower] <output-dir> [pattern]`
|
||||
4. `ce-dat-tool <archive.dat> extract [--lower] (--file-list|--files-from) <list-file> <output-dir>`
|
||||
5. `ce-dat-tool <archive.dat> cat <entry>`
|
||||
6. `ce-dat-tool create <input-dir> <archive.dat>`
|
||||
4. `ce-dat-tool <archive.dat> cat <entry>`
|
||||
5. `ce-dat-tool create <input-dir> <archive.dat>`
|
||||
|
||||
Use `--lower` with `extract` when you want every extracted file and directory name forced to lowercase.
|
||||
For example:
|
||||
|
||||
@@ -144,13 +144,13 @@ In time this stuff will receive in-game interface, right now you have to do it m
|
||||
* Expanded 4-row barter screen
|
||||
* Expanded AP bar
|
||||
* Party members can loot and barter in place of PC
|
||||
* Increased pathfinding nodes 5x for more accurate pathfinding, and use accurate path length for walk vs run
|
||||
* Increased pathfinding nodes 5x for more accurate pathfinding
|
||||
* Ctrl-click to quickly move items when bartering, looting, or stealing
|
||||
* _a_ to select "all" when selecting item quantity, and for `Take All` when looting
|
||||
* When bartering, caps default to the right amount to balance the trade
|
||||
* _a_ to select "all" when selecting item quantity
|
||||
* _a_ to `Take All` when looting
|
||||
* When bartering, caps default to the right amount to balance the trade (if possible)
|
||||
* Music continues playing between maps
|
||||
* Auto open doors
|
||||
* Integrated "HELP" menu
|
||||
* 44.1 kHz stereo sound/music supported, in .ogg and .wav format as well as legacy .acm
|
||||
* Last used save slot is remembered
|
||||
* Item/Corpse/Container/Critter highlighting (configure using [mods/sfall-mods.ini](https://github.com/sfall-team/sfall/blob/master/artifacts/config_files/sfall-mods.ini))
|
||||
|
||||
@@ -53,7 +53,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| Perks and traits | set_perk_image<br>set_perk_*<br>set_pyromaniac_mod<br>apply_heaveho_fix<br>set_swiftlearner_mod<br>has/set_fake_perk<br>has/set_fake_trait<br>set_selectable_perk<br>set_perkbox_title<br>show/hide_real_perks<br>perk_add_mode<br>clear_selectable_perks<br>add/remove_trait<br>seq_perk_freq | not implemented | - |
|
||||
| Virtual file system | fs_create<br>fs_copy<br>fs_find<br>fs_read/write_*<br>fs_delete<br>fs_size<br>fs_pos<br>fs_seek<br>fs_resize | đźš« | Open an issue if you have a use case for these |
|
||||
| Combat / Knockback | set_weapon_knockback<br>set_target_knockback<br>set_attacker_knockback<br>remove_weapon_knockback<br>remove_target_knockback<br>remove_attacker_knockback | not implemented | - |
|
||||
| Maps and encounters | in_world_map<br>force_encounter{_with_flags}<br>set_map_time_multi<br>get/set_map_enter_position<br>exec_map_update_scripts<br>get/set_terrain_name<br>set_town_title<br>get/set_can_rest_on_map<br>set_rest_heal_time<br>set_rest_mode<br>set_worldmap_heal_time | implemented: in_world_map, force_encounter, force_encounter_with_flags, set_map_time_multi | - |
|
||||
| Maps and encounters | in_world_map<br>force_encounter<br>force_encounter_with_flags<br>set_map_time_multi<br>get/set_map_enter_position<br>exec_map_update_scripts<br>get/set_terrain_name<br>set_town_title<br>get/set_can_rest_on_map<br>set_rest_heal_time<br>set_rest_mode<br>set_worldmap_heal_time | implemented: in_world_map, force_encounter, force_encounter_with_flags, set_map_time_multi | - |
|
||||
| Maps and encounters / Worldmap | get_world_map_x/y_pos<br>set_world_map_pos | âś… | - |
|
||||
| Audio | play_sfall_sound<br>stop_sfall_sound | âś… | `play_sfall_sound` currently supports `.acm`, `.wav`, `.ogg` formats, and can load from `.dat` archives. `.mp3` is not yet supported. |
|
||||
| Combat / Weapons and ammo | get/set_weapon_ammo_pid<br>get/set_weapon_ammo_count | âś… | - |
|
||||
@@ -63,7 +63,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| Lists | list_begin<br>list_next<br>list_end<br>list_as_array<br>party_member_list | âś… | - |
|
||||
| Explosions | set_attack_explosion_pattern<br>set_attack_explosion_art<br>set_attack_explosion_radius<br>set_attack_is_explosion_fire<br>set_explosion_radius<br>set_dynamite_damage<br>set_plastic_damage<br>get_explosion_damage<br>set_explosion_max_targets<br>item_make_explosive | âś… except item_make_explosive | - |
|
||||
| Animations | reg_anim_combat_check<br>reg_anim_destroy<br>reg_anim_animate_and_hide<br>reg_anim_light<br>reg_anim_change_fid<br>reg_anim_take_out<br>reg_anim_turn_towards<br>reg_anim_callback<br>reg_anim_animate_and_move | âś… except reg_anim_callback | - |
|
||||
| Art and appearance | art_exists<br>art_frame_data<br>refresh_pc_art<br>art_cache_clear | âś… | - |
|
||||
| Art and appearance | art_exists<br>refresh_pc_art<br>art_cache_clear<br>set_hero_race<br>set_hero_style | implemented: art_exists, refresh_pc_art, art_cache_clear | - |
|
||||
| Tiles and paths | get_tile_fid<br>tile_under_cursor<br>tile_light<br>tile_get_objs<br>tile_refresh_display<br>obj_blocking_tile<br>tile_by_position<br>get_tile_ground_fid<br>get_tile_roof_fid<br>obj_blocking_line<br>path_find_to<br>objects_in_radius | âś… | - |
|
||||
| Utility | sprintf<br>typeof<br>atoi<br>atof | âś… | - |
|
||||
| Utility / Strings | string_split<br>substr<br>strlen<br>charcode<br>get_string_pointer<br>string_find<br>string_find_from<br>string_format<br>string_format_array<br>string_replace<br>string_to_case<br>string_compare | âś… | `get_string_pointer` is deprecated and intentionally omitted. |
|
||||
@@ -75,18 +75,18 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| Global scripts / Global script functions | set_global_script_repeat<br>set_global_script_type<br>available_global_script_types | âś… except available_global_script_types | - |
|
||||
| Combat | attack_is_aimed<br>block_combat<br>force_aimed_shots<br>disable_aimed_shots<br>get_attack_type<br>get/set_bodypart_hit_modifier<br>combat_data<br>get/set/reset_critical_table<br>get_last_target<br>get_last_attacker<br>set_critter_burst_disable<br>get/set_critter_current_ap<br>set_spray_settings<br>get/set_combat_free_move | âś… except block_combat, force_aimed_shots, disable_aimed_shots, get_last_target, get_last_attacker, set_spray_settings | - |
|
||||
| Car | set_car_current_town<br>car_gas_amount<br>set_car_intface_art | implemented: all except set_car_intface_art | - |
|
||||
| Interface / Windows and images | interface_art_draw<br>interface_print<br>draw_image{_scaled}<br>get_window_under_mouse<br>create_win<br>get_window_attribute<br>message_box<br>set_window_flag<br>win_fill_color<br>interface_overlay<br>dialog_message<br>get_text_width<br>hide_window<br>show_window<br>create_message_window | âś… except interface_print, interface_overlay | - |
|
||||
| Interface / Windows and images | art_frame_data<br>interface_art_draw<br>interface_print<br>draw_image<br>draw_image_scaled<br>get_window_under_mouse<br>create_win<br>get_window_attribute<br>message_box<br>set_window_flag<br>win_fill_color<br>interface_overlay<br>dialog_message<br>get_text_width<br>hide_window<br>show_window<br>create_message_window | âś… except interface_print, draw_image, draw_image_scaled, interface_overlay | - |
|
||||
| Interface / Outline | outlined_object<br>get_outline<br>set_outline | âś… | - |
|
||||
| Interface / Main interface | intface_is_hidden<br>intface_redraw<br>intface_hide<br>intface_show<br>set_quest_failure_value | implemented: only intface_redraw | `intface_redraw` only supports the zero-argument form; the optional-argument path is explicitly unimplemented. |
|
||||
| Interface / Inventory | display_stats<br>inventory_redraw<br>critter_inven_obj2<br>get_current_inven_size<br>item_weight | âś… except get_current_inven_size | - |
|
||||
| Interface / Cursor | get/set_cursor_mode | âś… | - |
|
||||
| Locks | lock_is_jammed<br>unjam_lock<br>set_unjam_locks_time | not implemented | - |
|
||||
| INI settings | get_ini_setting<br>get_ini_string<br>get_ini_section<br>get_ini_sections<br>get_ini_config<br>get_ini_config_db<br>set_ini_setting | âś… | `modified_ini` is intentionally omitted as deprecated. |
|
||||
| Objects and scripts | set_self<br>set_dude_obj<br>real_dude_obj<br>remove_script<br>get/set_script<br>obj_is_carrying_obj<br>loot_obj<br>dialog_obj<br>obj_under_cursor<br>get/set_object_data<br>get/set_flags<br>set_unique_id<br>set_scr_name<br>obj_is_openable<br>get/set_proto_data<br>get_object_ai_data | implemented: set_self, real_dude_obj, get/set/remove_script, obj_is_carrying_obj, loot_obj, dialog_obj, obj_under_cursor, get_object_data, get_flags, set_flags, set_unique_id, obj_is_openable, get_proto_data, set_proto_data | get_object_data assumes raw memory offsets which may return different values in CE |
|
||||
| Objects and scripts | set_self<br>set_dude_obj<br>real_dude_obj<br>remove_script<br>get/set_script<br>obj_is_carrying_obj<br>loot_obj<br>dialog_obj<br>obj_under_cursor<br>get/set_object_data<br>get/set_flags<br>set_unique_id<br>set_scr_name<br>obj_is_openable<br>get/set_proto_data<br>get_object_ai_data | implemented: set_self, get/set/remove_script, obj_is_carrying_obj, loot_obj, dialog_obj, obj_under_cursor, get_object_data, get_flags, set_flags, set_unique_id, obj_is_openable, get_proto_data, set_proto_data | - |
|
||||
| Other / Game management | set_movie_path<br>stop/resume_game<br>mark_movie_played<br>game_loaded<br>get_game_mode<br>get_uptime<br>signal_close_game | implemented: game_loaded, get_game_mode, get_uptime, signal_close_game | - |
|
||||
| Gameplay tweaks | set_pickpocket_max<br>set_hit_chance_max<br>set_xp_mod<br>set_critter_hit_chance_mod<br>set_base_hit_chance_mod<br>set_hp_per_level_mod<br>gdialog_get_barter_mod<br>get/set_unspent_ap_bonus<br>get/set_unspent_ap_perk_bonus<br>set_base_pickpocket_mod<br>set_critter_pickpocket_mod<br>get/set_inven_ap_cost<br>set_drugs_data<br>get_kill_counter<br>mod_kill_counter<br>set_pipboy_available | implemented: gdialog_get_barter_mod, get/set_unspent_ap{_perk}_bonus, get/set_inven_ap_cost | - |
|
||||
| NPCs | inc_npc_level<br>get_npc_level<br>npc_engine_level_up | not implemented | - |
|
||||
| Hero Appearance | set_dm/df_model<br>hero_select_win<br>set_hero_race<br>set_hero_style | not implemented | - |
|
||||
| Hero Appearange | set_dm/df_model<br>hero_select_win | not implemented | - |
|
||||
| Events | add_g_timer_event<br>remove_timer_event<br>create_spatial<br>spatial_radius | not implemented | - |
|
||||
| Other | get_year<br>active_hand<br>toggle_active_hand<br>get/set_viewport_x/y<br>get_light_level<br>message_str_game<br>sneak_success<br>unwield_slot<br>add_extra_msg_file<br>get_metarule_table<br>metarule_exist<br> | âś… except get/set_viewport_x/y, sneak_success | `input_funcs_available`, `nb_create_char` are deprecated in sfall and intentionally absent in CE. `add_extra_msg_file` does not support the explicit `fileNumber` form in CE. |
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{0}{}{HELP}
|
||||
@@ -1 +0,0 @@
|
||||
{0}{}{AIDE}
|
||||
@@ -1 +0,0 @@
|
||||
{0}{}{HILFE}
|
||||
@@ -1 +0,0 @@
|
||||
{0}{}{AIUTO}
|
||||
@@ -1 +0,0 @@
|
||||
{0}{}{POMOC}
|
||||
@@ -1 +0,0 @@
|
||||
{0}{}{AJUDA}
|
||||
@@ -1 +0,0 @@
|
||||
{0}{}{ПОМОЩЬ}
|
||||
@@ -1 +0,0 @@
|
||||
{0}{}{AYUDA}
|
||||
@@ -130,8 +130,6 @@ inventory_columns=2
|
||||
;Set to 1 to scale only the main menu background image to fit the screen while maintaining aspect ratio.
|
||||
;Set to 2 to also scale the main menu button panel, red buttons, and button labels with the menu art.
|
||||
main_menu_scale_mode=1
|
||||
;Set to 1 to show a Help option in the in-game options menu which opens the F1 help screen.
|
||||
in_game_menu_help=1
|
||||
|
||||
|
||||
[qol]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "lib.arrays.h" // For arrays_equal, len_array, etc.
|
||||
#include "test_utils.h" // For assertEquals, VALTYPE_STR, report_test_results
|
||||
#include "sfall.h"
|
||||
|
||||
// To test: copy test.ini into the game folder before running
|
||||
procedure ini_test_suite begin
|
||||
@@ -30,15 +29,6 @@ procedure ini_test_suite begin
|
||||
call assertEquals("TC2 get_ini_setting empty int", get_ini_setting("test.ini|ValidSection|EmptyValue"), 0);
|
||||
call assertEquals("TC2 get_ini_string empty string", get_ini_string("test.ini|ValidSection|EmptyValue"), "");
|
||||
|
||||
// Regression: sfall resolves leading-backslash paths relative to the game root.
|
||||
result_array := get_ini_section("\\test.ini", "ValidSection");
|
||||
call assertEquals("TC2B Size", len_array(result_array), 3);
|
||||
call assertEquals("TC2B Key1", result_array["Key1"], "Value1");
|
||||
call assertEquals("TC2B get_ini_setting int", get_ini_setting("\\test.ini|ValidSection|Key2"), 2);
|
||||
call assertEquals("TC2B get_ini_string string", get_ini_string("\\test.ini|ValidSection|Key1"), "Value1");
|
||||
result_array := get_ini_sections("\\test.ini");
|
||||
call assertEquals("TC2B get_ini_sections size", len_array(result_array), 3);
|
||||
|
||||
// INI file not found
|
||||
result_array := get_ini_section("nonexistent.ini", "AnySection");
|
||||
call assertEquals("TC3 Size", len_array(result_array), 0);
|
||||
|
||||
+12
-42
@@ -1,8 +1,5 @@
|
||||
#include "animation.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -40,13 +37,9 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// sfall exposes this as [Misc] AnimationsAtOnceLimit. Keep CE aligned with
|
||||
// sfall's default 64 total / 52 non-reserved slots for now; this can become
|
||||
// configurable later.
|
||||
#define ANIMATION_SEQUENCE_LIST_CAPACITY 64
|
||||
#define ANIMATION_SEQUENCE_LIST_CAPACITY 32
|
||||
#define ANIMATION_DESCRIPTION_LIST_CAPACITY 55
|
||||
#define ANIMATION_SAD_LIST_CAPACITY 64
|
||||
#define ANIMATION_NON_RESERVED_LIST_CAPACITY 52
|
||||
#define ANIMATION_SAD_LIST_CAPACITY 24
|
||||
|
||||
#define ANIMATION_SEQUENCE_FORCED 0x01
|
||||
|
||||
@@ -82,11 +75,6 @@ typedef enum AnimationKind {
|
||||
ANIM_KIND_SET_LIGHT_INTENSITY,
|
||||
} AnimationKind;
|
||||
|
||||
static bool animationKindIsCallback(int kind)
|
||||
{
|
||||
return kind == ANIM_KIND_CALLBACK || kind == ANIM_KIND_CALLBACK3;
|
||||
}
|
||||
|
||||
typedef enum AnimationSequenceFlags {
|
||||
// Specifies that the animation sequence has high priority, it cannot be
|
||||
// cleared.
|
||||
@@ -345,7 +333,7 @@ static AnimationSad gAnimationSads[ANIMATION_SAD_LIST_CAPACITY];
|
||||
static PathNode gClosedPathNodeList[PATH_NODE_CAPACITY];
|
||||
|
||||
// 0x54CC14 anim_set
|
||||
static AnimationSequence gAnimationSequences[ANIMATION_SEQUENCE_LIST_CAPACITY];
|
||||
static AnimationSequence gAnimationSequences[32];
|
||||
|
||||
// 0x561814 seen_tile
|
||||
static unsigned char gPathfinderProcessedTiles[5000];
|
||||
@@ -449,7 +437,7 @@ static int _anim_free_slot(int requestOptions)
|
||||
}
|
||||
|
||||
return -1;
|
||||
} else if ((requestOptions & ANIMATION_REQUEST_RESERVED) != 0 || v2 < ANIMATION_NON_RESERVED_LIST_CAPACITY) {
|
||||
} else if ((requestOptions & ANIMATION_REQUEST_RESERVED) != 0 || v2 < 20) {
|
||||
return v1;
|
||||
}
|
||||
|
||||
@@ -484,7 +472,7 @@ int reg_anim_clear(Object* a1)
|
||||
int animationDescriptionIndex;
|
||||
for (animationDescriptionIndex = 0; animationDescriptionIndex < animationSequence->length; animationDescriptionIndex++) {
|
||||
AnimationDescription* animationDescription = &(animationSequence->animations[animationDescriptionIndex]);
|
||||
if (a1 != animationDescription->owner || animationKindIsCallback(animationDescription->kind)) {
|
||||
if (a1 != animationDescription->owner || animationDescription->kind == 11) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -598,7 +586,7 @@ static int _check_registry(Object* obj)
|
||||
if (animationSequenceIndex != gAnimationSequenceCurrentIndex && animationSequence->step != ANIM_COMPLETE) {
|
||||
for (int animationDescriptionIndex = 0; animationDescriptionIndex < animationSequence->length; animationDescriptionIndex++) {
|
||||
AnimationDescription* animationDescription = &(animationSequence->animations[animationDescriptionIndex]);
|
||||
if (obj == animationDescription->owner && !animationKindIsCallback(animationDescription->kind)) {
|
||||
if (obj == animationDescription->owner && animationDescription->kind != 11) {
|
||||
if ((animationSequence->flags & ANIM_SEQ_INSIGNIFICANT) == 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -630,7 +618,7 @@ int animationIsBusy(Object* a1)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (animationKindIsCallback(animationDescription->kind)) {
|
||||
if (animationDescription->kind == ANIM_KIND_CALLBACK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1620,25 +1608,12 @@ static int _anim_set_end(int animationSequenceIndex)
|
||||
}
|
||||
}
|
||||
|
||||
std::array<Object*, ANIMATION_DESCRIPTION_LIST_CAPACITY> destroyedOwners = {};
|
||||
std::size_t destroyedOwnersLength = 0;
|
||||
auto ownerWasDestroyed = [&destroyedOwners, &destroyedOwnersLength](Object* owner) {
|
||||
return std::find(destroyedOwners.begin(), destroyedOwners.begin() + destroyedOwnersLength, owner) != destroyedOwners.begin() + destroyedOwnersLength;
|
||||
};
|
||||
|
||||
for (i = 0; i < animationSequence->length; i++) {
|
||||
animationDescription = &(animationSequence->animations[i]);
|
||||
if (animationDescription->kind == ANIM_KIND_HIDE && ((i < animationSequence->animationIndex) || (animationDescription->extendedFlags & ANIMATION_SEQUENCE_FORCED))) {
|
||||
Object* owner = animationDescription->owner;
|
||||
if (ownerWasDestroyed(owner)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
destroyedOwners[destroyedOwnersLength++] = owner;
|
||||
|
||||
Rect rect;
|
||||
int elevation = owner->elevation;
|
||||
objectDestroy(owner, &rect);
|
||||
int elevation = animationDescription->owner->elevation;
|
||||
objectDestroy(animationDescription->owner, &rect);
|
||||
tileWindowRefreshRect(&rect, elevation);
|
||||
}
|
||||
}
|
||||
@@ -1647,23 +1622,18 @@ static int _anim_set_end(int animationSequenceIndex)
|
||||
animationDescription = &(animationSequence->animations[i]);
|
||||
if (animationDescription->artCacheKey) {
|
||||
artUnlock(animationDescription->artCacheKey);
|
||||
animationDescription->artCacheKey = nullptr;
|
||||
}
|
||||
|
||||
if (!animationKindIsCallback(animationDescription->kind)) {
|
||||
if (animationDescription->kind != 11 && animationDescription->kind != 12) {
|
||||
// TODO: Check.
|
||||
if (animationDescription->kind != ANIM_KIND_PING) {
|
||||
Object* owner = animationDescription->owner;
|
||||
if (ownerWasDestroyed(owner)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FID_TYPE(owner->fid) == OBJ_TYPE_CRITTER) {
|
||||
int j = 0;
|
||||
for (; j < i; j++) {
|
||||
AnimationDescription* ad = &(animationSequence->animations[j]);
|
||||
if (owner == ad->owner) {
|
||||
if (!animationKindIsCallback(ad->kind)) {
|
||||
if (ad->kind != ANIM_KIND_CALLBACK && ad->kind != ANIM_KIND_CALLBACK3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3375,7 +3345,7 @@ static unsigned int animationComputeTicksPerFrame(Object* object, int fid)
|
||||
}
|
||||
}
|
||||
|
||||
return std::max(1000 / fps, 1);
|
||||
return 1000 / fps;
|
||||
}
|
||||
|
||||
int animationRegisterSetLightIntensity(Object* owner, int lightDistance, int lightIntensity, int delay)
|
||||
|
||||
+54
-140
@@ -47,20 +47,6 @@ static int artReadFrameData(unsigned char* data, File* stream, int count, int* p
|
||||
static int artReadHeader(Art* art, File* stream);
|
||||
static int artGetDataSize(const Art* art);
|
||||
static int paddingForSize(int size);
|
||||
static char artGetCritterWeaponCode(int weaponType);
|
||||
|
||||
// A frame is laid out like [ArtFrame header][pixel bytes][padding].
|
||||
// These functions return a pointer to the pixel bytes, but must be given a pointer to a frame header,
|
||||
// not any ArtFrame pointer.
|
||||
static unsigned char* artFrameData(ArtFrame* frame)
|
||||
{
|
||||
return reinterpret_cast<unsigned char*>(frame) + sizeof(*frame);
|
||||
}
|
||||
|
||||
static const unsigned char* artFrameData(const ArtFrame* frame)
|
||||
{
|
||||
return reinterpret_cast<const unsigned char*>(frame) + sizeof(*frame);
|
||||
}
|
||||
|
||||
// 0x5002D8 str2
|
||||
static char gDefaultJumpsuitMaleFileName[] = "hmjmps";
|
||||
@@ -532,7 +518,7 @@ unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry**
|
||||
frm = artGetFrame(art, frame, direction);
|
||||
if (frm != nullptr) {
|
||||
|
||||
return artFrameData(frm);
|
||||
return (unsigned char*)frm + sizeof(*frm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,7 +570,7 @@ int _art_get_code(int animation, int weaponType, char* weaponCodePtr, char* anim
|
||||
return -1;
|
||||
}
|
||||
|
||||
*weaponCodePtr = artGetCritterWeaponCode(weaponType);
|
||||
*weaponCodePtr = 'd' + (weaponType - 1);
|
||||
return 0;
|
||||
} else if (animation == ANIM_PRONE_TO_STANDING) {
|
||||
*animationCodePtr = 'h';
|
||||
@@ -626,7 +612,7 @@ int _art_get_code(int animation, int weaponType, char* weaponCodePtr, char* anim
|
||||
*weaponCodePtr = 'a';
|
||||
*animationCodePtr = 'n';
|
||||
} else {
|
||||
*weaponCodePtr = artGetCritterWeaponCode(weaponType);
|
||||
*weaponCodePtr = 'd' + (weaponType - 1);
|
||||
*animationCodePtr = 'e';
|
||||
}
|
||||
return 0;
|
||||
@@ -634,7 +620,7 @@ int _art_get_code(int animation, int weaponType, char* weaponCodePtr, char* anim
|
||||
|
||||
*animationCodePtr = 'a' + animation;
|
||||
if (animation <= ANIM_WALK && weaponType > 0) {
|
||||
*weaponCodePtr = artGetCritterWeaponCode(weaponType);
|
||||
*weaponCodePtr = 'd' + (weaponType - 1);
|
||||
return 0;
|
||||
}
|
||||
*weaponCodePtr = 'a';
|
||||
@@ -642,24 +628,6 @@ int _art_get_code(int animation, int weaponType, char* weaponCodePtr, char* anim
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char artGetCritterWeaponCode(int weaponType)
|
||||
{
|
||||
switch (weaponType) {
|
||||
case WEAPON_ANIMATION_SFALL_S:
|
||||
return 's';
|
||||
case WEAPON_ANIMATION_SFALL_O:
|
||||
return 'o';
|
||||
case WEAPON_ANIMATION_SFALL_P:
|
||||
return 'p';
|
||||
case WEAPON_ANIMATION_SFALL_Q:
|
||||
return 'q';
|
||||
case WEAPON_ANIMATION_SFALL_T:
|
||||
return 't';
|
||||
default:
|
||||
return 'd' + (weaponType - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x419428
|
||||
char* artBuildFilePath(int fid)
|
||||
{
|
||||
@@ -839,9 +807,19 @@ int artGetSize(Art* art, int frame, int direction, int* widthPtr, int* heightPtr
|
||||
}
|
||||
|
||||
// 0x419820
|
||||
int artGetFrameOffsets(const Art* art, int frame, int direction, int* xPtr, int* yPtr)
|
||||
int artGetFrameOffsets(Art* art, int frame, int direction, int* xPtr, int* yPtr)
|
||||
{
|
||||
return artGetFrameData(art, frame, direction, nullptr, nullptr, xPtr, yPtr) != nullptr ? 0 : -1;
|
||||
ArtFrame* frm;
|
||||
|
||||
frm = artGetFrame(art, frame, direction);
|
||||
if (frm == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*xPtr = frm->x;
|
||||
*yPtr = frm->y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x41984C
|
||||
@@ -860,33 +838,14 @@ int artGetRotationOffsets(Art* art, int rotation, int* xPtr, int* yPtr)
|
||||
// 0x419870
|
||||
unsigned char* artGetFrameData(Art* art, int frame, int direction)
|
||||
{
|
||||
return artGetFrameData(art, frame, direction, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
ArtFrame* frm;
|
||||
|
||||
unsigned char* artGetFrameData(const Art* art, int frame, int direction, int* widthPtr, int* heightPtr, int* xOffsetPtr, int* yOffsetPtr)
|
||||
{
|
||||
ArtFrame* frm = artGetFrame(art, frame, direction);
|
||||
frm = artGetFrame(art, frame, direction);
|
||||
if (frm == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (widthPtr != nullptr) {
|
||||
*widthPtr = frm->width;
|
||||
}
|
||||
|
||||
if (heightPtr != nullptr) {
|
||||
*heightPtr = frm->height;
|
||||
}
|
||||
|
||||
if (xOffsetPtr != nullptr) {
|
||||
*xOffsetPtr = frm->x;
|
||||
}
|
||||
|
||||
if (yOffsetPtr != nullptr) {
|
||||
*yOffsetPtr = frm->y;
|
||||
}
|
||||
|
||||
return artFrameData(frm);
|
||||
return (unsigned char*)frm + sizeof(*frm);
|
||||
}
|
||||
|
||||
// 0x419880
|
||||
@@ -913,10 +872,11 @@ ArtFrame* artGetFrame(const Art* art, int frame, int rotation)
|
||||
|
||||
ConstBuffer2D artGetFrameBuffer(const Art* art, int frame, int direction)
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
unsigned char* data = artGetFrameData(art, frame, direction, &width, &height, nullptr, nullptr);
|
||||
return { data, width, height };
|
||||
ArtFrame* frm = artGetFrame(art, frame, direction);
|
||||
if (frm == nullptr) {
|
||||
return { nullptr, 0, 0 };
|
||||
}
|
||||
return { reinterpret_cast<unsigned char*>(frm) + sizeof(*frm), frm->width, frm->height };
|
||||
}
|
||||
|
||||
// 0x4198C8
|
||||
@@ -976,11 +936,9 @@ int artAliasFid(int fid)
|
||||
int anim = FID_ANIM_TYPE(fid);
|
||||
if (type == OBJ_TYPE_CRITTER) {
|
||||
if (anim == ANIM_ELECTRIFY
|
||||
|| anim == ANIM_CHARRED_BODY
|
||||
|| anim == ANIM_BURNED_TO_NOTHING
|
||||
|| anim == ANIM_ELECTRIFIED_TO_NOTHING
|
||||
|| anim == ANIM_ELECTRIFY_SF
|
||||
|| anim == ANIM_CHARRED_BODY_SF
|
||||
|| anim == ANIM_BURNED_TO_NOTHING_SF
|
||||
|| anim == ANIM_ELECTRIFIED_TO_NOTHING_SF
|
||||
|| anim == ANIM_FIRE_DANCE
|
||||
@@ -1031,21 +989,10 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
|
||||
Art art;
|
||||
if (artReadHeader(&art, stream) == 0) {
|
||||
*sizePtr = artGetDataSize(&art);
|
||||
if (*sizePtr < 0) {
|
||||
debugPrint("ART ERROR: fid %d path %s returned negative data size %d\n", fid, artFilePath, *sizePtr);
|
||||
} else if (*sizePtr > 0x10000) {
|
||||
debugPrint("ART INFO: fid %d path %s has large data size %d\n", fid, artFilePath, *sizePtr);
|
||||
}
|
||||
result = 0;
|
||||
} else {
|
||||
debugPrint("ART ERROR: artReadHeader failed for fid %d path %s\n", fid, artFilePath);
|
||||
}
|
||||
fileClose(stream);
|
||||
} else {
|
||||
debugPrint("ART ERROR: could not open file for fid %d path %s\n", fid, artFilePath);
|
||||
}
|
||||
} else {
|
||||
debugPrint("ART ERROR: could not build path for fid %d\n", fid);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1063,32 +1010,19 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
|
||||
if (artGetLocalizedPath(artFileName, &localizedPath)) {
|
||||
if (artRead(localizedPath, data) == 0) {
|
||||
loaded = true;
|
||||
} else {
|
||||
debugPrint("ART INFO: artRead localized failed for fid %d path %s\n", fid, localizedPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
if (artRead(artFileName, data) == 0) {
|
||||
loaded = true;
|
||||
} else {
|
||||
debugPrint("ART INFO: artRead fallback failed for fid %d path %s\n", fid, artFileName);
|
||||
}
|
||||
}
|
||||
|
||||
if (loaded) {
|
||||
*sizePtr = artGetDataSize((Art*)data);
|
||||
if (*sizePtr < 0) {
|
||||
debugPrint("ART ERROR: fid %d path %s read data returned negative size %d\n", fid, artFileName, *sizePtr);
|
||||
} else if (*sizePtr > 0x10000) {
|
||||
debugPrint("ART INFO: fid %d path %s read large data size %d\n", fid, artFileName, *sizePtr);
|
||||
}
|
||||
result = 0;
|
||||
} else {
|
||||
debugPrint("ART ERROR: failed to load ART data for fid %d path %s\n", fid, artFileName);
|
||||
}
|
||||
} else {
|
||||
debugPrint("ART ERROR: could not build path for fid %d\n", fid);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1173,20 +1107,6 @@ static int artReadHeader(Art* art, File* stream)
|
||||
art->dataSize = fileGetSize(stream);
|
||||
}
|
||||
|
||||
if (art->frameCount < 0) {
|
||||
debugPrint("ART WARNING: negative frameCount %d in header\n", art->frameCount);
|
||||
}
|
||||
|
||||
if (art->dataSize < 0) {
|
||||
debugPrint("ART WARNING: negative dataSize %d in header\n", art->dataSize);
|
||||
}
|
||||
|
||||
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
|
||||
if (art->dataOffsets[rotation] < 0) {
|
||||
debugPrint("ART WARNING: negative dataOffset[%d] %d in header\n", rotation, art->dataOffsets[rotation]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1285,18 +1205,12 @@ static Art* artAllocateSingleFrame(int width, int height, unsigned char** frameD
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pixelCount > static_cast<size_t>(INT_MAX) - sizeof(ArtFrame)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Art header = {};
|
||||
header.version = 4;
|
||||
header.framesPerSecond = 10;
|
||||
header.actionFrame = 0;
|
||||
header.frameCount = 1;
|
||||
// FRM dataSize excludes in-memory alignment padding; artGetDataSize adds
|
||||
// the padding needed by artGetFrame's adjusted frame offsets.
|
||||
header.dataSize = static_cast<int>(sizeof(ArtFrame) + pixelCount);
|
||||
header.dataSize = sizeof(ArtFrame) + static_cast<int>(pixelCount);
|
||||
|
||||
int currentPadding = paddingForSize(sizeof(Art));
|
||||
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
|
||||
@@ -1321,7 +1235,7 @@ static Art* artAllocateSingleFrame(int width, int height, unsigned char** frameD
|
||||
frame->x = 0;
|
||||
frame->y = 0;
|
||||
|
||||
*frameDataPtr = artFrameData(frame);
|
||||
*frameDataPtr = reinterpret_cast<unsigned char*>(frame) + sizeof(ArtFrame);
|
||||
return art;
|
||||
}
|
||||
|
||||
@@ -1477,13 +1391,6 @@ int artRead(const char* path, unsigned char* data)
|
||||
return -3;
|
||||
}
|
||||
|
||||
int totalAllocSize = artGetDataSize(art);
|
||||
if (totalAllocSize <= 0) {
|
||||
debugPrint("ART ERROR: artRead computed invalid totalAllocSize %d for %s\n", totalAllocSize, path);
|
||||
fileClose(stream);
|
||||
return -5;
|
||||
}
|
||||
|
||||
int currentPadding = paddingForSize(sizeof(Art));
|
||||
int previousPadding = 0;
|
||||
|
||||
@@ -1493,14 +1400,7 @@ int artRead(const char* path, unsigned char* data)
|
||||
if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) {
|
||||
art->padding[index] += previousPadding;
|
||||
currentPadding += previousPadding;
|
||||
long long frameDataOffset = static_cast<long long>(sizeof(Art)) + art->dataOffsets[index] + art->padding[index];
|
||||
if (frameDataOffset < 0 || frameDataOffset >= totalAllocSize) {
|
||||
debugPrint("ART ERROR: invalid frame data destination for %s at rotation %d: offset=%lld base=%p size=%d\n", path, index, frameDataOffset, data, totalAllocSize);
|
||||
fileClose(stream);
|
||||
return -5;
|
||||
}
|
||||
unsigned char* frameDataPtr = data + frameDataOffset;
|
||||
if (artReadFrameData(frameDataPtr, stream, art->frameCount, &previousPadding) != 0) {
|
||||
if (artReadFrameData(data + sizeof(Art) + art->dataOffsets[index] + art->padding[index], stream, art->frameCount, &previousPadding) != 0) {
|
||||
fileClose(stream);
|
||||
return -5;
|
||||
}
|
||||
@@ -1610,6 +1510,8 @@ public:
|
||||
|
||||
const Art* art() const { return _art.get(); }
|
||||
|
||||
ArtFrame* frame(int frame, int direction) const;
|
||||
|
||||
unsigned int mru = 0;
|
||||
|
||||
private:
|
||||
@@ -1621,6 +1523,11 @@ NamedCacheEntry::NamedCacheEntry(ArtPtr&& art)
|
||||
{
|
||||
}
|
||||
|
||||
ArtFrame* NamedCacheEntry::frame(int frame, int direction) const
|
||||
{
|
||||
return artGetFrame(_art.get(), frame, direction);
|
||||
}
|
||||
|
||||
std::shared_ptr<NamedCacheEntry> artLockNamedFrameData(const char* path)
|
||||
{
|
||||
auto it = gNamedArtCache.find(path);
|
||||
@@ -1701,6 +1608,7 @@ ObjectType FrmId::objectType() const
|
||||
FrmImage::FrmImage()
|
||||
{
|
||||
_key = nullptr;
|
||||
_frame = nullptr;
|
||||
_data = nullptr;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
@@ -1716,6 +1624,7 @@ FrmImage::~FrmImage()
|
||||
FrmImage::FrmImage(FrmImage&& other) noexcept
|
||||
: _namedKey(std::move(other._namedKey))
|
||||
, _key(other._key)
|
||||
, _frame(other._frame)
|
||||
, _data(other._data)
|
||||
, _width(other._width)
|
||||
, _height(other._height)
|
||||
@@ -1731,6 +1640,7 @@ FrmImage& FrmImage::operator=(FrmImage&& other) noexcept
|
||||
unlock();
|
||||
_namedKey = std::move(other._namedKey);
|
||||
_key = other._key;
|
||||
_frame = other._frame;
|
||||
_data = other._data;
|
||||
_width = other._width;
|
||||
_height = other._height;
|
||||
@@ -1776,11 +1686,18 @@ bool FrmImage::lock(unsigned int fid, int frame, int direction)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setFrame(art, frame, direction)) {
|
||||
_frame = artGetFrame(art, frame, direction);
|
||||
if (_frame == nullptr) {
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
_data = reinterpret_cast<unsigned char*>(_frame) + sizeof(ArtFrame);
|
||||
_width = _frame->width;
|
||||
_height = _frame->height;
|
||||
_xOffset = _frame->x;
|
||||
_yOffset = _frame->y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1798,11 +1715,18 @@ bool FrmImage::lock(const char* frmPath, int frame, int direction)
|
||||
_namedKey = artLockNamedFrameData(frmPath);
|
||||
if (!_namedKey) return false;
|
||||
|
||||
if (!setFrame(_namedKey->art(), frame, direction)) {
|
||||
_frame = _namedKey->frame(frame, direction);
|
||||
if (_frame == nullptr) {
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
_data = reinterpret_cast<unsigned char*>(_frame) + sizeof(ArtFrame);
|
||||
_width = _frame->width;
|
||||
_height = _frame->height;
|
||||
_xOffset = _frame->x;
|
||||
_yOffset = _frame->y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1832,6 +1756,7 @@ void FrmImage::resetInternal()
|
||||
{
|
||||
_namedKey = nullptr;
|
||||
_key = nullptr;
|
||||
_frame = nullptr;
|
||||
_data = nullptr;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
@@ -1839,15 +1764,4 @@ void FrmImage::resetInternal()
|
||||
_yOffset = 0;
|
||||
}
|
||||
|
||||
bool FrmImage::setFrame(const Art* art, int frame, int direction)
|
||||
{
|
||||
unsigned char* data = artGetFrameData(art, frame, direction, &_width, &_height, &_xOffset, &_yOffset);
|
||||
if (data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_data = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -100,11 +100,6 @@ typedef enum WeaponAnimation {
|
||||
WEAPON_ANIMATION_LASER_RIFLE, // k
|
||||
WEAPON_ANIMATION_MINIGUN, // l
|
||||
WEAPON_ANIMATION_LAUNCHER, // m
|
||||
WEAPON_ANIMATION_SFALL_S, // s
|
||||
WEAPON_ANIMATION_SFALL_O, // o
|
||||
WEAPON_ANIMATION_SFALL_P, // p
|
||||
WEAPON_ANIMATION_SFALL_Q, // q
|
||||
WEAPON_ANIMATION_SFALL_T, // t
|
||||
WEAPON_ANIMATION_COUNT,
|
||||
} WeaponAnimation;
|
||||
|
||||
@@ -144,10 +139,9 @@ int artGetFrameCount(Art* art);
|
||||
int artGetWidth(Art* art, int frame, int direction);
|
||||
int artGetHeight(Art* art, int frame, int direction);
|
||||
int artGetSize(Art* art, int frame, int direction, int* out_width, int* out_height);
|
||||
int artGetFrameOffsets(const Art* art, int frame, int direction, int* xPtr, int* yPtr);
|
||||
int artGetFrameOffsets(Art* art, int frame, int direction, int* xPtr, int* yPtr);
|
||||
int artGetRotationOffsets(Art* art, int rotation, int* out_offset_x, int* out_offset_y);
|
||||
unsigned char* artGetFrameData(Art* art, int frame, int direction);
|
||||
unsigned char* artGetFrameData(const Art* art, int frame, int direction, int* widthPtr, int* heightPtr, int* xOffsetPtr, int* yOffsetPtr);
|
||||
ArtFrame* artGetFrame(const Art* art, int frame, int direction);
|
||||
ConstBuffer2D artGetFrameBuffer(const Art* art, int frame, int direction);
|
||||
bool artExists(int fid);
|
||||
@@ -226,10 +220,10 @@ public:
|
||||
|
||||
private:
|
||||
void resetInternal();
|
||||
bool setFrame(const Art* art, int frame, int direction);
|
||||
|
||||
std::shared_ptr<NamedCacheEntry> _namedKey;
|
||||
CacheEntry* _key = nullptr;
|
||||
ArtFrame* _frame = nullptr;
|
||||
unsigned char* _data = nullptr;
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
|
||||
+3
-18
@@ -48,11 +48,6 @@ static int _copy_file_data(File* stream1, File* stream2, int length);
|
||||
|
||||
static int gAutomapWindow = -1;
|
||||
|
||||
static bool automapEntryIsValid(int map, int elevation)
|
||||
{
|
||||
return map >= 0 && map < AUTOMAP_MAP_COUNT && elevationIsValid(elevation);
|
||||
}
|
||||
|
||||
typedef enum AutomapFrm {
|
||||
AUTOMAP_FRM_BACKGROUND,
|
||||
AUTOMAP_FRM_BUTTON_UP,
|
||||
@@ -298,10 +293,6 @@ int automapSave(File* stream)
|
||||
// 0x41B8B4 automapDisplayMap
|
||||
int _automapDisplayMap(int map)
|
||||
{
|
||||
if (map < 0 || map >= AUTOMAP_MAP_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _displayMapList[map];
|
||||
}
|
||||
|
||||
@@ -552,13 +543,13 @@ static void automapRenderInMapWindow(int window, int elevation, unsigned char* b
|
||||
continue;
|
||||
}
|
||||
|
||||
if (object->pid == PROTO_ID_EXIT_GRID_MAP_MARKER) {
|
||||
if (object->pid == PROTO_ID_0x2000031) {
|
||||
objectColor = _colorTable[32328];
|
||||
} else if (objectType == OBJ_TYPE_WALL) {
|
||||
objectColor = _colorTable[992];
|
||||
} else if (objectType == OBJ_TYPE_SCENERY
|
||||
&& (flags & AUTOMAP_WTH_HIGH_DETAILS) != 0
|
||||
&& object->pid != PROTO_ID_BLOCK_HEX_AUTO_INVISO) {
|
||||
&& object->pid != PROTO_ID_0x2000158) {
|
||||
objectColor = _colorTable[480];
|
||||
} else if (object == gDude) {
|
||||
objectColor = _colorTable[31744];
|
||||
@@ -705,9 +696,6 @@ int automapSaveCurrent()
|
||||
{
|
||||
int map = mapGetCurrentMap();
|
||||
int elevation = gElevation;
|
||||
if (!automapEntryIsValid(map, elevation)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int entryOffset = gAutomapHeader.offsets[map][elevation];
|
||||
if (entryOffset < 0) {
|
||||
@@ -953,9 +941,6 @@ err:
|
||||
static int automapLoadEntry(int map, int elevation)
|
||||
{
|
||||
gAutomapEntry.compressedData = nullptr;
|
||||
if (!automapEntryIsValid(map, elevation)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
@@ -1105,7 +1090,7 @@ static void _decode_map_data(int elevation)
|
||||
int contentType;
|
||||
|
||||
int objectType = FID_TYPE(object->fid);
|
||||
if (objectType == OBJ_TYPE_SCENERY && object->pid != PROTO_ID_BLOCK_HEX_AUTO_INVISO) {
|
||||
if (objectType == OBJ_TYPE_SCENERY && object->pid != PROTO_ID_0x2000158) {
|
||||
contentType = 2;
|
||||
} else if (objectType == OBJ_TYPE_WALL) {
|
||||
contentType = 1;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user