Jump to content


Area structure function


  • Please log in to reply
21 replies to this topic

#1 -Guest-

-Guest-
  • Guest

Posted 24 September 2010 - 05:25 AM

here's an attempt at fj_are_structure (named shortened because it now deletes as well as adds).

set fj_delete_mode to the count-from-zero index of the structure to delete, and fj_structure type as usual. Single structures (bitmask, songlist, rest interrupts) still need fj_delete_mode = 1.

the aforementioned bitmask, songlist, and rest interrupt table can now be deleted or added, and if you add another the function will simply replace the preexisting. don't use.

embedded creatures are now supported; if fj_loaded & 1 == 0 when adding a new actor, the function will try fj_cre_embedded = ~path/to/file.cre~. failing that, it'll try fj_cre_resref. failing that, it'll mark the the actor not embedded. don't use this either.

embedded projectiles are now supported. set fj_embedded_eff0, fj_embedded_eff1... = ~path/to/v2.eff~ or eff resref if adding new ones. none of this will work correctly on the mac port. don't use it.

the unzeroing header offsets now requires a fj_unzero_header_off = 1 toggle. shouldn't matter.

fj_debug = 1 dumps even more useless "info" into your debug file. maybe it'll help someone debug an area or this function sometime. probably not.

you might need to set the linebreaks back to *nix.

no warranty is expressed or implied in this post.

[codebox]
DEFINE_PATCH_FUNCTION fj_are_structure

// let's set up our variables! i++++ 4eva

INT_VAR

// these are internal variables, not to be used as input
is_bg2 = ENGINE_IS ~soa tob~
is_pst = ENGINE_IS pst
is_id2 = ENGINE_IS iwd2
fj_position = 0x11c
fj_itm_idx = 0
fj_vertex_idx = 0
off = 0
num = 0
off1 = 0
num1 = 0
key = 0
value = 0
key1 = 0
value1 = 0
fj_return_offset = 0
fj_deleted = 0

// instead of adding fj_structure_type, delete if num == fj_delete_mode
fj_delete_mode = ` 0

// offsets to various structures unpointed at zero
fj_unzero_header_off = 0

// spams your console with psuedoinformative nonsense
fj_debug = 0

// actors
fj_loc_x = 0
fj_loc_y = 0
fj_dest_x = 0
fj_dest_y = 0
fj_loading = 1
fj_spawned = 0
fj_animation = 0
fj_orientation = 0
fj_expiry = ` 0
fj_wander_dist_actor = 0
fj_mvmt_dist_actor = 0
fj_schedule = ` 0
fj_num_talked = 0

// regions
fj_type = 0
fj_box_left = 0
fj_box_top = 0
fj_box_right = 0
fj_box_bottom = 0
fj_cursor_idx = 0
fj_flags = 0
fj_info_point_strref = ` 0
fj_trap_detect = 0
fj_trap_remove = 0
fj_trap_active = 0
fj_trap_status = 0
fj_alt_x = 0
fj_alt_y = 0

// spawns
fj_spawn_num = 0
fj_difficulty = 0
fj_delay = 10
fj_method = 0
fj_duration = 1000
fj_wander_dist_spawn = 1000
fj_mvmt_dist_spawn = 1000
fj_max_num = 0
fj_enable = 1
fj_day_prob = 100
fj_night_prob = 100

// containers
fj_con_itm_idx = ` 0
fj_lock_diff = 100
fj_trap_remove_diff = 100
fj_trap_loc_x = 0
fj_trap_loc_y = 0
fj_lockpick_strref = ` 0

// items
fj_itm_expiry = 0
fj_charge0 = 0
fj_charge1 = 0
fj_charge2 = 0

// ambients
fj_radius = 500
fj_loc_z = 0
fj_volume = 80
fj_sound_num = 0
fj_delay = 0
fj_variation = 0

// variables
fj_variable_value = 0

// doors
fj_open_box_left = 0
fj_open_box_top = 0
fj_open_box_right = 0
fj_open_box_bottom = 0
fj_closed_box_left = 0
fj_closed_box_top = 0
fj_closed_box_right = 0
fj_closed_box_bottom = 0
fj_detect_diff = 0
fj_locked_diff = 0
fj_open_loc_x = 0
fj_open_loc_y = 0
fj_closed_loc_x = 0
fj_closed_loc_y = 0
fj_dlg_strref = ` 0

// animations
fj_bam_seq = 0
fj_bam_frame = 0
fj_transparent = 0
fj_init_frame = 0
fj_loop_chance = 0
fj_skip_cycles = 0

// songs
fj_song_day = 0
fj_song_night = 0
fj_song_victory = 0
fj_song_battle = 0
fj_song_defeat = 0
fj_song_day_vol = 100
fj_song_night_vol = 100
fj_song_reverb = 0

// rest interrupts
fj_cre_strref0 = ` 0
fj_cre_strref1 = ` 0
fj_cre_strref2 = ` 0
fj_cre_strref3 = ` 0
fj_cre_strref4 = ` 0
fj_cre_strref5 = ` 0
fj_cre_strref6 = ` 0
fj_cre_strref7 = ` 0
fj_cre_strref8 = ` 0
fj_cre_strref9 = ` 0

// map notes
fj_note_strref = ` 0
fj_strref_loc = 1
fj_color = 0
fj_note_id = 0

// embedded projectiles
fj_missile_num = ` 0
fj_frequency = 0
fj_target = 0

STR_VAR

// variables
fj_structure_type = ~~
fj_name = ~~

// actors
fj_dlg_resref = ~~
fj_bcs_override = ~~
fj_bcs_general = ~~
fj_bcs_class = ~~
fj_bcs_race = ~~
fj_bcs_default = ~~
fj_bcs_specific = ~~
fj_cre_resref = ~~
fj_cre_embedded = ~~

// regions
fj_destination_area = ~~
fj_destination_name = ~~
fj_key_resref = ~~
fj_reg_script = ~~

// spawns
fj_cre_resref0 = ~~
fj_cre_resref1 = ~~
fj_cre_resref2 = ~~
fj_cre_resref3 = ~~
fj_cre_resref4 = ~~
fj_cre_resref5 = ~~
fj_cre_resref6 = ~~
fj_cre_resref7 = ~~
fj_cre_resref8 = ~~
fj_cre_resref9 = ~~

// containers
fj_trap_script = ~~

// ambients
fj_wav_resref0 = ~~
fj_wav_resref1 = ~~
fj_wav_resref2 = ~~
fj_wav_resref3 = ~~
fj_wav_resref4 = ~~
fj_wav_resref5 = ~~
fj_wav_resref6 = ~~
fj_wav_resref7 = ~~
fj_wav_resref8 = ~~
fj_wav_resref9 = ~~

// doors
fj_door_wed_id = ~~
fj_door_open_wav = ~~
fj_door_close_wav = ~~
fj_door_script = ~~
fj_travel_trigger = ~~

// animations
fj_bam_resref = ~~
fj_bmp_resref = ~~

//bitmask
fj_bitmask = ~~

// songs
fj_song_day0 = ~~
fj_song_day1 = ~~
fj_song_night0 = ~~
fj_song_night1 = ~~

// map notes
fj_note_text = ~~ // only for PST

RET
fj_return_offset

BEGIN

// we must set $num(0) == num_0 when using function input
// otherwise WeiDU won't recognize that they're synonymous
PATCH_FOR_EACH value IN
vertex door_open_vert door_closed_vert cell_open_vert cell_closed_vert
BEGIN
FOR( num = 0 ; VARIABLE_IS_SET EVAL ~fj_%value%_%num%~ ; ++num )BEGIN
SET $EVAL ~fj_%value%~(~%num%~) = EVAL ~fj_%value%_%num%~
END
END
FOR( num = 0 ; VARIABLE_IS_SET EVAL ~fj_embedded_eff_%num%~ ; ++num )BEGIN
TEXT_SPRINT $fj_embedded_eff(~%num%~) EVAL ~%fj_embedded_eff_%num%%~
END

// php over this array rather than an explicit list to reduce code length and typos
CLEAR_ARRAY struct
TEXT_SPRINT $struct(0x54 0x58 0x02 0x110) actor
TEXT_SPRINT $struct(0x5c 0x5a 0x02 0x0c4) region
TEXT_SPRINT $struct(0x60 0x64 0x04 0x0c8) spawn
TEXT_SPRINT $struct(0x68 0x6c 0x04 0x068) entrance
TEXT_SPRINT $struct(0x70 0x74 0x02 0x0c0) container
TEXT_SPRINT $struct(0x78 0x76 0x02 0x014) itm
TEXT_SPRINT $struct(0x84 0x82 0x02 0x0d4) ambient
TEXT_SPRINT $struct(0x88 0x8c 0x02 0x054) variable
TEXT_SPRINT $struct(0xa8 0xa4 0x04 0x0c8) door
TEXT_SPRINT $struct(0xb8 0xb4 0x04 0x06c) tiled
TEXT_SPRINT $struct(0x7c 0x80 0x02 0x004) vertex
TEXT_SPRINT $struct(0xb0 0xac 0x04 0x04c) animation
TEXT_SPRINT $struct(0xa0 0x9c 0x04 0x000) bitmask
TEXT_SPRINT $struct(0xbc 0x00 0x00 0x090) songs
TEXT_SPRINT $struct(0xc0 0x00 0x00 0x0e4) interrupts
PATCH_IF is_pst BEGIN
TEXT_SPRINT $struct(0xc8 0xcc 0x04 0x214) note
END ELSE BEGIN
TEXT_SPRINT $struct(0xc4 0xc8 0x04 0x034) note
END
TEXT_SPRINT $struct(0xcc 0xd0 0x02 0x01c) projectile
PHP_EACH struct AS key => value BEGIN
PATCH_IF ~%value%~ STRING_EQUAL_CASE ~%fj_structure_type%~ BEGIN
SET fj_structure_type = key_0
END
END

// Icewind Dale II decided to be uselessly special
PATCH_IF is_id2 BEGIN
READ_ASCII 0x54 id2_header (0x10)
DELETE_BYTES 0x54 0x10
PATCH_FOR_EACH off IN
0x54 0x5c 0x60 0x68 0x70 0x78 0x7c 0x84 0x88 0xa0 0xa8 0xb0 0xb8 0xbc 0xc0
BEGIN
PATCH_IF LONG_AT off BEGIN
WRITE_LONG off THIS - 0x10
END
END
END

PATCH_IF fj_unzero_header_off BEGIN

// a small courtesy to fix areas missing obligatory structures
PATCH_IF fj_delete_mode == ` 0 BEGIN
PATCH_IF !LONG_AT 0xbc && fj_structure_type != 0xbc BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Offset to songlist points to 0! Adding empty songlist.~ END
WRITE_LONG 0xbc BUFFER_LENGTH
INSERT_BYTES BUFFER_LENGTH 0x90
END
PATCH_IF !LONG_AT 0xc0 && fj_structure_type != 0xc0 BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Offset to rest interrupts points to 0! Adding empty rest interrupt table.~ END
WRITE_LONG 0xc0 BUFFER_LENGTH
INSERT_BYTES BUFFER_LENGTH 0xe4
END
END

// offsets to valid structures should not point to 0
PATCH_FOR_EACH off IN
0x54 0x5c 0x60 0x68 0x70 0x78 0x7c 0x84 0x88 0xa0 0xa8 0xb0 0xb8
BEGIN
PATCH_IF !LONG_AT off BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Header offset %off% points to 0, setting to 0x11c.~ END
WRITE_LONG off 0x11c
END
END
PATCH_IF is_pst BEGIN
PATCH_IF !LONG_AT 0xc8 BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Header offset 0xc8 points to 0, setting to 0x11c.~ END
WRITE_LONG 0xc8 0x11c
END
END ELSE PATCH_IF is_bg2 BEGIN
PATCH_FOR_EACH off IN 0xc4 0xcc BEGIN
PATCH_IF !LONG_AT off BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Header offset %off% points to 0, setting to 0x11c.~ END
WRITE_LONG off 0x11c
END
END
END

END

// long block to read all the existing data
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Beginning unmarshalling.~ END
PHP_EACH struct AS key => value BEGIN
PATCH_IF( LONG_AT key_0 )BEGIN // skip it if the header offset points at 0
CLEAR_ARRAY EVAL ~%value%~
GET_OFFSET_ARRAY EVAL ~%value%~ key_0 0x04 key_1 key_2 0x00 0x00 key_3 // e.g. $region
PHP_EACH ~%value%~ AS num => off BEGIN
CLEAR_ARRAY array

PATCH_IF( key_0 == 0x54 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading actor %num%.~ END
PATCH_IF!( LONG_AT (off + 0x28) & 0x01 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~ Associating embedded creature.~ END
READ_ASCII LONG_AT (off + 0x88) $are_embedded_cre(~%num%~) (LONG_AT (off + 0x8c))
END
WRITE_LONG off + 0x88 0
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0x5c )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading region %num%.~ END
PATCH_IF( fj_debug && SHORT_AT ( off + 0x2a ) )BEGIN PATCH_PRINT ~ Associating vertices.~ END
GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x2a) 0x02 (off + 0x2c) 0x04 0x04
PHP_EACH array AS num1 => off1 BEGIN
READ_LONG off1 $EVAL ~are_region_%num%_vertex~(~%num1%~)
END
CLEAR_ARRAY array
WRITE_LONG off + 0x2c 0x00
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0x60 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading spawn %num%~ END
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0x68 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading entrance %num%~ END
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0x70 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading container %num%.~ END

// load container vertices
GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x54) 0x04 (off + 0x50) 0x04 0x04
PATCH_IF( fj_debug && LONG_AT 0x54 )BEGIN PATCH_PRINT ~ Associating vertices.~ END
PHP_EACH array AS num1 => off1 BEGIN
READ_LONG off1 $EVAL ~are_container_%num%_vertex~(~%num1%~)
END
CLEAR_ARRAY array

// load container items
GET_OFFSET_ARRAY array 0x78 0x04 (off + 0x44) 0x04 (off + 0x40) 0x04 0x14
PATCH_IF( fj_debug && LONG_AT 0x44 )BEGIN PATCH_PRINT ~ Associating items.~ END
PHP_EACH array AS num1 => off1 BEGIN
READ_ASCII off1 $EVAL ~are_container_%num%_itm~(~%num1%~) (0x14)
END
CLEAR_ARRAY array

// read container structure
WRITE_LONG off + 0x40 0x00 // wipe item index
WRITE_LONG off + 0x44 0x00 // wipe item count
WRITE_LONG off + 0x50 0x00 // wipe vertex index
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

// items are read off with their associated containers
PATCH_IF( key_0 == 0x78 )BEGIN
END ELSE

PATCH_IF( key_0 == 0x84 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading ambient %num%~ END
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0x88 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading variable %num%~ END
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0xa8 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading door %num% and associated vertices.~ END
// load door open vertices
GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x30) 0x02 (off + 0x2c) 0x04 0x04
PHP_EACH array AS num1 => off1 BEGIN
READ_LONG off1 $EVAL ~are_door_open_%num%_vertex~(~%num1%~)
END
CLEAR_ARRAY array
WRITE_LONG off + 0x2c 0x00
// load door closed vertices
GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x32) 0x02 (off + 0x34) 0x04 0x04
PHP_EACH array AS num1 => off1 BEGIN
READ_LONG off1 $EVAL ~are_door_closed_%num%_vertex~(~%num1%~)
END
CLEAR_ARRAY array
WRITE_LONG off + 0x34 0x00
// load cell open vertices
GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x4c) 0x02 (off + 0x48) 0x04 0x04
PHP_EACH array AS num1 => off1 BEGIN
READ_LONG off1 $EVAL ~are_cell_open_%num%_vertex~(~%num1%~)
END
CLEAR_ARRAY array
WRITE_LONG off + 0x48 0x00
// load cell closed vertices
GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x4e) 0x02 (off + 0x50) 0x04 0x04
PHP_EACH array AS num1 => off1 BEGIN
READ_LONG off1 $EVAL ~are_cell_closed_%num%_vertex~(~%num1%~)
END
CLEAR_ARRAY array
WRITE_LONG off + 0x50 0x00
// read the door structure itself
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0xb8 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading tiled object %num% (something is probably very wrong).~ END
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

// vertices, these are read off with their associated regions/containers/doors
PATCH_IF( key_0 == 0x78 )BEGIN
END ELSE

PATCH_IF( key_0 == 0xb0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading animation %num%.~ END
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF(
( key_0 == 0xc4 && is_bg2 ) ||
( key_0 == 0xc8 && is_pst)
)BEGIN //
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading map note %num%.~ END
READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
END ELSE

PATCH_IF( key_0 == 0xcc && is_bg2 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading projectile %num%.~ END
READ_ASCII off $are_projectile(~%num%~) (key_3)
PATCH_IF SHORT_AT (off + 0x0c) BEGIN
PATCH_IF( fj_debug )BEGIN PATCH_PRINT ~ Associating v2 embedded effects.~ END
READ_ASCII LONG_AT (off + 0x08) $are_embedded_eff(~%num%~) (SHORT_AT (off + 0x0c))
END
END

END // php_each ~%value%~
CLEAR_ARRAY array
CLEAR_ARRAY EVAL ~%value%~

// single structures

// bitmask
PATCH_IF( key_0 == 0xa0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading bitmask (should only exist in saved game areas).~ END
READ_ASCII LONG_AT 0xa0 $are_bitmask(0) (LONG_AT 0x9c)
END ELSE

// songs
PATCH_IF( key_0 == 0xbc )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading songs (obligatory structure).~ END
READ_ASCII LONG_AT 0xbc $are_songs(0) (0x90)
END ELSE

// rest interrupts
PATCH_IF( key_0 == 0xc0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading rest interrupt table (obligatory structure).~ END
READ_ASCII LONG_AT 0xc0 $are_interrupts(0) (0xe4)
END

END // skip if key_0 points to 0
END // end php_each $structure: all extended structures now loaded in buffer

PATCH_IF fj_debug BEGIN PATCH_PRINT ~Trimming %SOURCE_FILE% down to the header.~ END
DELETE_BYTES 0x11c BUFFER_LENGTH - 0x11c
PHP_EACH struct AS key => value BEGIN
PATCH_IF( key_2 == 0x02 )BEGIN
WRITE_SHORT key_1 0x00
END ELSE
PATCH_IF( key_2 == 0x04 )BEGIN
WRITE_LONG key_1 0x00
END
END

PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassembling %SOURCE_FILE%.~ END
PHP_EACH struct AS key => value BEGIN
WRITE_LONG key_0 fj_position
PHP_EACH ~are_%value%~ AS key1 => value1 BEGIN
PATCH_IF( key_0 != 0xa0 && key_0 != 0xbc && key_0 != 0xc0 )BEGIN
PATCH_IF( key1 != fj_delete_mode || fj_structure_type != key_0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
PATCH_IF( key_2 == 0x02 )BEGIN
WRITE_SHORT key_1 THIS + 0x01
END ELSE
PATCH_IF( key_2 == 0x04 )BEGIN
WRITE_LONG key_1 THIS + 0x01
END
INSERT_BYTES fj_position key_3
WRITE_ASCIIE fj_position ~%value1%~
SET fj_position += key_3
END
END ELSE
PATCH_IF( key_0 == 0xa0 && fj_structure_type != key_0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
TEXT_SPRINT value1 $are_bitmask(0)
WRITE_LONG 0x9c STRING_LENGTH EVAL ~%value1%~
INSERT_BYTES fj_position LONG_AT 0x9c
WRITE_ASCIIE fj_position ~%value1%~
SET fj_position += LONG_AT 0x9c
END ELSE
PATCH_IF( key_0 == 0xbc && fj_structure_type != key_0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
INSERT_BYTES fj_position key_3
WRITE_ASCIIE fj_position ~%value1%~
SET fj_position += key_3
END ELSE
PATCH_IF( key_0 == 0xc0 && fj_structure_type != key_0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
INSERT_BYTES fj_position key_3
WRITE_ASCIIE fj_position ~%value1%~
SET fj_position += key_3
END
END // PHP_EACH $EVAL ~are_%value%~

// add new structure
PATCH_IF( key_0 == fj_structure_type && fj_delete_mode == ` 0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding new %value% structure.~ END
PATCH_IF( key_2 == 0x02 )BEGIN
WRITE_SHORT key_1 THIS + 0x01
END ELSE PATCH_IF( key_2 == 0x04 )BEGIN
WRITE_LONG key_1 THIS + 0x01
END
SET fj_return_offset = fj_position
INSERT_BYTES fj_position key_3
SET fj_position += key_3

// actor
PATCH_IF( fj_structure_type == 0x54 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~
WRITE_SHORT fj_return_offset + 0x20 fj_loc_x
WRITE_SHORT fj_return_offset + 0x22 fj_loc_y
WRITE_SHORT fj_return_offset + 0x24 fj_dest_x
WRITE_SHORT fj_return_offset + 0x26 fj_dest_y
WRITE_LONG fj_return_offset + 0x28 fj_loading
WRITE_LONG fj_return_offset + 0x2c fj_spawned
WRITE_LONG fj_return_offset + 0x30 fj_animation
WRITE_LONG fj_return_offset + 0x34 fj_orientation
WRITE_LONG fj_return_offset + 0x38 fj_expiry
WRITE_SHORT fj_return_offset + 0x3c fj_wander_dist_actor
WRITE_SHORT fj_return_offset + 0x3e fj_mvmt_dist_actor
WRITE_LONG fj_return_offset + 0x40 fj_schedule
WRITE_LONG fj_return_offset + 0x44 fj_num_talked
WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_dlg_resref%~
WRITE_ASCIIE fj_return_offset + 0x50 ~%fj_bcs_override%~
WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_bcs_general%~
WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_bcs_class%~
WRITE_ASCIIE fj_return_offset + 0x68 ~%fj_bcs_race%~
WRITE_ASCIIE fj_return_offset + 0x70 ~%fj_bcs_default%~
WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_bcs_specific%~
WRITE_ASCIIE fj_return_offset + 0x80 ~%fj_cre_resref%~
END ELSE

// region
PATCH_IF( fj_structure_type == 0x5c )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_SHORT fj_return_offset + 0x20 fj_type
WRITE_SHORT fj_return_offset + 0x22 fj_box_left
WRITE_SHORT fj_return_offset + 0x24 fj_box_top
WRITE_SHORT fj_return_offset + 0x26 fj_box_right
WRITE_SHORT fj_return_offset + 0x28 fj_box_bottom
WRITE_LONG fj_return_offset + 0x34 fj_cursor_idx
WRITE_ASCIIE fj_return_offset + 0x38 ~%fj_destination_area%~ #8
WRITE_ASCIIE fj_return_offset + 0x40 ~%fj_destination_name%~ #32
WRITE_LONG fj_return_offset + 0x60 fj_flags
WRITE_LONG fj_return_offset + 0x64 fj_info_point_strref
WRITE_SHORT fj_return_offset + 0x68 fj_trap_detect
WRITE_SHORT fj_return_offset + 0x6a fj_trap_remove
WRITE_SHORT fj_return_offset + 0x6c fj_trap_active
WRITE_SHORT fj_return_offset + 0x6e fj_trap_status
WRITE_SHORT fj_return_offset + 0x70 fj_loc_x
WRITE_SHORT fj_return_offset + 0x72 fj_loc_y
WRITE_ASCIIE fj_return_offset + 0x74 ~%fj_key_resref%~ #8
WRITE_ASCIIE fj_return_offset + 0x7c ~%fj_reg_script%~ #8
WRITE_SHORT fj_return_offset + 0x84 fj_alt_x
WRITE_SHORT fj_return_offset + 0x86 fj_alt_y
PHP_EACH fj_vertex AS key1 => value1 BEGIN
WRITE_SHORT fj_return_offset + 0x2a THIS + 0x01
END
END ELSE

// spawn
PATCH_IF( fj_structure_type == 0x60 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_SHORT fj_return_offset + 0x20 fj_loc_x
WRITE_SHORT fj_return_offset + 0x22 fj_loc_y
WRITE_ASCIIE fj_return_offset + 0x24 ~%fj_cre_resref0%~ #8
WRITE_ASCIIE fj_return_offset + 0x2c ~%fj_cre_resref1%~ #8
WRITE_ASCIIE fj_return_offset + 0x34 ~%fj_cre_resref2%~ #8
WRITE_ASCIIE fj_return_offset + 0x3c ~%fj_cre_resref3%~ #8
WRITE_ASCIIE fj_return_offset + 0x44 ~%fj_cre_resref4%~ #8
WRITE_ASCIIE fj_return_offset + 0x4c ~%fj_cre_resref5%~ #8
WRITE_ASCIIE fj_return_offset + 0x54 ~%fj_cre_resref6%~ #8
WRITE_ASCIIE fj_return_offset + 0x5c ~%fj_cre_resref7%~ #8
WRITE_ASCIIE fj_return_offset + 0x64 ~%fj_cre_resref8%~ #8
WRITE_ASCIIE fj_return_offset + 0x6c ~%fj_cre_resref9%~ #8
WRITE_SHORT fj_return_offset + 0x74 fj_spawn_num
WRITE_SHORT fj_return_offset + 0x76 fj_difficulty
WRITE_SHORT fj_return_offset + 0x78 fj_delay
WRITE_SHORT fj_return_offset + 0x7a fj_method
WRITE_LONG fj_return_offset + 0x7c fj_duration
WRITE_SHORT fj_return_offset + 0x80 fj_wander_distance
WRITE_SHORT fj_return_offset + 0x82 fj_mvmt_distance
WRITE_SHORT fj_return_offset + 0x84 fj_max_num
WRITE_SHORT fj_return_offset + 0x86 fj_enable
WRITE_LONG fj_return_offset + 0x88 fj_schedule
WRITE_SHORT fj_return_offset + 0x8c fj_day_prob
WRITE_SHORT fj_return_offset + 0x8e fj_night_prob
END ELSE

// entrance
PATCH_IF( fj_structure_type == 0x68 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_SHORT fj_return_offset + 0x20 fj_loc_x
WRITE_SHORT fj_return_offset + 0x22 fj_loc_y
WRITE_SHORT fj_return_offset + 0x24 fj_orientation
END ELSE

// container
PATCH_IF( fj_structure_type == 0x70 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_SHORT fj_return_offset + 0x20 fj_loc_x
WRITE_SHORT fj_return_offset + 0x22 fj_loc_y
WRITE_SHORT fj_return_offset + 0x24 fj_type
WRITE_SHORT fj_return_offset + 0x26 fj_lock_diff
WRITE_LONG fj_return_offset + 0x28 fj_flags
WRITE_SHORT fj_return_offset + 0x2c fj_trap_detect
WRITE_SHORT fj_return_offset + 0x2e fj_trap_remove_diff
WRITE_SHORT fj_return_offset + 0x30 fj_trap_active
WRITE_SHORT fj_return_offset + 0x32 fj_trap_status
WRITE_SHORT fj_return_offset + 0x34 fj_trap_loc_x
WRITE_SHORT fj_return_offset + 0x36 fj_trap_loc_y
WRITE_SHORT fj_return_offset + 0x38 fj_box_left
WRITE_SHORT fj_return_offset + 0x3a fj_box_top
WRITE_SHORT fj_return_offset + 0x3c fj_box_right
WRITE_SHORT fj_return_offset + 0x3e fj_box_bottom
WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_trap_script%~ #8
WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_key_resref%~ #8
WRITE_LONG fj_return_offset + 0x84 fj_lockpick_strref
PHP_EACH fj_vertex AS key1 => value1 BEGIN
WRITE_LONG fj_return_offset + 0x54 THIS + 0x01
END
END ELSE

// itm
PATCH_IF( fj_structure_type == 0x78 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #8
WRITE_SHORT fj_return_offset + 0x08 fj_itm_expiry
WRITE_SHORT fj_return_offset + 0x0a fj_charge0
WRITE_SHORT fj_return_offset + 0x0c fj_charge1
WRITE_SHORT fj_return_offset + 0x0e fj_charge2
WRITE_LONG fj_return_offset + 0x10 fj_flags
END ELSE

// ambient
PATCH_IF( fj_structure_type == 0x84 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_SHORT fj_return_offset + 0x20 fj_loc_x
WRITE_SHORT fj_return_offset + 0x22 fj_loc_y
WRITE_SHORT fj_return_offset + 0x24 fj_radius
WRITE_SHORT fj_return_offset + 0x26 fj_loc_z
WRITE_SHORT fj_return_offset + 0x2e fj_volume
WRITE_ASCIIE fj_return_offset + 0x30 ~%fj_wav_resref0%~ #8
WRITE_ASCIIE fj_return_offset + 0x38 ~%fj_wav_resref1%~ #8
WRITE_ASCIIE fj_return_offset + 0x40 ~%fj_wav_resref2%~ #8
WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_wav_resref3%~ #8
WRITE_ASCIIE fj_return_offset + 0x50 ~%fj_wav_resref4%~ #8
WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_wav_resref5%~ #8
WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_wav_resref6%~ #8
WRITE_ASCIIE fj_return_offset + 0x68 ~%fj_wav_resref7%~ #8
WRITE_ASCIIE fj_return_offset + 0x70 ~%fj_wav_resref8%~ #8
WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_wav_resref9%~ #8
WRITE_SHORT fj_return_offset + 0x80 fj_sound_num
WRITE_LONG fj_return_offset + 0x84 fj_delay
WRITE_LONG fj_return_offset + 0x88 fj_variation
WRITE_LONG fj_return_offset + 0x8c fj_schedule
WRITE_LONG fj_return_offset + 0x90 fj_flags
END ELSE

// variable
PATCH_IF( fj_structure_type == 0x88 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_LONG fj_return_offset + 0x28 fj_variable_value
END ELSE

// door
PATCH_IF( fj_structure_type == 0xa8 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_ASCIIE fj_return_offset + 0x20 ~%fj_door_wed_id%~
WRITE_LONG fj_return_offset + 0x28 fj_flags
WRITE_SHORT fj_return_offset + 0x38 fj_open_box_left
WRITE_SHORT fj_return_offset + 0x3a fj_open_box_top
WRITE_SHORT fj_return_offset + 0x3c fj_open_box_right
WRITE_SHORT fj_return_offset + 0x3e fj_open_box_bottom
WRITE_SHORT fj_return_offset + 0x40 fj_closed_box_left
WRITE_SHORT fj_return_offset + 0x42 fj_closed_box_top
WRITE_SHORT fj_return_offset + 0x44 fj_closed_box_right
WRITE_SHORT fj_return_offset + 0x46 fj_closed_box_bottom
WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_door_open_wav%~
WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_door_close_wav%~
WRITE_LONG fj_return_offset + 0x68 fj_cursor_idx
WRITE_SHORT fj_return_offset + 0x6c fj_trap_detect
WRITE_SHORT fj_return_offset + 0x6e fj_trap_remove
WRITE_SHORT fj_return_offset + 0x70 fj_trap_active
WRITE_SHORT fj_return_offset + 0x72 fj_trap_status
WRITE_SHORT fj_return_offset + 0x74 fj_trap_loc_x
WRITE_SHORT fj_return_offset + 0x76 fj_trap_loc_y
WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_key_resref%~
WRITE_ASCIIE fj_return_offset + 0x80 ~%fj_door_script%~
WRITE_LONG fj_return_offset + 0x88 fj_detect_diff
WRITE_LONG fj_return_offset + 0x8c fj_locked_diff
WRITE_SHORT fj_return_offset + 0x90 fj_open_loc_x
WRITE_SHORT fj_return_offset + 0x92 fj_open_loc_y
WRITE_SHORT fj_return_offset + 0x94 fj_closed_loc_x
WRITE_SHORT fj_return_offset + 0x96 fj_closed_loc_y
WRITE_LONG fj_return_offset + 0x98 fj_lockpick_strref
WRITE_ASCIIE fj_return_offset + 0x9c ~%fj_travel_trigger%~ #24
WRITE_LONG fj_return_offset + 0xb4 fj_dlg_strref
WRITE_ASCIIE fj_return_offset + 0xb8 ~%fj_dlg_resref%~
PHP_EACH fj_door_open_vert AS key1 => value1 BEGIN
WRITE_SHORT fj_return_offset + 0x30 THIS + 0x01
END
PHP_EACH fj_door_closed_vert AS key1 => value1 BEGIN
WRITE_SHORT fj_return_offset + 0x32 THIS + 0x01
END
PHP_EACH fj_cell_open_vert AS key1 => value1 BEGIN
WRITE_SHORT fj_return_offset + 0x4c THIS + 0x01
END
PHP_EACH fj_cell_closed_vert AS key1 => value1 BEGIN
WRITE_SHORT fj_return_offset + 0x4e THIS + 0x01
END
END ELSE

// animation
PATCH_IF( fj_structure_type == 0xb0 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_SHORT fj_return_offset + 0x20 fj_loc_x
WRITE_SHORT fj_return_offset + 0x22 fj_loc_y
WRITE_LONG fj_return_offset + 0x24 fj_schedule
WRITE_ASCIIE fj_return_offset + 0x28 ~%fj_bam_resref%~ #8
WRITE_SHORT fj_return_offset + 0x30 fj_bam_seq
WRITE_SHORT fj_return_offset + 0x32 fj_bam_frame
WRITE_LONG fj_return_offset + 0x34 fj_flags
WRITE_SHORT fj_return_offset + 0x38 fj_loc_z
WRITE_SHORT fj_return_offset + 0x3a fj_transparent
WRITE_SHORT fj_return_offset + 0x3c fj_init_frame
WRITE_BYTE fj_return_offset + 0x3e fj_loop_chance
WRITE_BYTE fj_return_offset + 0x3f fj_skip_cycles
WRITE_ASCIIE fj_return_offset + 0x40 ~%fj_bmp_resref%~ #8
END ELSE

// bitmask
PATCH_IF( fj_structure_type == 0xa0 )BEGIN
PATCH_IF( FILE_EXISTS ~%fj_bitmask%~ )BEGIN
SET key1 = BUFFER_LENGTH
APPEND_FILE_EVALUATE ~%fj_bitmask%~
WRITE_LONG 0x9c BUFFER_LENGTH - key1
END ELSE BEGIN
WRITE_LONG 0x9c 0x00
END
SET fj_position += LONG_AT 0x9c
END ELSE

// songs
PATCH_IF( fj_structure_type == 0xbc )BEGIN
WRITE_LONG fj_return_offset + 0x00 fj_song_day
WRITE_LONG fj_return_offset + 0x04 fj_song_night
WRITE_LONG fj_return_offset + 0x08 fj_song_victory
WRITE_LONG fj_return_offset + 0x0c fj_song_battle
WRITE_LONG fj_return_offset + 0x10 fj_song_defeat
WRITE_LONG fj_return_offset + 0x14 0xffffffff
WRITE_LONG fj_return_offset + 0x18 0xffffffff
WRITE_LONG fj_return_offset + 0x1c 0xffffffff
WRITE_LONG fj_return_offset + 0x20 0xffffffff
WRITE_LONG fj_return_offset + 0x24 0xffffffff
WRITE_ASCIIE fj_return_offset + 0x28 ~%fj_song_day0%~ #8
WRITE_ASCIIE fj_return_offset + 0x30 ~%fj_song_day1%~ #8
WRITE_LONG fj_return_offset + 0x38 fj_song_day_vol
WRITE_ASCIIE fj_return_offset + 0x3c ~%fj_song_night0%~ #8
WRITE_ASCIIE fj_return_offset + 0x44 ~%fj_song_night1%~ #8
WRITE_LONG fj_return_offset + 0x4c fj_song_night_vol
WRITE_LONG fj_return_offset + 0x50 fj_song_reverb
END ELSE

// rest interrupts
PATCH_IF( fj_structure_type == 0xc0 )BEGIN
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
WRITE_LONG fj_return_offset + 0x20 fj_cre_strref0
WRITE_LONG fj_return_offset + 0x24 fj_cre_strref1
WRITE_LONG fj_return_offset + 0x28 fj_cre_strref2
WRITE_LONG fj_return_offset + 0x2c fj_cre_strref3
WRITE_LONG fj_return_offset + 0x30 fj_cre_strref4
WRITE_LONG fj_return_offset + 0x34 fj_cre_strref5
WRITE_LONG fj_return_offset + 0x38 fj_cre_strref6
WRITE_LONG fj_return_offset + 0x3c fj_cre_strref7
WRITE_LONG fj_return_offset + 0x40 fj_cre_strref8
WRITE_LONG fj_return_offset + 0x44 fj_cre_strref9
WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_cre_resref0%~ #8
WRITE_ASCIIE fj_return_offset + 0x50 ~%fj_cre_resref1%~ #8
WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_cre_resref2%~ #8
WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_cre_resref3%~ #8
WRITE_ASCIIE fj_return_offset + 0x68 ~%fj_cre_resref4%~ #8
WRITE_ASCIIE fj_return_offset + 0x70 ~%fj_cre_resref5%~ #8
WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_cre_resref6%~ #8
WRITE_ASCIIE fj_return_offset + 0x80 ~%fj_cre_resref7%~ #8
WRITE_ASCIIE fj_return_offset + 0x88 ~%fj_cre_resref8%~ #8
WRITE_ASCIIE fj_return_offset + 0x90 ~%fj_cre_resref9%~ #8
WRITE_SHORT fj_return_offset + 0x98 fj_spawn_num
WRITE_SHORT fj_return_offset + 0x9a fj_difficulty
WRITE_LONG fj_return_offset + 0x9c fj_duration
WRITE_SHORT fj_return_offset + 0xa0 fj_wander_distance
WRITE_SHORT fj_return_offset + 0xa2 fj_mvmt_distance
WRITE_SHORT fj_return_offset + 0xa4 fj_max_num
WRITE_SHORT fj_return_offset + 0xa6 fj_enable
WRITE_SHORT fj_return_offset + 0xa8 fj_day_prob
WRITE_SHORT fj_return_offset + 0xaa fj_night_prob
END ELSE

// map note (BGII)
PATCH_IF( fj_structure_type == 0xc4 )BEGIN
WRITE_SHORT fj_return_offset + 0x00 fj_loc_x
WRITE_SHORT fj_return_offset + 0x02 fj_loc_y
WRITE_LONG fj_return_offset + 0x04 fj_note_strref
WRITE_SHORT fj_return_offset + 0x08 fj_strref_loc
WRITE_SHORT fj_return_offset + 0x0a fj_color
WRITE_LONG fj_return_offset + 0x0c fj_note_id
END ELSE

// map note (PST)
PATCH_IF( fj_structure_type == 0xc8 )BEGIN
WRITE_LONG fj_return_offset + 0x000 fj_loc_x
WRITE_LONG fj_return_offset + 0x004 fj_loc_y
WRITE_ASCIIE fj_return_offset + 0x008 ~%fj_note_text%~
WRITE_LONG fj_return_offset + 0x1fc fj_color
END ELSE

// embedded projectile
PATCH_IF( fj_structure_type == 0xcc )BEGIN
PATCH_IF( fj_missile_num == ` 0 )BEGIN
SET fj_missile_num = IDS_OF_SYMBOL ( projectl ~%fj_name%~ )
PATCH_IF( fj_missile_num > ` 0 )BEGIN
SET fj_missile_num -= 0x01
END
END
WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #8
WRITE_SHORT fj_return_offset + 0x0e fj_missile_num
WRITE_SHORT fj_return_offset + 0x10 fj_frequency
WRITE_SHORT fj_return_offset + 0x12 fj_duration
WRITE_SHORT fj_return_offset + 0x14 fj_loc_x
WRITE_SHORT fj_return_offset + 0x16 fj_loc_y
WRITE_SHORT fj_return_offset + 0x18 fj_loc_z
WRITE_SHORT fj_return_offset + 0x1a fj_target
END

END

// add items and index to their containers
PATCH_IF( key_0 == 0x78 )BEGIN
PHP_EACH are_container AS num => structure BEGIN
PATCH_IF( fj_structure_type == 0x70 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
SET fj_deleted = 0x01
END ELSE BEGIN
WRITE_LONG LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x40 fj_itm_idx
PHP_EACH ~are_container_%num%_itm~ AS num1 => value1 BEGIN
PATCH_IF(
( fj_structure_type != 0x78 ) ||
( fj_delete_mode != num1 + LONG_AT ( LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x40 ) )
)BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating item %num1% to container %num%.~ END
WRITE_LONG LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x44 THIS + 0x01
INSERT_BYTES fj_position 0x14
WRITE_ASCIIE fj_position ~%value1%~
WRITE_SHORT 0x76 THIS + 0x01
SET ++fj_itm_idx
SET fj_position += key_3
END
END
END
PATCH_IF( fj_con_itm_idx == num && fj_structure_type == 0x78 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding new item to container %num%.~ END
WRITE_LONG LONG_AT 0x70 + 0xc0 * num + 0x44 THIS + 0x01
WRITE_SHORT 0x76 THIS + 0x01
INSERT_BYTES fj_position key_3
WRITE_ASCIIE fj_position + 0x00 ~%fj_name%~ #8
WRITE_SHORT fj_position + 0x08 fj_itm_expiry
WRITE_SHORT fj_position + 0x0a fj_charge0
WRITE_SHORT fj_position + 0x0c fj_charge1
WRITE_SHORT fj_position + 0x0e fj_charge2
WRITE_LONG fj_position + 0x10 fj_flags
SET ++fj_itm_idx
SET fj_position += key_3
END
END// php $are_container
SET fj_deleted = 0x00
PHP_EACH are_container AS key => value BEGIN
CLEAR_ARRAY EVAL ~are_container_%key%_itm~
END
END ELSE

// add vertices
PATCH_IF( key_0 == 0x7c )BEGIN

// vertices associated with regions
PHP_EACH are_region AS num => structure BEGIN
PATCH_IF( fj_structure_type == 0x5c && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
SET fj_deleted = 0x01
END ELSE BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating vertices to region %num%.~ END
WRITE_LONG LONG_AT 0x5c + 0xc4 * ( num - fj_deleted ) + 0x2c fj_vertex_idx
PHP_EACH ~are_region_%num%_vertex~ AS num1 => value1 BEGIN
INSERT_BYTES fj_position 0x04
WRITE_LONG fj_position value1
WRITE_SHORT 0x80 THIS + 0x01
SET ++fj_vertex_idx
SET fj_position += key_3
END
END
END
SET fj_deleted = 0x00
PATCH_IF( fj_structure_type == 0x5c && fj_delete_mode == ` 0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding vertices to new region.~ END
WRITE_LONG fj_return_offset + 0x2c fj_vertex_idx
PHP_EACH fj_vertex AS num => off BEGIN
INSERT_BYTES fj_position 0x04
WRITE_SHORT 0x80 THIS + 0x01
SET ++fj_vertex_idx
END
PHP_EACH fj_vertex AS num => off BEGIN
WRITE_LONG fj_position off
SET fj_position += 0x04
END
END
PHP_EACH are_region AS num => structure BEGIN
CLEAR_ARRAY EVAL ~are_region_%num%_vertex~
END

// vertices associated with containers
PHP_EACH are_container AS num => structure BEGIN
PATCH_IF( fj_structure_type == 0x70 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
SET fj_deleted = 0x01
END ELSE BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating vertices to container %num%.~ END
WRITE_LONG LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x50 fj_vertex_idx
PHP_EACH ~are_container_%num%_vertex~ AS num1 => value1 BEGIN
INSERT_BYTES fj_position 0x04
WRITE_LONG fj_position value1
WRITE_SHORT 0x80 THIS + 0x01
SET ++fj_vertex_idx
SET fj_position += 0x04
END
END
END
SET fj_deleted = 0x00
PATCH_IF fj_structure_type == 0x70 && fj_delete_mode == ` 0 BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding vertices to new container.~ END
WRITE_LONG fj_return_offset + 0x50 fj_vertex_idx
PHP_EACH fj_vertex AS num => off BEGIN
SET ++fj_vertex_idx
INSERT_BYTES fj_position 0x04
WRITE_SHORT 0x80 THIS + 0x01
END
PHP_EACH fj_vertex AS num => off BEGIN
WRITE_LONG fj_position off
SET fj_position += 0x04
END
END
PHP_EACH are_container AS num => structure BEGIN
CLEAR_ARRAY EVAL ~are_container_%num%_vertex~
END

// vertices associated with doors
PHP_EACH are_door AS num => structure BEGIN
PATCH_IF( fj_structure_type == 0xa8 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
SET fj_deleted = 0x01
END ELSE BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating vertices to door %num%.~ END
WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x2c fj_vertex_idx
PHP_EACH ~are_door_open_%num%_vertex~ AS num1 => value1 BEGIN
INSERT_BYTES fj_position 0x04
WRITE_LONG fj_position value1
SET ++fj_vertex_idx
SET fj_position += 0x04
WRITE_SHORT 0x80 THIS + 0x01
END
WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x34 fj_vertex_idx
PHP_EACH ~are_door_closed_%num%_vertex~ AS num1 => value1 BEGIN
INSERT_BYTES fj_position 0x04
WRITE_LONG fj_position value1
SET ++fj_vertex_idx
SET fj_position += 0x04
WRITE_SHORT 0x80 THIS + 0x01
END
WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x48 fj_vertex_idx
PHP_EACH ~are_cell_open_%num%_vertex~ AS num1 => value1 BEGIN
INSERT_BYTES fj_position 0x04
WRITE_LONG fj_position value1
SET ++fj_vertex_idx
SET fj_position += 0x04
WRITE_SHORT 0x80 THIS + 0x01
END
WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x50 fj_vertex_idx
PHP_EACH ~are_cell_closed_%num%_vertex~ AS num1 => value1 BEGIN
INSERT_BYTES fj_position 0x04
WRITE_LONG fj_position value1
SET ++fj_vertex_idx
SET fj_position += 0x04
WRITE_SHORT 0x80 THIS + 0x01
END
END
END
SET fj_deleted = 0x00
PATCH_FOR_EACH value1 IN
door_open door_closed cell_open cell_closed
BEGIN
CLEAR_ARRAY EVAL ~are_%value1%_%num%_vertex~
END
PATCH_IF( fj_structure_type == 0xa8 && fj_delete_mode == ` 0 )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding vertices to new door.~ END
WRITE_LONG fj_return_offset + 0x2c fj_vertex_idx
WRITE_LONG fj_return_offset + 0x34
LONG_AT (fj_return_offset + 0x2c) + SHORT_AT (fj_return_offset + 0x30)
WRITE_LONG fj_return_offset + 0x48
LONG_AT (fj_return_offset + 0x34) + SHORT_AT (fj_return_offset + 0x32)
WRITE_LONG fj_return_offset + 0x50
LONG_AT (fj_return_offset + 0x48) + SHORT_AT (fj_return_offset + 0x4c)
PATCH_FOR_EACH vertex_type IN
door_open door_closed cell_open cell_closed
BEGIN
PHP_EACH ~fj_%vertex_type%_vert~ AS num => off BEGIN
INSERT_BYTES fj_position 0x04
WRITE_SHORT 0x80 THIS + 0x01
END
PHP_EACH ~fj_%vertex_type%_vert~ AS num => off BEGIN
WRITE_LONG fj_position off
SET fj_position += 0x04
END
CLEAR_ARRAY EVAL ~fj_%vertex_type%_vert~
END
END

END // adding vertices

// reinsert embedded creatures
PATCH_IF( key_0 == 0x54 )BEGIN
PHP_EACH are_embedded_cre AS num => value1 BEGIN
PATCH_IF( fj_structure_type == 0x54 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
SET fj_deleted = 0x01
END ELSE BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating embedded creature to actor %num%.~ END
WRITE_LONG LONG_AT 0x54 + ( num - fj_deleted ) * 0x110 + 0x88 fj_position
INSERT_BYTES fj_position LONG_AT (LONG_AT 0x54 + ( num - fj_deleted ) * 0x110 + 0x8c)
WRITE_ASCIIE fj_position ~%value1%~
SET fj_position += LONG_AT (LONG_AT 0x54 + ( num - fj_deleted ) * 0x110 + 0x8c)
END
END
CLEAR_ARRAY are_embedded_cre
SET fj_deleted = 0x00
PATCH_IF( fj_structure_type == 0x54 && fj_delete_mode == ` 0 && !( fj_loading & 0x01 ) )BEGIN
PATCH_IF( FILE_EXISTS ~%fj_cre_embedded%~ )BEGIN
SET off = BUFFER_LENGTH // we do a stupid dance here to avoid INNER_ACTION
APPEND_FILE_EVALUATE ~%fj_cre_embedded%~
READ_ASCII off fj_cre_embedded (BUFFER_LENGTH - off)
DELETE_BYTES off STRING_LENGTH EVAL ~%fj_cre_embedded%~
END ELSE PATCH_IF( FILE_EXISTS_IN_GAME ~%fj_cre_resref%.cre~ ) BEGIN
INNER_PATCH_FILE ~%fj_cre_resref%.cre~ BEGIN
READ_ASCII 0x00 fj_cre_embedded (BUFFER_LENGTH)
END
END
PATCH_IF( ~%fj_cre_embedded%~ STR_CMP ~~ )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Embedding creature to new actor.~ END
WRITE_LONG fj_return_offset + 0x88 fj_position
WRITE_LONG fj_return_offset + 0x8c STRING_LENGTH EVAL ~%fj_cre_embedded%~
INSERT_BYTES fj_position LONG_AT (fj_return_offset + 0x8c)
WRITE_ASCIIE fj_position ~%fj_cre_embedded%~
SET fj_position += LONG_AT (fj_return_offset + 0x8c)
END ELSE BEGIN
WRITE_LONG fj_return_offset + 0x28 THIS | 0x01 // if we didn't find a .cre, mark it unembedded
END
END
END ELSE

// reinsert embedded projectile effects
PATCH_IF( key_0 == 0xcc )BEGIN
PHP_EACH are_embedded_eff AS num => value1 BEGIN
PATCH_IF( fj_structure_type == 0xcc && fj_delete_mode == num && fj_deleted == 0x00 )BEGIN
SET fj_deleted = 0x01
END ELSE BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating embedded effects to projectile %num%.~ END
WRITE_LONG LONG_AT 0xcc + ( num - fj_deleted ) * 0x1c + 0x08 fj_position
INSERT_BYTES fj_position SHORT_AT (LONG_AT 0xcc + ( num - fj_deleted ) * 0x1c + 0x0c)
WRITE_ASCIIE fj_position ~%value1%~
SET fj_position += SHORT_AT (LONG_AT 0xcc + ( num - fj_deleted ) * 0x1c + 0x0c)
END
END
SET fj_deleted = 0x00
CLEAR_ARRAY are_embedded_eff
PATCH_IF( fj_structure_type == 0xcc && fj_delete_mode == ` 0 )BEGIN
WRITE_LONG fj_return_offset + 0x08 fj_position
PHP_EACH fj_embedded_eff AS num => value1 BEGIN
PATCH_IF( FILE_EXISTS ~%value1%~ )BEGIN
SET off = BUFFER_LENGTH
APPEND_FILE_EVALUATE ~%value1%~
READ_ASCII off + 0x08 value1 (0x108)
DELETE_BYTES off 0x110
END ELSE PATCH_IF( FILE_EXISTS_IN_GAME ~%value1%.eff~ )BEGIN
INNER_PATCH_FILE ~%value1%.eff~ BEGIN
READ_ASCII 0x08 value1 (0x108)
END
END ELSE BEGIN
TEXT_SPRINT value1 ~~
END
PATCH_IF( ~%value1%~ STR_CMP ~~ )BEGIN
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding effect %num% to new embedded projectile.~ END
WRITE_SHORT fj_return_offset + 0x0c THIS + 0x108
INSERT_BYTES fj_position 0x108
WRITE_ASCIIE fj_position ~%value1%~
SET fj_position += 0x108
END
END
END
CLEAR_ARRAY fj_embedded_eff
END

END // php $struct: everything added

// restoring Icewind Dale II's special snowflakiness
PATCH_IF is_id2 BEGIN
PATCH_FOR_EACH off IN
0x54 0x5c 0x60 0x68 0x70 0x78 0x7c 0x84
0x88 0xa0 0xa8 0xb0 0xb8 0xbc 0xc0
BEGIN
PATCH_IF LONG_AT off BEGIN
WRITE_LONG off THIS + 0x10
END
END
INSERT_BYTES 0x54 0x10
WRITE_ASCIIE 0x54 ~%id2_header%~
SET fj_return_offset += 0x10
END

// wipedown remaining arrays
PHP_EACH struct AS key => value BEGIN
CLEAR_ARRAY EVAL ~are_%value%~
END
CLEAR_ARRAY struct

END

// EOF
[/codebox]

[codebox]
\verb+fj_add_are_structure+: adds a structure to an area file. All variables are zero or blank by default unless otherwise indicated. Fields designated by an asterisk are typically required; all others are optional.
This is a PATCH function.

Universal structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_structure_type+ to the type of area structure to be added (actor, region, spawn, entrance, container, itm, ambient, variable, door, animation, explored bitmask, songlist, rest interrupt table, note or projectile)*;
\item INT_VAR \verb+fj_delete_mode+ to the index of the structure to be deleted (if deleting rather than adding a new structure);
\item INT_VAR \verb+fj_delete+ to 1 to enable feedback
\item RET \verb+fj_return_offset+ returns the position of the structure in the area file;
\end{itemize}
Actor structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the actor's name*;
\item INT_VAR \verb+fj_loc_x+ to the starting X (horizontal) coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the starting Y (vertical) coordinate*;
\item INT_VAR \verb+fj_dest_x+ to the destination X coordinate (normally the same as fj_loc_x)*;
\item INT_VAR \verb+fj_dest_y+ to the destination Y coordinate (normally the same as fj_loc_y)*;
\item INT_VAR \verb+fj_loading+ to whether the .cre file is loaded (0=attached, 1=loaded, default 1);
\item INT_VAR \verb+fj_spawned+ to whether the creature has been spawned (0=no, 1=yes);
\item INT_VAR \verb+fj_animation+ to the actor's animation number (from animate.ids, though the engine uses the animation set on the .cre file);
\item INT_VAR \verb+fj_orientation+ to the facing direction (0-15 where 0=south, 4=west, 8=north, 12=east)*;
\item INT_VAR \verb+fj_expiry+ to the actor removal timer in absolute ticks (default -1 to avoid removal);
\item INT_VAR \verb+fj_wander_dist_actor+ to the actor's random walk distance limit;
\item INT_VAR \verb+fj_mvmt_dist_actor+ to the actor's movement distance limit;
\item INT_VAR \verb+fj_schedule+ to the hourly appearance schedule (bits 0-23, default -1 or always);
\item INT_VAR \verb+fj_num_talked+ to the NumTimesTalkedTo (in .sav files);
\item STR_VAR \verb+fj_dlg_resref+ to the actor's dialogue file (normally obtained from .cre files);
\item STR_VAR \verb+fj_bcs_override+ to the actor's override script (normally obtained from .cre files);
\item STR_VAR \verb+fj_bcs_general+ to the actor's general script (normally obtained from .cre files);
\item STR_VAR \verb+fj_bcs_class+ to the actor's class script (normally obtained from .cre files);
\item STR_VAR \verb+fj_bcs_race+ to the actor's race script (normally obtained from .cre files);
\item STR_VAR \verb+fj_bcs_default+ to the actor's default script (normally obtained from .cre files);
\item STR_VAR \verb+fj_bcs_specific+ to the actor's specific script (normally obtained from .cre files);
\item STR_VAR \verb+fj_cre_resref+ to the actor's resource reference (creature filename)*;
\item STR_VAR \verb+fj_cre_embedded+ to ~path/to/file.cre~ if embedding a new creature (defaults to fj_cre_resref if not set);
\end{itemize}
Region structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the region's name*;
\item INT_VAR \verb+fj_type+ to the region type (0=trap, 1=info, 2=travel)*;
\item INT_VAR \verb+fj_box_left+ to the leftmost X coordinate of the region's bounding box*;
\item INT_VAR \verb+fj_box_top+ to the topmost Y coordinate of the region's bounding box*;
\item INT_VAR \verb+fj_box_right+ to the rightmost X coordinate of the region's bounding box*;
\item INT_VAR \verb+fj_box_bottom+ to the bottommost Y coordinate of the region's bounding box*;
\item INT_VAR \verb+fj_cursor_idx+ to the region's mouse cursor index (from cursors.bam)*;
\item STR_VAR \verb+fj_destination_area+ to the destination area resource reference (for travel regions);
\item STR_VAR \verb+fj_destination_name+ to the entrance name in the destination area (for travel regions);
\item INT_VAR \verb+fj_flags+ to the bitwise region flags;
\item INT_VAR \verb+fj_info_point_strref+ to the information text string reference (for info points, default -1);
\item INT_VAR \verb+fj_trap_detect+ to the trap detection difficulty percentage;
\item INT_VAR \verb+fj_trap_remove+ to the trap removal difficulty percentage;
\item INT_VAR \verb+fj_trap_active+ to whether the region is trapped (0=no, 1=yes);
\item INT_VAR \verb+fj_trap_status+ to whether the trap is detected (0=no, 1=yes);
\item INT_VAR \verb+fj_loc_x+ to the trap launch X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the trap launch Y coordinate*;
\item STR_VAR \verb+fj_key_resref+ to the filename of the region's key;
\item STR_VAR \verb+fj_reg_script+ to the region script;
\item INT_VAR \verb+fj_alt_x+ to the activation point X coordinate;
\item INT_VAR \verb+fj_alt_y+ to the activation point Y coordinate;
\item INT_VAR \verb+fj_vertex_0+ to (X coordinate + (Y coordinate << 16)) for each vertex pair*;
\end{itemize}
Spawn structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the spawn point's name*;
\item INT_VAR \verb+fj_loc_x+ to the spawning X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the spawning Y coordinate*;
\item STR_VAR \verb+fj_cre_resref0+ to the resource reference of each creature spawned (0-9)*;
\item INT_VAR \verb+fj_spawn_num+ to the count of spawn creatures*;
\item INT_VAR \verb+fj_base_num+ to the base number of creatures to spawn (encounter difficulty)*;
\item INT_VAR \verb+fj_delay+ to the number of seconds between spawning (default 10);
\item INT_VAR \verb+fj_method+ to the spawn method;
\item INT_VAR \verb+fj_duration+ to the creature duration (default 1000);
\item INT_VAR \verb+fj_wander_dist_spawn+ to the creature's random walk distance limit (default 1000);
\item INT_VAR \verb+fj_mvmt_dist_spawn+ to the creature's movement distance limit (default 1000);
\item INT_VAR \verb+fj_max_num+ to the maximum number of creatures to spawn*;
\item INT_VAR \verb+fj_enable+ to the spawn point status (0=inactive, 1=active, default 1);
\item INT_VAR \verb+fj_schedule+ to the hourly appearance schedule (bits 0-23, default -1 or always);
\item INT_VAR \verb+fj_day_prob+ to the spawn point daytime probability (default 100);
\item INT_VAR \verb+fj_night_prob+ to the spawn point nighttime probability (default 100);
\end{itemize}
Entrance structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the entrance's name*;
\item INT_VAR \verb+fj_loc_x+ to the X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the Y coordinate*;
\item INT_VAR \verb+fj_orientation+ to the facing direction (0-15 where 0=south, 4=west, 8=north, 12=east)*;
\end{itemize}
Container structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the container's name*;
\item INT_VAR \verb+fj_loc_x+ to the X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the Y coordinate*;
\item INT_VAR \verb+fj_type+ to the container type (1=bag, 2=chest, 3=drawer, 4=pile, 5=table, 6=shelf, 7=altar, 8=nonvisible, 9=spellbook, 10=body, 11=barrel, 12=crate)*;
\item INT_VAR \verb+fj_lock_diff+ to the lock difficulty (default 100);
\item INT_VAR \verb+fj_flags+ to the bitwise container flags (bit0=locked, bit3=trap resets, bit5=disabled);
\item INT_VAR \verb+fj_trap_detect+ to the trap detection difficulty percentage;
\item INT_VAR \verb+fj_trap_remove_diff+ to the trap removal difficulty percentage (default 100);
\item INT_VAR \verb+fj_trap_active+ to whether the container is trapped (0=no, 1=yes);
\item INT_VAR \verb+fj_trap_status+ to whether the trap is detected (0=no, 1=yes);
\item INT_VAR \verb+fj_trap_loc_x+ to the trap launch X coordinate*;
\item INT_VAR \verb+fj_trap_loc_y+ to the trap launch Y coordinate*;
\item INT_VAR \verb+fj_box_left+ to the leftmost X coordinate of the trap's bounding box;
\item INT_VAR \verb+fj_box_top+ to the topmost Y coordinate of the trap's bounding box;
\item INT_VAR \verb+fj_box_right+ to the rightmost X coordinate of the trap's bounding box;
\item INT_VAR \verb+fj_box_bottom+ to the bottommost Y coordinate of the trap's bounding box;
\item STR_VAR \verb+fj_trap_script+ to the trap's script;
\item INT_VAR \verb+fj_vertex_0+ to (X coordinate + (Y coordinate << 16)) for each vertex pair*;
\item STR_VAR \verb+fj_key_resref+ to the filename of the container's key;
\item INT_VAR \verb+fj_lockpick_strref+ to the lockpick string reference (default -1);
\end{itemize}
Item structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the item's resource reference (filename)*;
\item INT_VAR \verb+fj_con_itm_idx+ to the index of the container to which the item is added;
\item INT_VAR \verb+fj_itm_expiry+ to the item expiration time;
\item INT_VAR \verb+fj_charge0+ to the charges of the 1st ability (item quantity for stackables);
\item INT_VAR \verb+fj_charge1+ to the charges of the 2nd ability;
\item INT_VAR \verb+fj_charge1+ to the charges of the 3rd ability;
\item INT_VAR \verb+fj_flags+ to the bitwise item flags (bit0=identified, bit1=unstealable, bit2=stolen, bit3=undroppable);
\end{itemize}
Ambient structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the ambient's name*;
\item INT_VAR \verb+fj_loc_x+ to the X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the Y coordinate*;
\item INT_VAR \verb+fj_radius+ to the sound radius (default 500);
\item INT_VAR \verb+fj_loc_z+ to the Z coordinate (height);
\item INT_VAR \verb+fj_volume+ to the volume percentage (default 80);
\item STR_VAR \verb+fj_wav_resref0+ to the resource reference of each sound (0-9)*;
\item INT_VAR \verb+fj_sound_num+ to the number of sounds*;
\item INT_VAR \verb+fj_delay+ to the base interval in seconds between sounds from this ambient list;
\item INT_VAR \verb+fj_variation+ to the base deviation from the base interval;
\item INT_VAR \verb+fj_schedule+ to the hourly appearance schedule (bits 0-23, default -1 or always);
\item INT_VAR \verb+fj_flags+ to the bitwise ambient flags*;
\end{itemize}
Variable structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the variable's name*;
\item INT_VAR \verb+fj_variable_value+ to the variable's value*;
\end{itemize}
Door structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the door's name*;
\item STR_VAR \verb+fj_door_wed_id+ to the door ID linked to the .wed file*;
\item INT_VAR \verb+fj_flags+ to the bitwise door flags*;
\item INT_VAR \verb+fj_open_box_left+ to the leftmost X coordinate of the open door's bounding box*;
\item INT_VAR \verb+fj_open_box_top+ to the topmost Y coordinate of the open door's bounding box*;
\item INT_VAR \verb+fj_open_box_right+ to the rightmost X coordinate of the open door's bounding box*;
\item INT_VAR \verb+fj_open_box_bottom+ to the bottommost Y coordinate of the open door's bounding box*;
\item INT_VAR \verb+fj_closed_box_left+ to the leftmost X coordinate of the closed door's bounding box*;
\item INT_VAR \verb+fj_closed_box_top+ to the topmost Y coordinate of the closed door's bounding box*;
\item INT_VAR \verb+fj_closed_box_right+ to the rightmost X coordinate of the closed door's bounding box*;
\item INT_VAR \verb+fj_closed_box_bottom+ to the bottommost Y coordinate of the closed door's bounding box*;
\item INT_VAR \verb+fj_door_open_vert_0+ to (X coordinate + (Y coordinate << 16)) for each vertex pair of the open door*;
\item INT_VAR \verb+fj_door_closed_vert_0+ to (X coordinate + (Y coordinate << 16)) for each vertex pair of the closed door*;
\item INT_VAR \verb+fj_cell_open_vert_0+ to (X coordinate + (Y coordinate << 16)) for each impeded search map cell of the open door*;
\item INT_VAR \verb+fj_cell_closed_vert_0+ to (X coordinate + (Y coordinate << 16)) for each impeded search map cell of the closed door*;
\item STR_VAR \verb+fj_door_open_wav+ to the door open sound;
\item STR_VAR \verb+fj_door_close_wav+ to the door closed sound;
\item INT_VAR \verb+fj_cursor_idx+ to the door's mouse cursor index (from cursors.bam)*;
\item INT_VAR \verb+fj_trap_detect+ to the trap detection difficulty percentage;
\item INT_VAR \verb+fj_trap_remove+ to the trap removal difficulty percentage;
\item INT_VAR \verb+fj_trap_active+ to whether the door is trapped (0=no, 1=yes);
\item INT_VAR \verb+fj_trap_status+ to whether the trap is detected (0=no, 1=yes);
\item INT_VAR \verb+fj_trap_loc_x+ to the trap launch X coordinate*;
\item INT_VAR \verb+fj_trap_loc_y+ to the trap launch Y coordinate*;
\item STR_VAR \verb+fj_key_resref+ to the filename of the door's key;
\item STR_VAR \verb+fj_door_script+ to the door script;
\item INT_VAR \verb+fj_detect_diff+ to the detection difficulty (for secret doors);
\item INT_VAR \verb+fj_locked_diff+ to the lock difficulty;
\item INT_VAR \verb+fj_open_loc_x+ to the X coordinate for toggling the door's open state*;
\item INT_VAR \verb+fj_open_loc_y+ to the Y coordinate for toggling the door's open state*;
\item INT_VAR \verb+fj_closed_loc_x+ to the X coordinate for toggling the door's closed state*;
\item INT_VAR \verb+fj_closed_loc_y+ to the Y coordinate for toggling the door's closed state*;
\item INT_VAR \verb+fj_lockpick_strref+ to the lockpick string reference (default -1);
\item STR_VAR \verb+fj_travel_trigger+ to the travel region name*;
\item INT_VAR \verb+fj_dlg_strref+ to the dialogue string reference (default -1);
\item STR_VAR \verb+fj_dlg_resref+ to the door's dialogue file;
\end{itemize}
Animation structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the animation name*;
\item INT_VAR \verb+fj_loc_x+ to the X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the Y coordinate*;
\item INT_VAR \verb+fj_schedule+ to the hourly appearance schedule (bits 0-23, default -1 or always);
\item STR_VAR \verb+fj_bam_resref+ to the animation resource reference (filename)*;
\item INT_VAR \verb+fj_bam_seq+ to the BAM sequence number;
\item INT_VAR \verb+fj_bam_frame+ to the BAM frame number;
\item INT_VAR \verb+fj_flags+ to the bitwise animation flags*;
\item INT_VAR \verb+fj_loc_z+ to the height;
\item INT_VAR \verb+fj_transparent+ to the BAM transparency;
\item INT_VAR \verb+fj_init_frame+ to the starting frame;
\item INT_VAR \verb+fj_loop_chance+ to the chance of looping;
\item INT_VAR \verb+fj_skip_cycles+ to start delay in frames;
\item STR_VAR \verb+fj_bmp_resref+ to the palette bitmap;
\end{itemize}
Bitmask structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_bitmask+ to ~path/to/binary.file~*;
\end{itemize}
Songlist structure variables:
\begin{itemize}
\item INT_VAR \verb+fj_song_day+ to the day SONGLIST.2DA entry;
\item INT_VAR \verb+fj_song_night+ to the night SONGLIST.2DA entry;
\item INT_VAR \verb+fj_song_victory+ to the victory SONGLIST.2DA entry;
\item INT_VAR \verb+fj_song_battle+ to the battle SONGLIST.2DA entry;
\item INT_VAR \verb+fj_song_defeat+ to the defeat SONGLIST.2DA entry;
\item STR_VAR \verb+fj_song_day0+ to the day song WAV resref;
\item STR_VAR \verb+fj_song_day1+ to the night song WAV resref;
\item INT_VAR \verb+fj_song_day_vol+ to the day songs volume (default 100);
\item STR_VAR \verb+fj_song_night0+ to the night song WAV resref;
\item STR_VAR \verb+fj_song_night1+ to the second night song WAV resref;
\item INT_VAR \verb+fj_song_night_vol+ to the night songs volume (default 100);
\end{itemize}
Rest interrupt structure variables
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the name of the rest interrupts (for editor use only);
\item INT_VAR \verb+fj_cre_strref0...fj_cre_strref9+ to string displayed upon party ambush (default -1);
\item STR_VAR \verb+fj_cre_resref0...fj_cre_resref9+ to creature resref;
\item STR_VAR \verb+fj_spawn_num+ to the number of spawned attackers;
\item STR_VAR \verb+fj_difficulty+ to the difficulty of the encounter;
\item STR_VAR \verb+fj_duration+ to the creature's duration (default 1000);
\item STR_VAR \verb+fj_wander_distance+ to the creature's random walk distance limit (default 1000);
\item STR_VAR \verb+fj_mvmt_distance+ to the creature's movement distance limit (default 1000);
\item STR_VAR \verb+fj_max_num+ to maximum number of spawned creatures;
\item STR_VAR \verb+fj_enable+ to whether rest interrupts are enabled (0=no, 1=yes);
\item STR_VAR \verb+fj_day_prob+ to probability of daytime ambush;
\item STR_VAR \verb+fj_night_prob+ to probability of nightime ambush;
\end{itemize}
Map note structure variables:
\begin{itemize}
\item INT_VAR \verb+fj_loc_x+ to the X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the Y coordinate*;
\item INT_VAR \verb+fj_note_strref+ to the note string reference (default -1, BGII only)*;
\item STR_VAR \verb+fj_note_text+ to the note text (PST only)*;
\item INT_VAR \verb+fj_strref_loc+ to the strref location (0=external, 1=dialog.tlk, default 1);
\item INT_VAR \verb+fj_color+ to the map marker color (0-7);
\item INT_VAR \verb+fj_note_id+ to the note ID;
\end{itemize}
Projectile trap structure variables:
\begin{itemize}
\item STR_VAR \verb+fj_name+ to the projectile filename*;
\item INT_VAR \verb+fj_eff_off+ to the effect block offset;
\item INT_VAR \verb+fj_eff_size+ to the effect block size;
\item INT_VAR \verb+fj_missile_num+ to the missile.ids reference (projectl.ids - 1);
\item INT_VAR \verb+fj_frequency to the explosion length (in frames);
\item INT_VAR \verb+fj_duration+ to the number of explosions;
\item INT_VAR \verb+fj_loc_x+ to the X coordinate*;
\item INT_VAR \verb+fj_loc_y+ to the Y coordinate*;
\item INT_VAR \verb+fj_loc_z+ to the height;
\item INT_VAR \verb+fj_target+ to the target ID;
\item STR_VAR \verb+fj_embedded_eff0+ to ~path/to/v2.eff~ or eff resref containing to projectile's effects*;
\end{itemize}
A few examples best illustrate the use of this function.

Example 1: add an actor to an area
\begin{verbatim}
COPY_EXISTING ar0500.are override
LPF fj_are_structure
INT_VAR
fj_loc_x = 2780
fj_loc_y = 1955
fj_dest_x = 2780
fj_dest_y = 1955
fj_animation = 0x6110 //fighter female human
fj_orientation = 15 //SSE
STR_VAR
fj_structure_type = actor
fj_name = Aurora
fj_cre_resref = agaurora
END
\end{verbatim}
Example 2: add a region to an area (in this case, a travel trigger to another area)
\begin{verbatim}
LPF fj_are_structure
INT_VAR
fj_type = 2 //travel
fj_box_left = 3415
fj_box_top = 625
fj_box_right = 3450
fj_box_bottom = 700
fj_cursor_idx = 30 //door
fj_vertex_0 = 3415 + (625 << 16)
fj_vertex_1 = 3450 + (650 << 16)
fj_vertex_2 = 3450 + (700 << 16)
fj_vertex_3 = 3415 + (676 << 16)
STR_VAR
fj_structure_type = region
fj_name = Tran0540
fj_destination_area = ag0540
fj_destination_name = Exit0500
END
\end{verbatim}
Example 3: add an entrance (from another area) to an area
\begin{verbatim}
LPF fj_are_structure
INT_VAR
fj_loc_x = 3490
fj_loc_y = 655
fj_orientation = 10 //NE
STR_VAR
fj_structure_type = entrance
fj_name = Exit0540
END
\end{verbatim}
Example 4: add a container to an area, then add an item to the new container
\begin{verbatim}
LPF fj_are_structure
INT_VAR
fj_type = 8 //nonvisible
fj_loc_x = 4388
fj_loc_y = 2876
fj_box_left = 4372
fj_box_top = 2826
fj_box_right = 4420
fj_box_bottom = 2858
fj_trap_loc_x = 4380
fj_trap_loc_y = 2870
fj_vertex_0 = 4411 + (2858 << 16)
fj_vertex_1 = 4372 + (2845 << 16)
fj_vertex_2 = 4382 + (2826 << 16)
fj_vertex_3 = 4420 + (2839 << 16)
STR_VAR
fj_structure_type = container
fj_name = ~Cornerstone~
END
LPF fj_are_structure
INT_VAR
fj_con_itm_idx = SHORT_AT 0x74 - 1 // a new container will be last in the file
fj_flags = 1 // identified
STR_VAR
fj_name = c6lantho
fj_structure_type = itm
END
\end{verbatim}
Example 5: add a door to an area
\begin{verbatim}
LPF fj_are_structure
INT_VAR
fj_flags = 0b100000000
fj_open_box_left = 520
fj_open_box_top = 724
fj_open_box_right = 545
fj_open_box_bottom = 830
fj_closed_box_left = 507
fj_closed_box_top = 761
fj_closed_box_right = 562
fj_closed_box_bottom = 869
fj_cursor_idx = 30
fj_trap_loc_x = 500
fj_trap_loc_y = 852
fj_open_loc_x = 517
fj_open_loc_y = 881
fj_closed_loc_x = 562
fj_closed_loc_y = 814
fj_door_open_vert_0 = 520 + (826 << 16)
fj_door_open_vert_1 = 527 + (830 << 16)
fj_door_open_vert_2 = 545 + (798 << 16)
fj_door_open_vert_3 = 545 + (727 << 16)
fj_door_open_vert_4 = 539 + (724 << 16)
fj_door_open_vert_5 = 520 + (750 << 16)
fj_door_closed_vert_0 = 507 + (831 << 16)
fj_door_closed_vert_1 = 562 + (869 << 16)
fj_door_closed_vert_2 = 562 + (799 << 16)
fj_door_closed_vert_3 = 507 + (761 << 16)
fj_cell_open_vert_0 = 32 + (68 << 16)
fj_cell_open_vert_1 = 33 + (67 << 16)
fj_cell_open_vert_2 = 32 + (67 << 16)
fj_cell_closed_vert_0 = 32 + (70 << 16)
fj_cell_closed_vert_1 = 33 + (71 << 16)
fj_cell_closed_vert_2 = 34 + (72 << 16)
fj_cell_closed_vert_3 = 34 + (71 << 16)
fj_cell_closed_vert_4 = 33 + (70 << 16)
fj_cell_closed_vert_5 = 32 + (69 << 16)
fj_cell_closed_vert_6 = 32 + (68 << 16)
fj_cell_closed_vert_7 = 33 + (69 << 16)
fj_cell_closed_vert_8 = 34 + (70 << 16)
STR_VAR
fj_structure_type = door
fj_name = Door10
fj_door_wed_id = DOOR10
END
\end{verbatim}
Example 6: add an animation to an area
\begin{verbatim}
LPF fj_are_structure
INT_VAR
fj_loc_x = 2785
fj_loc_y = 949
fj_flags = 0b00000000000000000001000110000101
//visible, not illuminated, invisible in dark, covered by actors, shown in combat
STR_VAR
fj_structure_type = animation
fj_name = Cave1
fj_bam_resref = ag1100c1
END
\end{verbatim}
Example 7: delete all ambients from an area
\begin{verbatim}
FOR( i = SHORT_AT 0x82 ; i ; --i )BEGIN
LPF fj_are_structure
INT_VAR fj_delete_mode = i - 1
STR_VAR fj_structure_type = ambient
END
END
\end{verbatim}

[/codebox]

#2 -Guest-

-Guest-
  • Guest

Posted 24 September 2010 - 05:37 AM

that should be "Single structures (bitmask, songlist, rest interrupts) still need fj_delete_mode = 0" of course. whatever.

#3 Miloch

Miloch

    Barbarian

  • Modder
  • 6573 posts

Posted 24 September 2010 - 05:49 AM

Ooh, pretty. Even updated documentation too! :Bow:

Infinity Engine Contributions
Aurora * BG1 NPC * BG1 Fixpack * Haiass * Infinity Animations * Level 1 NPCs * P5Tweaks
PnP Free Action * Thrown Hammers * Unique Containers * BG:EE * BGII:EE * IWD:EE
================================================================
Player & Modder Resources
BAM Batcher * Creature Lister * Creature Checker * Creature Fixer * Tutu/BGT Area Map & List * Tutu Mod List
================================================================
"Infinity turns out to be the opposite of what people say it is. It is not 'that which has nothing beyond itself' that is infinite, but 'that which always has something beyond itself'." -Aristotle


#4 Steve

Steve
  • Member
  • 142 posts

Posted 25 September 2010 - 11:08 AM

Who was that masked man!?

#5 Echon

Echon

    Planewanker

  • Member
  • 284 posts

Posted 04 October 2010 - 12:31 PM

I have added CREs with this function (which was not terribly difficult considering the detailed example) but I was hoping someone could write an example of how to remove one actor from an area, since I clearly cannot. I am not even sure if it is supposed to target the actor by # or resref or whatever.

Regarding adding CREs, is there any point in having the animation variable or not setting it to 0? Looking at a number of identical CREs in a ARE with NI, this offset is often 0 or something seemingly random.

#6 Miloch

Miloch

    Barbarian

  • Modder
  • 6573 posts

Posted 04 October 2010 - 07:56 PM

I have split this out of the Aurora comments thread, in case someone might actually want to talk about the mod there :P.

I have added CREs with this function (which was not terribly difficult considering the detailed example) but I was hoping someone could write an example of how to remove one actor from an area, since I clearly cannot. I am not even sure if it is supposed to target the actor by # or resref or whatever.

I have not had a chance to test it yet (which is maybe where you can help out :)) but it would seem to take a number according to this:

set fj_delete_mode to the count-from-zero index of the structure to delete, and fj_structure type as usual. Single structures (bitmask, songlist, rest interrupts) still need fj_delete_mode = 1.

Meaning that you should only need to do this for example:
COPY_EXISTING fw2602.are override
  LPF fj_are_structure
    INT_VAR
    fj_delete_mode    = 0 (should delete "Shank" in the Tutu Candlekeep Priest's Quarters)
    STR_VAR
    fj_structure_type = actor
  END
BUT_ONLY
Because Shank is the first and only actor (starting from 0). Of course it would be useful if you could actually match it against numeric index *and* resource name, in case someone has inserted another actor before the one you want to delete. Maybe it does that already, though I'd think our "Guest" would have said so.

Regarding adding CREs, is there any point in having the animation variable or not setting it to 0? Looking at a number of identical CREs in a ARE with NI, this offset is often 0 or something seemingly random.

Not really - you don't really need to set it at all, since the default is 0. It gets the animation from the .cre files. (However, I think it *might* need to be set for areas with embedded creatures, like those in saved games.) I always set it so I have some idea of what kind of creatures are in my areas without necessarily having to dig up the .cre files separately.

Infinity Engine Contributions
Aurora * BG1 NPC * BG1 Fixpack * Haiass * Infinity Animations * Level 1 NPCs * P5Tweaks
PnP Free Action * Thrown Hammers * Unique Containers * BG:EE * BGII:EE * IWD:EE
================================================================
Player & Modder Resources
BAM Batcher * Creature Lister * Creature Checker * Creature Fixer * Tutu/BGT Area Map & List * Tutu Mod List
================================================================
"Infinity turns out to be the opposite of what people say it is. It is not 'that which has nothing beyond itself' that is infinite, but 'that which always has something beyond itself'." -Aristotle


#7 Echon

Echon

    Planewanker

  • Member
  • 284 posts

Posted 04 October 2010 - 11:31 PM

That looks a lot like what I was trying to do yesterday but anyway, this works. Thanks. Consider the adding/removing CREs part of it tested.

#8 -Guest-

-Guest-
  • Guest

Posted 09 October 2010 - 05:42 AM

repost with fix
DEFINE_PATCH_FUNCTION fj_are_structure

  // let's set up our variables! i++++ 4eva

  INT_VAR

  // these are internal variables, not to be used as input
  is_bg2               = ENGINE_IS ~soa tob~
  is_pst               = ENGINE_IS pst
  is_id2               = ENGINE_IS iwd2
  fj_position          = 0x11c
  fj_itm_idx           = 0
  fj_vertex_idx        = 0
  off                  = 0
  num                  = 0
  off1                 = 0
  num1                 = 0
  key                  = 0
  value                = 0
  key1                 = 0
  value1               = 0
  fj_return_offset     = 0
  fj_deleted           = 0

  // instead of adding fj_structure_type, delete if num == fj_delete_mode
  fj_delete_mode       = ` 0

  // offsets to various structures unpointed at zero
  fj_unzero_header_off = 0

  // spams your console with psuedoinformative nonsense
  fj_debug             = 0

  // actors
  fj_loc_x             = 0
  fj_loc_y             = 0
  fj_dest_x            = 0
  fj_dest_y            = 0
  fj_loading           = 1
  fj_spawned           = 0
  fj_animation         = 0
  fj_orientation       = 0
  fj_expiry            = ` 0
  fj_wander_dist_actor = 0
  fj_mvmt_dist_actor   = 0
  fj_schedule          = ` 0
  fj_num_talked        = 0

  // regions
  fj_type              = 0
  fj_box_left          = 0
  fj_box_top           = 0
  fj_box_right         = 0
  fj_box_bottom        = 0
  fj_cursor_idx        = 0
  fj_flags             = 0
  fj_info_point_strref = ` 0
  fj_trap_detect       = 0
  fj_trap_remove       = 0
  fj_trap_active       = 0
  fj_trap_status       = 0
  fj_alt_x             = 0
  fj_alt_y             = 0

  // spawns
  fj_spawn_num         = 0
  fj_difficulty        = 0
  fj_delay             = 10
  fj_method            = 0
  fj_duration          = 1000
  fj_wander_dist_spawn = 1000
  fj_mvmt_dist_spawn   = 1000
  fj_max_num           = 0
  fj_enable            = 1
  fj_day_prob          = 100
  fj_night_prob        = 100

  // containers
  fj_lock_diff         = 100
  fj_trap_remove_diff  = 100
  fj_trap_loc_x        = 0
  fj_trap_loc_y        = 0
  fj_lockpick_strref   = ` 0

  // items
  fj_con_itm_idx       = ` 0
  fj_itm_expiry        = 0
  fj_charge0           = 0
  fj_charge1           = 0
  fj_charge2           = 0

  // ambients
  fj_radius            = 500
  fj_loc_z             = 0
  fj_volume            = 80
  fj_sound_num         = 0
  fj_delay             = 0
  fj_variation         = 0

  // variables
  fj_variable_value    = 0

  // doors
  fj_open_box_left     = 0
  fj_open_box_top      = 0
  fj_open_box_right    = 0
  fj_open_box_bottom   = 0
  fj_closed_box_left   = 0
  fj_closed_box_top    = 0
  fj_closed_box_right  = 0
  fj_closed_box_bottom = 0
  fj_detect_diff       = 0
  fj_locked_diff       = 0
  fj_open_loc_x        = 0
  fj_open_loc_y        = 0
  fj_closed_loc_x      = 0
  fj_closed_loc_y      = 0
  fj_dlg_strref        = ` 0

  // animations
  fj_bam_seq           = 0
  fj_bam_frame         = 0
  fj_transparent       = 0
  fj_init_frame        = 0
  fj_loop_chance       = 0
  fj_skip_cycles       = 0

  // songs
  fj_song_day          = 0
  fj_song_night        = 0
  fj_song_victory      = 0
  fj_song_battle       = 0
  fj_song_defeat       = 0
  fj_song_day_vol      = 100
  fj_song_night_vol    = 100
  fj_song_reverb       = 0

  // rest interrupts
  fj_cre_strref0       = ` 0
  fj_cre_strref1       = ` 0
  fj_cre_strref2       = ` 0
  fj_cre_strref3       = ` 0
  fj_cre_strref4       = ` 0
  fj_cre_strref5       = ` 0
  fj_cre_strref6       = ` 0
  fj_cre_strref7       = ` 0
  fj_cre_strref8       = ` 0
  fj_cre_strref9       = ` 0

  // map notes
  fj_note_strref       = ` 0
  fj_strref_loc        = 1
  fj_color             = 0
  fj_note_id           = 0

  // embedded projectiles
  fj_missile_num       = ` 0
  fj_frequency         = 0
  fj_target            = 0

  STR_VAR

  // variables
  fj_structure_type    = ~~
  fj_name              = ~~

  // actors
  fj_dlg_resref        = ~~
  fj_bcs_override      = ~~
  fj_bcs_general       = ~~
  fj_bcs_class         = ~~
  fj_bcs_race          = ~~
  fj_bcs_default       = ~~
  fj_bcs_specific      = ~~
  fj_cre_resref        = ~~
  fj_cre_embedded      = ~~

  // regions
  fj_destination_area  = ~~
  fj_destination_name  = ~~
  fj_key_resref        = ~~
  fj_reg_script        = ~~

  // spawns
  fj_cre_resref0       = ~~
  fj_cre_resref1       = ~~
  fj_cre_resref2       = ~~
  fj_cre_resref3       = ~~
  fj_cre_resref4       = ~~
  fj_cre_resref5       = ~~
  fj_cre_resref6       = ~~
  fj_cre_resref7       = ~~
  fj_cre_resref8       = ~~
  fj_cre_resref9       = ~~

  // containers
  fj_trap_script       = ~~

  // ambients
  fj_wav_resref0       = ~~
  fj_wav_resref1       = ~~
  fj_wav_resref2       = ~~
  fj_wav_resref3       = ~~
  fj_wav_resref4       = ~~
  fj_wav_resref5       = ~~
  fj_wav_resref6       = ~~
  fj_wav_resref7       = ~~
  fj_wav_resref8       = ~~
  fj_wav_resref9       = ~~

  // doors
  fj_door_wed_id       = ~~
  fj_door_open_wav     = ~~
  fj_door_close_wav    = ~~
  fj_door_script       = ~~
  fj_travel_trigger    = ~~

  // animations
  fj_bam_resref        = ~~
  fj_bmp_resref        = ~~

  //bitmask
  fj_bitmask           = ~~

  // songs
  fj_song_day0         = ~~
  fj_song_day1         = ~~
  fj_song_night0       = ~~
  fj_song_night1       = ~~

  // map notes
  fj_note_text         = ~~ // only for PST

  RET
  fj_return_offset

BEGIN

// we must set $num(0) == num_0 when using function input
// otherwise WeiDU won't recognize that they're synonymous
PATCH_FOR_EACH value IN
  vertex door_open_vert door_closed_vert cell_open_vert cell_closed_vert
BEGIN
  FOR( num = 0 ; VARIABLE_IS_SET EVAL ~fj_%value%_%num%~ ; ++num )BEGIN
    SET $EVAL ~fj_%value%~(~%num%~) = EVAL ~fj_%value%_%num%~
  END
END
FOR( num = 0 ; VARIABLE_IS_SET EVAL ~fj_embedded_eff_%num%~ ; ++num )BEGIN
  TEXT_SPRINT $fj_embedded_eff(~%num%~) EVAL ~%fj_embedded_eff_%num%%~
END

// php over this array rather than an explicit list to reduce code length and typos
CLEAR_ARRAY struct
TEXT_SPRINT $struct(0x54 0x58 0x02 0x110) actor
TEXT_SPRINT $struct(0x5c 0x5a 0x02 0x0c4) region
TEXT_SPRINT $struct(0x60 0x64 0x04 0x0c8) spawn
TEXT_SPRINT $struct(0x68 0x6c 0x04 0x068) entrance
TEXT_SPRINT $struct(0x70 0x74 0x02 0x0c0) container
TEXT_SPRINT $struct(0x78 0x76 0x02 0x014) itm
TEXT_SPRINT $struct(0x84 0x82 0x02 0x0d4) ambient
TEXT_SPRINT $struct(0x88 0x8c 0x02 0x054) variable
TEXT_SPRINT $struct(0xa8 0xa4 0x04 0x0c8) door
TEXT_SPRINT $struct(0xb8 0xb4 0x04 0x06c) tiled
TEXT_SPRINT $struct(0x7c 0x80 0x02 0x004) vertex
TEXT_SPRINT $struct(0xb0 0xac 0x04 0x04c) animation
TEXT_SPRINT $struct(0xa0 0x9c 0x04 0x000) bitmask
TEXT_SPRINT $struct(0xbc 0x00 0x00 0x090) songs
TEXT_SPRINT $struct(0xc0 0x00 0x00 0x0e4) interrupts
PATCH_IF is_pst BEGIN
  TEXT_SPRINT $struct(0xc8 0xcc 0x04 0x214) note
END ELSE BEGIN
  TEXT_SPRINT $struct(0xc4 0xc8 0x04 0x034) note
END
TEXT_SPRINT $struct(0xcc 0xd0 0x02 0x01c) projectile
PHP_EACH struct AS key => value BEGIN
  PATCH_IF ~%value%~ STRING_EQUAL_CASE ~%fj_structure_type%~ BEGIN
    SET fj_structure_type = key_0
  END
END

// Icewind Dale II decided to be uselessly special
PATCH_IF is_id2 BEGIN
  READ_ASCII   0x54 id2_header (0x10)
  DELETE_BYTES 0x54 0x10
  PATCH_FOR_EACH off IN
    0x54 0x5c 0x60 0x68 0x70 0x78 0x7c 0x84 0x88 0xa0 0xa8 0xb0 0xb8 0xbc 0xc0
  BEGIN
    PATCH_IF LONG_AT off BEGIN
      WRITE_LONG off THIS - 0x10
    END
  END
END

PATCH_IF fj_unzero_header_off BEGIN

  // a small courtesy to fix areas missing obligatory structures
  PATCH_IF fj_delete_mode == ` 0 BEGIN
    PATCH_IF !LONG_AT 0xbc && fj_structure_type != 0xbc BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Offset to songlist points to 0! Adding empty songlist.~ END
      WRITE_LONG 0xbc BUFFER_LENGTH
      INSERT_BYTES BUFFER_LENGTH 0x90
    END
    PATCH_IF !LONG_AT 0xc0 && fj_structure_type != 0xc0 BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Offset to rest interrupts points to 0! Adding empty rest interrupt table.~ END
      WRITE_LONG 0xc0 BUFFER_LENGTH
      INSERT_BYTES BUFFER_LENGTH 0xe4
    END
  END

  // offsets to valid structures should not point to 0
  PATCH_FOR_EACH off IN
    0x54 0x5c 0x60 0x68 0x70 0x78 0x7c 0x84 0x88 0xa0 0xa8 0xb0 0xb8
  BEGIN
    PATCH_IF !LONG_AT off BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Header offset %off% points to 0, setting to 0x11c.~ END
      WRITE_LONG off 0x11c
    END
  END
  PATCH_IF is_pst BEGIN
    PATCH_IF !LONG_AT 0xc8 BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Header offset 0xc8 points to 0, setting to 0x11c.~ END
      WRITE_LONG 0xc8 0x11c
    END
  END ELSE PATCH_IF is_bg2 BEGIN
    PATCH_FOR_EACH off IN 0xc4 0xcc BEGIN
      PATCH_IF !LONG_AT off BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Header offset %off% points to 0, setting to 0x11c.~ END
        WRITE_LONG off 0x11c
      END
    END
  END

END

// long block to read all the existing data
PATCH_IF fj_debug BEGIN PATCH_PRINT ~Beginning unmarshalling.~ END
PHP_EACH struct AS key => value BEGIN
  PATCH_IF( LONG_AT key_0 )BEGIN // skip it if the header offset points at 0
    CLEAR_ARRAY EVAL ~%value%~
    GET_OFFSET_ARRAY EVAL ~%value%~ key_0 0x04 key_1 key_2 0x00 0x00 key_3 // e.g. $region
    PHP_EACH ~%value%~ AS num => off BEGIN
      CLEAR_ARRAY array

      PATCH_IF( key_0 == 0x54 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading actor %num%.~ END
        PATCH_IF!( LONG_AT (off + 0x28) & 0x01 )BEGIN
          PATCH_IF fj_debug BEGIN PATCH_PRINT ~  Associating embedded creature.~ END
          READ_ASCII LONG_AT (off + 0x88) $are_embedded_cre(~%num%~) (LONG_AT (off + 0x8c))
        END
        WRITE_LONG off + 0x88 0
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0x5c )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading region %num%.~ END
        PATCH_IF( fj_debug && SHORT_AT ( off + 0x2a ) )BEGIN PATCH_PRINT ~  Associating vertices.~ END
        GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x2a) 0x02 (off + 0x2c) 0x04 0x04
        PHP_EACH array AS num1 => off1 BEGIN
          READ_LONG off1 $EVAL ~are_region_%num%_vertex~(~%num1%~)
        END
        CLEAR_ARRAY array
        WRITE_LONG off + 0x2c 0x00
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0x60 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading spawn %num%~ END
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0x68 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading entrance %num%~ END
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0x70 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading container %num%.~ END

        // load container vertices
        GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x54) 0x04 (off + 0x50) 0x04 0x04
        PATCH_IF( fj_debug && LONG_AT 0x54 )BEGIN PATCH_PRINT ~  Associating vertices.~ END
        PHP_EACH array AS num1 => off1 BEGIN
          READ_LONG off1 $EVAL ~are_container_%num%_vertex~(~%num1%~)
        END
        CLEAR_ARRAY array

        // load container items
        GET_OFFSET_ARRAY array 0x78 0x04 (off + 0x44) 0x04 (off + 0x40) 0x04 0x14
        PATCH_IF( fj_debug && LONG_AT 0x44 )BEGIN PATCH_PRINT ~  Associating items.~ END
        PHP_EACH array AS num1 => off1 BEGIN
          READ_ASCII off1 $EVAL ~are_container_%num%_itm~(~%num1%~) (0x14)
        END
        CLEAR_ARRAY array

        // read container structure
        WRITE_LONG off + 0x40 0x00 // wipe item index
        WRITE_LONG off + 0x44 0x00 // wipe item count
        WRITE_LONG off + 0x50 0x00 // wipe vertex index
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      // items are read off with their associated containers
      PATCH_IF( key_0 == 0x78 )BEGIN
      END ELSE

      PATCH_IF( key_0 == 0x84 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading ambient %num%~ END
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0x88 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading variable %num%~ END
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0xa8 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading door %num% and associated vertices.~ END
        // load door open vertices
        GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x30) 0x02 (off + 0x2c) 0x04 0x04
        PHP_EACH array AS num1 => off1 BEGIN
          READ_LONG off1 $EVAL ~are_door_open_%num%_vertex~(~%num1%~)
        END
        CLEAR_ARRAY array
        WRITE_LONG off + 0x2c 0x00
        // load door closed vertices
        GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x32) 0x02 (off + 0x34) 0x04 0x04
        PHP_EACH array AS num1 => off1 BEGIN
          READ_LONG off1 $EVAL ~are_door_closed_%num%_vertex~(~%num1%~)
        END
        CLEAR_ARRAY array
        WRITE_LONG off + 0x34 0x00
        // load cell open vertices
        GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x4c) 0x02 (off + 0x48) 0x04 0x04
        PHP_EACH array AS num1 => off1 BEGIN
          READ_LONG off1 $EVAL ~are_cell_open_%num%_vertex~(~%num1%~)
        END
        CLEAR_ARRAY array
        WRITE_LONG off + 0x48 0x00
        // load cell closed vertices
        GET_OFFSET_ARRAY array 0x7c 0x04 (off + 0x4e) 0x02 (off + 0x50) 0x04 0x04
        PHP_EACH array AS num1 => off1 BEGIN
          READ_LONG off1 $EVAL ~are_cell_closed_%num%_vertex~(~%num1%~)
        END
        CLEAR_ARRAY array
        WRITE_LONG off + 0x50 0x00
        // read the door structure itself
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0xb8 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading tiled object %num% (something is probably very wrong).~ END
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      // vertices, these are read off with their associated regions/containers/doors
      PATCH_IF( key_0 == 0x78 )BEGIN
      END ELSE

      PATCH_IF( key_0 == 0xb0 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading animation %num%.~ END
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF(
        ( key_0 == 0xc4 && is_bg2 ) ||
        ( key_0 == 0xc8 && is_pst)
      )BEGIN //
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading map note %num%.~ END
        READ_ASCII off $EVAL ~are_%value%~(~%num%~) (key_3)
      END ELSE

      PATCH_IF( key_0 == 0xcc && is_bg2 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading projectile %num%.~ END
        READ_ASCII off $are_projectile(~%num%~) (key_3)
        PATCH_IF SHORT_AT (off + 0x0c) BEGIN
          PATCH_IF( fj_debug )BEGIN PATCH_PRINT ~  Associating v2 embedded effects.~ END
          READ_ASCII LONG_AT (off + 0x08) $are_embedded_eff(~%num%~) (SHORT_AT (off + 0x0c))
        END
      END

    END // php_each ~%value%~
    CLEAR_ARRAY array
    CLEAR_ARRAY EVAL ~%value%~

    // single structures

    // bitmask
    PATCH_IF( key_0 == 0xa0 )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading bitmask (should only exist in saved game areas).~ END
      READ_ASCII LONG_AT 0xa0 $are_bitmask(0) (LONG_AT 0x9c)
    END ELSE

    // songs
    PATCH_IF( key_0 == 0xbc )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading songs (obligatory structure).~ END
      READ_ASCII LONG_AT 0xbc $are_songs(0) (0x90)
    END ELSE

    // rest interrupts
    PATCH_IF( key_0 == 0xc0 )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reading rest interrupt table (obligatory structure).~ END
      READ_ASCII LONG_AT 0xc0 $are_interrupts(0) (0xe4)
    END

  END // skip if key_0 points to 0
END // end php_each $structure: all extended structures now loaded in buffer

PATCH_IF fj_debug BEGIN PATCH_PRINT ~Trimming %SOURCE_FILE% down to the header.~ END
DELETE_BYTES 0x11c BUFFER_LENGTH - 0x11c
PHP_EACH struct AS key => value BEGIN
  PATCH_IF( key_2 == 0x02 )BEGIN
    WRITE_SHORT key_1 0x00
  END ELSE
  PATCH_IF( key_2 == 0x04 )BEGIN
    WRITE_LONG  key_1 0x00
  END
END

PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassembling %SOURCE_FILE%.~ END
PHP_EACH struct AS key => value BEGIN
  WRITE_LONG key_0 fj_position
  PHP_EACH ~are_%value%~ AS key1 => value1 BEGIN
    PATCH_IF( key_0 != 0xa0 && key_0 != 0xbc && key_0 != 0xc0 )BEGIN
      PATCH_IF( key1 != fj_delete_mode || fj_structure_type != key_0 )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
        PATCH_IF( key_2 == 0x02 )BEGIN
          WRITE_SHORT key_1 THIS + 0x01
        END ELSE
        PATCH_IF( key_2 == 0x04 )BEGIN
          WRITE_LONG  key_1 THIS + 0x01
        END
        INSERT_BYTES fj_position key_3
        WRITE_ASCIIE fj_position ~%value1%~
        SET fj_position += key_3
      END
    END ELSE
    PATCH_IF( key_0 == 0xa0 && fj_structure_type != key_0 )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
      TEXT_SPRINT  value1      $are_bitmask(0)
      WRITE_LONG   0x9c        STRING_LENGTH EVAL ~%value1%~
      INSERT_BYTES fj_position LONG_AT 0x9c
      WRITE_ASCIIE fj_position ~%value1%~
      SET fj_position += LONG_AT 0x9c
    END ELSE
    PATCH_IF( key_0 == 0xbc && fj_structure_type != key_0 )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
      INSERT_BYTES fj_position key_3
      WRITE_ASCIIE fj_position ~%value1%~
      SET fj_position += key_3
    END ELSE
    PATCH_IF( key_0 == 0xc0 && fj_structure_type != key_0 )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reinserting %value% number %key1%.~ END
      INSERT_BYTES fj_position key_3
      WRITE_ASCIIE fj_position ~%value1%~
      SET fj_position += key_3
    END
  END // PHP_EACH $EVAL ~are_%value%~

  // add new structure
  PATCH_IF( key_0 == fj_structure_type && fj_delete_mode == ` 0 && key_0 != 0x78 )BEGIN
    PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding new %value% structure.~ END
    PATCH_IF( key_2 == 0x02 )BEGIN
      WRITE_SHORT key_1 THIS + 0x01
    END ELSE PATCH_IF( key_2 == 0x04 )BEGIN
      WRITE_LONG  key_1 THIS + 0x01
    END
    SET fj_return_offset = fj_position
    INSERT_BYTES fj_position key_3
    SET fj_position += key_3

    // actor
    PATCH_IF( fj_structure_type == 0x54 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~
      WRITE_SHORT  fj_return_offset + 0x20 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x22 fj_loc_y
      WRITE_SHORT  fj_return_offset + 0x24 fj_dest_x
      WRITE_SHORT  fj_return_offset + 0x26 fj_dest_y
      WRITE_LONG   fj_return_offset + 0x28 fj_loading
      WRITE_LONG   fj_return_offset + 0x2c fj_spawned
      WRITE_LONG   fj_return_offset + 0x30 fj_animation
      WRITE_LONG   fj_return_offset + 0x34 fj_orientation
      WRITE_LONG   fj_return_offset + 0x38 fj_expiry
      WRITE_SHORT  fj_return_offset + 0x3c fj_wander_dist_actor
      WRITE_SHORT  fj_return_offset + 0x3e fj_mvmt_dist_actor
      WRITE_LONG   fj_return_offset + 0x40 fj_schedule
      WRITE_LONG   fj_return_offset + 0x44 fj_num_talked
      WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_dlg_resref%~
      WRITE_ASCIIE fj_return_offset + 0x50 ~%fj_bcs_override%~
      WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_bcs_general%~
      WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_bcs_class%~
      WRITE_ASCIIE fj_return_offset + 0x68 ~%fj_bcs_race%~
      WRITE_ASCIIE fj_return_offset + 0x70 ~%fj_bcs_default%~
      WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_bcs_specific%~
      WRITE_ASCIIE fj_return_offset + 0x80 ~%fj_cre_resref%~
    END ELSE

    // region
    PATCH_IF( fj_structure_type == 0x5c )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_SHORT  fj_return_offset + 0x20 fj_type
      WRITE_SHORT  fj_return_offset + 0x22 fj_box_left
      WRITE_SHORT  fj_return_offset + 0x24 fj_box_top
      WRITE_SHORT  fj_return_offset + 0x26 fj_box_right
      WRITE_SHORT  fj_return_offset + 0x28 fj_box_bottom
      WRITE_LONG   fj_return_offset + 0x34 fj_cursor_idx
      WRITE_ASCIIE fj_return_offset + 0x38 ~%fj_destination_area%~ #8
      WRITE_ASCIIE fj_return_offset + 0x40 ~%fj_destination_name%~ #32
      WRITE_LONG   fj_return_offset + 0x60 fj_flags
      WRITE_LONG   fj_return_offset + 0x64 fj_info_point_strref
      WRITE_SHORT  fj_return_offset + 0x68 fj_trap_detect
      WRITE_SHORT  fj_return_offset + 0x6a fj_trap_remove
      WRITE_SHORT  fj_return_offset + 0x6c fj_trap_active
      WRITE_SHORT  fj_return_offset + 0x6e fj_trap_status
      WRITE_SHORT  fj_return_offset + 0x70 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x72 fj_loc_y
      WRITE_ASCIIE fj_return_offset + 0x74 ~%fj_key_resref%~ #8
      WRITE_ASCIIE fj_return_offset + 0x7c ~%fj_reg_script%~ #8
      WRITE_SHORT  fj_return_offset + 0x84 fj_alt_x
      WRITE_SHORT  fj_return_offset + 0x86 fj_alt_y
      PHP_EACH fj_vertex AS key1 => value1 BEGIN
        WRITE_SHORT fj_return_offset + 0x2a THIS + 0x01
      END
    END ELSE

    // spawn
    PATCH_IF( fj_structure_type == 0x60 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_SHORT  fj_return_offset + 0x20 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x22 fj_loc_y
      WRITE_ASCIIE fj_return_offset + 0x24 ~%fj_cre_resref0%~ #8
      WRITE_ASCIIE fj_return_offset + 0x2c ~%fj_cre_resref1%~ #8
      WRITE_ASCIIE fj_return_offset + 0x34 ~%fj_cre_resref2%~ #8
      WRITE_ASCIIE fj_return_offset + 0x3c ~%fj_cre_resref3%~ #8
      WRITE_ASCIIE fj_return_offset + 0x44 ~%fj_cre_resref4%~ #8
      WRITE_ASCIIE fj_return_offset + 0x4c ~%fj_cre_resref5%~ #8
      WRITE_ASCIIE fj_return_offset + 0x54 ~%fj_cre_resref6%~ #8
      WRITE_ASCIIE fj_return_offset + 0x5c ~%fj_cre_resref7%~ #8
      WRITE_ASCIIE fj_return_offset + 0x64 ~%fj_cre_resref8%~ #8
      WRITE_ASCIIE fj_return_offset + 0x6c ~%fj_cre_resref9%~ #8
      WRITE_SHORT  fj_return_offset + 0x74 fj_spawn_num
      WRITE_SHORT  fj_return_offset + 0x76 fj_difficulty
      WRITE_SHORT  fj_return_offset + 0x78 fj_delay
      WRITE_SHORT  fj_return_offset + 0x7a fj_method
      WRITE_LONG   fj_return_offset + 0x7c fj_duration
      WRITE_SHORT  fj_return_offset + 0x80 fj_wander_distance
      WRITE_SHORT  fj_return_offset + 0x82 fj_mvmt_distance
      WRITE_SHORT  fj_return_offset + 0x84 fj_max_num
      WRITE_SHORT  fj_return_offset + 0x86 fj_enable
      WRITE_LONG   fj_return_offset + 0x88 fj_schedule
      WRITE_SHORT  fj_return_offset + 0x8c fj_day_prob
      WRITE_SHORT  fj_return_offset + 0x8e fj_night_prob
    END ELSE

    // entrance
    PATCH_IF( fj_structure_type == 0x68 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_SHORT  fj_return_offset + 0x20 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x22 fj_loc_y
      WRITE_SHORT  fj_return_offset + 0x24 fj_orientation
    END ELSE

    // container
    PATCH_IF( fj_structure_type == 0x70 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_SHORT  fj_return_offset + 0x20 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x22 fj_loc_y
      WRITE_SHORT  fj_return_offset + 0x24 fj_type
      WRITE_SHORT  fj_return_offset + 0x26 fj_lock_diff
      WRITE_LONG   fj_return_offset + 0x28 fj_flags
      WRITE_SHORT  fj_return_offset + 0x2c fj_trap_detect
      WRITE_SHORT  fj_return_offset + 0x2e fj_trap_remove_diff
      WRITE_SHORT  fj_return_offset + 0x30 fj_trap_active
      WRITE_SHORT  fj_return_offset + 0x32 fj_trap_status
      WRITE_SHORT  fj_return_offset + 0x34 fj_trap_loc_x
      WRITE_SHORT  fj_return_offset + 0x36 fj_trap_loc_y
      WRITE_SHORT  fj_return_offset + 0x38 fj_box_left
      WRITE_SHORT  fj_return_offset + 0x3a fj_box_top
      WRITE_SHORT  fj_return_offset + 0x3c fj_box_right
      WRITE_SHORT  fj_return_offset + 0x3e fj_box_bottom
      WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_trap_script%~ #8
      WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_key_resref%~ #8
      WRITE_LONG   fj_return_offset + 0x84 fj_lockpick_strref
      PHP_EACH fj_vertex AS key1 => value1 BEGIN
        WRITE_LONG fj_return_offset + 0x54 THIS + 0x01
      END
    END ELSE

    // ambient
    PATCH_IF( fj_structure_type == 0x84 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_SHORT  fj_return_offset + 0x20 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x22 fj_loc_y
      WRITE_SHORT  fj_return_offset + 0x24 fj_radius
      WRITE_SHORT  fj_return_offset + 0x26 fj_loc_z
      WRITE_SHORT  fj_return_offset + 0x2e fj_volume
      WRITE_ASCIIE fj_return_offset + 0x30 ~%fj_wav_resref0%~ #8
      WRITE_ASCIIE fj_return_offset + 0x38 ~%fj_wav_resref1%~ #8
      WRITE_ASCIIE fj_return_offset + 0x40 ~%fj_wav_resref2%~ #8
      WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_wav_resref3%~ #8
      WRITE_ASCIIE fj_return_offset + 0x50 ~%fj_wav_resref4%~ #8
      WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_wav_resref5%~ #8
      WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_wav_resref6%~ #8
      WRITE_ASCIIE fj_return_offset + 0x68 ~%fj_wav_resref7%~ #8
      WRITE_ASCIIE fj_return_offset + 0x70 ~%fj_wav_resref8%~ #8
      WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_wav_resref9%~ #8
      WRITE_SHORT  fj_return_offset + 0x80 fj_sound_num
      WRITE_LONG   fj_return_offset + 0x84 fj_delay
      WRITE_LONG   fj_return_offset + 0x88 fj_variation
      WRITE_LONG   fj_return_offset + 0x8c fj_schedule
      WRITE_LONG   fj_return_offset + 0x90 fj_flags
    END ELSE

    // variable
    PATCH_IF( fj_structure_type == 0x88 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_LONG   fj_return_offset + 0x28 fj_variable_value
    END ELSE

    // door
    PATCH_IF( fj_structure_type == 0xa8 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_ASCIIE fj_return_offset + 0x20 ~%fj_door_wed_id%~
      WRITE_LONG   fj_return_offset + 0x28 fj_flags
      WRITE_SHORT  fj_return_offset + 0x38 fj_open_box_left
      WRITE_SHORT  fj_return_offset + 0x3a fj_open_box_top
      WRITE_SHORT  fj_return_offset + 0x3c fj_open_box_right
      WRITE_SHORT  fj_return_offset + 0x3e fj_open_box_bottom
      WRITE_SHORT  fj_return_offset + 0x40 fj_closed_box_left
      WRITE_SHORT  fj_return_offset + 0x42 fj_closed_box_top
      WRITE_SHORT  fj_return_offset + 0x44 fj_closed_box_right
      WRITE_SHORT  fj_return_offset + 0x46 fj_closed_box_bottom
      WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_door_open_wav%~
      WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_door_close_wav%~
      WRITE_LONG   fj_return_offset + 0x68 fj_cursor_idx
      WRITE_SHORT  fj_return_offset + 0x6c fj_trap_detect
      WRITE_SHORT  fj_return_offset + 0x6e fj_trap_remove
      WRITE_SHORT  fj_return_offset + 0x70 fj_trap_active
      WRITE_SHORT  fj_return_offset + 0x72 fj_trap_status
      WRITE_SHORT  fj_return_offset + 0x74 fj_trap_loc_x
      WRITE_SHORT  fj_return_offset + 0x76 fj_trap_loc_y
      WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_key_resref%~
      WRITE_ASCIIE fj_return_offset + 0x80 ~%fj_door_script%~
      WRITE_LONG   fj_return_offset + 0x88 fj_detect_diff
      WRITE_LONG   fj_return_offset + 0x8c fj_locked_diff
      WRITE_SHORT  fj_return_offset + 0x90 fj_open_loc_x
      WRITE_SHORT  fj_return_offset + 0x92 fj_open_loc_y
      WRITE_SHORT  fj_return_offset + 0x94 fj_closed_loc_x
      WRITE_SHORT  fj_return_offset + 0x96 fj_closed_loc_y
      WRITE_LONG   fj_return_offset + 0x98 fj_lockpick_strref
      WRITE_ASCIIE fj_return_offset + 0x9c ~%fj_travel_trigger%~ #24
      WRITE_LONG   fj_return_offset + 0xb4 fj_dlg_strref
      WRITE_ASCIIE fj_return_offset + 0xb8 ~%fj_dlg_resref%~
      PHP_EACH fj_door_open_vert   AS key1 => value1 BEGIN
        WRITE_SHORT fj_return_offset + 0x30 THIS + 0x01
      END
      PHP_EACH fj_door_closed_vert AS key1 => value1 BEGIN
        WRITE_SHORT fj_return_offset + 0x32 THIS + 0x01
      END
      PHP_EACH fj_cell_open_vert   AS key1 => value1 BEGIN
        WRITE_SHORT fj_return_offset + 0x4c THIS + 0x01
      END
      PHP_EACH fj_cell_closed_vert AS key1 => value1 BEGIN
        WRITE_SHORT fj_return_offset + 0x4e THIS + 0x01
      END
    END ELSE

    // animation
    PATCH_IF( fj_structure_type == 0xb0 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_SHORT  fj_return_offset + 0x20 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x22 fj_loc_y
      WRITE_LONG   fj_return_offset + 0x24 fj_schedule
      WRITE_ASCIIE fj_return_offset + 0x28 ~%fj_bam_resref%~ #8
      WRITE_SHORT  fj_return_offset + 0x30 fj_bam_seq
      WRITE_SHORT  fj_return_offset + 0x32 fj_bam_frame
      WRITE_LONG   fj_return_offset + 0x34 fj_flags
      WRITE_SHORT  fj_return_offset + 0x38 fj_loc_z
      WRITE_SHORT  fj_return_offset + 0x3a fj_transparent
      WRITE_SHORT  fj_return_offset + 0x3c fj_init_frame
      WRITE_BYTE   fj_return_offset + 0x3e fj_loop_chance
      WRITE_BYTE   fj_return_offset + 0x3f fj_skip_cycles
      WRITE_ASCIIE fj_return_offset + 0x40 ~%fj_bmp_resref%~ #8
    END ELSE

    // bitmask
    PATCH_IF( fj_structure_type == 0xa0 )BEGIN
      PATCH_IF( FILE_EXISTS ~%fj_bitmask%~ )BEGIN
        SET key1 = BUFFER_LENGTH
        APPEND_FILE_EVALUATE ~%fj_bitmask%~
        WRITE_LONG 0x9c BUFFER_LENGTH - key1
      END ELSE BEGIN
        WRITE_LONG 0x9c 0x00
      END
      SET fj_position += LONG_AT 0x9c
    END ELSE

    // songs
    PATCH_IF( fj_structure_type == 0xbc )BEGIN
      WRITE_LONG   fj_return_offset + 0x00 fj_song_day
      WRITE_LONG   fj_return_offset + 0x04 fj_song_night
      WRITE_LONG   fj_return_offset + 0x08 fj_song_victory
      WRITE_LONG   fj_return_offset + 0x0c fj_song_battle
      WRITE_LONG   fj_return_offset + 0x10 fj_song_defeat
      WRITE_LONG   fj_return_offset + 0x14 0xffffffff
      WRITE_LONG   fj_return_offset + 0x18 0xffffffff
      WRITE_LONG   fj_return_offset + 0x1c 0xffffffff
      WRITE_LONG   fj_return_offset + 0x20 0xffffffff
      WRITE_LONG   fj_return_offset + 0x24 0xffffffff
      WRITE_ASCIIE fj_return_offset + 0x28 ~%fj_song_day0%~ #8
      WRITE_ASCIIE fj_return_offset + 0x30 ~%fj_song_day1%~ #8
      WRITE_LONG   fj_return_offset + 0x38 fj_song_day_vol
      WRITE_ASCIIE fj_return_offset + 0x3c ~%fj_song_night0%~ #8
      WRITE_ASCIIE fj_return_offset + 0x44 ~%fj_song_night1%~ #8
      WRITE_LONG   fj_return_offset + 0x4c fj_song_night_vol
      WRITE_LONG   fj_return_offset + 0x50 fj_song_reverb
    END ELSE

    // rest interrupts
    PATCH_IF( fj_structure_type == 0xc0 )BEGIN
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #32
      WRITE_LONG   fj_return_offset + 0x20 fj_cre_strref0
      WRITE_LONG   fj_return_offset + 0x24 fj_cre_strref1
      WRITE_LONG   fj_return_offset + 0x28 fj_cre_strref2
      WRITE_LONG   fj_return_offset + 0x2c fj_cre_strref3
      WRITE_LONG   fj_return_offset + 0x30 fj_cre_strref4
      WRITE_LONG   fj_return_offset + 0x34 fj_cre_strref5
      WRITE_LONG   fj_return_offset + 0x38 fj_cre_strref6
      WRITE_LONG   fj_return_offset + 0x3c fj_cre_strref7
      WRITE_LONG   fj_return_offset + 0x40 fj_cre_strref8
      WRITE_LONG   fj_return_offset + 0x44 fj_cre_strref9
      WRITE_ASCIIE fj_return_offset + 0x48 ~%fj_cre_resref0%~ #8
      WRITE_ASCIIE fj_return_offset + 0x50 ~%fj_cre_resref1%~ #8
      WRITE_ASCIIE fj_return_offset + 0x58 ~%fj_cre_resref2%~ #8
      WRITE_ASCIIE fj_return_offset + 0x60 ~%fj_cre_resref3%~ #8
      WRITE_ASCIIE fj_return_offset + 0x68 ~%fj_cre_resref4%~ #8
      WRITE_ASCIIE fj_return_offset + 0x70 ~%fj_cre_resref5%~ #8
      WRITE_ASCIIE fj_return_offset + 0x78 ~%fj_cre_resref6%~ #8
      WRITE_ASCIIE fj_return_offset + 0x80 ~%fj_cre_resref7%~ #8
      WRITE_ASCIIE fj_return_offset + 0x88 ~%fj_cre_resref8%~ #8
      WRITE_ASCIIE fj_return_offset + 0x90 ~%fj_cre_resref9%~ #8
      WRITE_SHORT  fj_return_offset + 0x98 fj_spawn_num
      WRITE_SHORT  fj_return_offset + 0x9a fj_difficulty
      WRITE_LONG   fj_return_offset + 0x9c fj_duration
      WRITE_SHORT  fj_return_offset + 0xa0 fj_wander_distance
      WRITE_SHORT  fj_return_offset + 0xa2 fj_mvmt_distance
      WRITE_SHORT  fj_return_offset + 0xa4 fj_max_num
      WRITE_SHORT  fj_return_offset + 0xa6 fj_enable
      WRITE_SHORT  fj_return_offset + 0xa8 fj_day_prob
      WRITE_SHORT  fj_return_offset + 0xaa fj_night_prob
    END ELSE

    // map note (BGII)
    PATCH_IF( fj_structure_type == 0xc4 )BEGIN
      WRITE_SHORT  fj_return_offset + 0x00 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x02 fj_loc_y
      WRITE_LONG   fj_return_offset + 0x04 fj_note_strref
      WRITE_SHORT  fj_return_offset + 0x08 fj_strref_loc
      WRITE_SHORT  fj_return_offset + 0x0a fj_color
      WRITE_LONG   fj_return_offset + 0x0c fj_note_id
    END ELSE

    // map note (PST)
    PATCH_IF( fj_structure_type == 0xc8 )BEGIN
      WRITE_LONG   fj_return_offset + 0x000 fj_loc_x
      WRITE_LONG   fj_return_offset + 0x004 fj_loc_y
      WRITE_ASCIIE fj_return_offset + 0x008 ~%fj_note_text%~
      WRITE_LONG   fj_return_offset + 0x1fc fj_color
    END ELSE

    // embedded projectile
    PATCH_IF( fj_structure_type == 0xcc )BEGIN
      PATCH_IF(  fj_missile_num == ` 0 )BEGIN
        SET fj_missile_num = IDS_OF_SYMBOL ( projectl ~%fj_name%~ )
        PATCH_IF( fj_missile_num > ` 0 )BEGIN
          SET fj_missile_num -= 0x01
        END
      END
      WRITE_ASCIIE fj_return_offset + 0x00 ~%fj_name%~ #8
      WRITE_SHORT  fj_return_offset + 0x0e fj_missile_num
      WRITE_SHORT  fj_return_offset + 0x10 fj_frequency
      WRITE_SHORT  fj_return_offset + 0x12 fj_duration
      WRITE_SHORT  fj_return_offset + 0x14 fj_loc_x
      WRITE_SHORT  fj_return_offset + 0x16 fj_loc_y
      WRITE_SHORT  fj_return_offset + 0x18 fj_loc_z
      WRITE_SHORT  fj_return_offset + 0x1a fj_target
    END

  END

  // add items and index to their containers
  PATCH_IF( key_0 == 0x78 )BEGIN
    PHP_EACH are_container AS num => structure BEGIN
      PATCH_IF( fj_structure_type == 0x70 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
        SET fj_deleted = 0x01
      END ELSE BEGIN
        WRITE_LONG LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x40 fj_itm_idx
        PHP_EACH ~are_container_%num%_itm~ AS num1 => value1 BEGIN
          PATCH_IF(
            ( fj_structure_type != 0x78 ) ||
            ( fj_delete_mode != num1 + LONG_AT ( LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x40 ) )
          )BEGIN
            PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating item %num1% to container %num%.~ END
            WRITE_LONG LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x44 THIS + 0x01
            INSERT_BYTES fj_position 0x14
            WRITE_ASCIIE fj_position ~%value1%~
            WRITE_SHORT  0x76 THIS + 0x01
            SET ++ fj_itm_idx
            SET fj_position += key_3
          END
        END
      END
      PATCH_IF( fj_con_itm_idx == num && fj_structure_type == 0x78 )BEGIN
        READ_LONG LONG_AT 0x70 + 0xc0 * num + 0x44 cnt
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding new item to container %num%.~ END 
        WRITE_LONG LONG_AT 0x70 + 0xc0 * num + 0x44 THIS + 0x01
        WRITE_SHORT  0x76 THIS + 0x01
        INSERT_BYTES fj_position key_3
        WRITE_ASCIIE fj_position + 0x00 ~%fj_name%~ #8
        WRITE_SHORT  fj_position + 0x08 fj_itm_expiry
        WRITE_SHORT  fj_position + 0x0a fj_charge0
        WRITE_SHORT  fj_position + 0x0c fj_charge1
        WRITE_SHORT  fj_position + 0x0e fj_charge2
        WRITE_LONG   fj_position + 0x10 fj_flags
        SET ++ fj_itm_idx
        SET fj_position  += key_3
      END
    END // php $are_container
    SET fj_deleted = 0x00
    PHP_EACH are_container AS key => value BEGIN
      CLEAR_ARRAY EVAL ~are_container_%key%_itm~
    END
  END ELSE

  // add vertices
  PATCH_IF( key_0 == 0x7c )BEGIN

    // vertices associated with regions
    PHP_EACH are_region    AS num => structure BEGIN
      PATCH_IF( fj_structure_type == 0x5c && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
        SET fj_deleted = 0x01
      END ELSE BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating vertices to region %num%.~ END
        WRITE_LONG LONG_AT 0x5c + 0xc4 * ( num - fj_deleted ) + 0x2c fj_vertex_idx
        PHP_EACH ~are_region_%num%_vertex~ AS num1 => value1 BEGIN
          INSERT_BYTES fj_position 0x04
          WRITE_LONG   fj_position value1
          WRITE_SHORT  0x80        THIS + 0x01
          SET ++fj_vertex_idx
          SET fj_position += key_3
        END
      END
    END
    SET fj_deleted = 0x00
    PATCH_IF( fj_structure_type == 0x5c && fj_delete_mode == ` 0 )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding vertices to new region.~ END
      WRITE_LONG fj_return_offset + 0x2c fj_vertex_idx
      PHP_EACH fj_vertex AS num => off BEGIN
        INSERT_BYTES fj_position 0x04
        WRITE_SHORT  0x80        THIS + 0x01
        SET ++fj_vertex_idx
      END
      PHP_EACH fj_vertex AS num => off BEGIN
        WRITE_LONG fj_position off
        SET fj_position += 0x04
      END
    END
    PHP_EACH are_region    AS num => structure BEGIN
      CLEAR_ARRAY EVAL ~are_region_%num%_vertex~
    END

    // vertices associated with containers
    PHP_EACH are_container AS num => structure BEGIN
      PATCH_IF( fj_structure_type == 0x70 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
        SET fj_deleted = 0x01
      END ELSE BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating vertices to container %num%.~ END
        WRITE_LONG LONG_AT 0x70 + 0xc0 * ( num - fj_deleted ) + 0x50 fj_vertex_idx
        PHP_EACH ~are_container_%num%_vertex~ AS num1 => value1 BEGIN
          INSERT_BYTES fj_position 0x04
          WRITE_LONG   fj_position value1
          WRITE_SHORT  0x80        THIS + 0x01
          SET ++fj_vertex_idx
          SET fj_position += 0x04
        END
      END
    END
    SET fj_deleted = 0x00
    PATCH_IF fj_structure_type == 0x70 && fj_delete_mode == ` 0 BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding vertices to new container.~ END
      WRITE_LONG fj_return_offset + 0x50 fj_vertex_idx
      PHP_EACH fj_vertex AS num => off BEGIN
        SET ++fj_vertex_idx
        INSERT_BYTES fj_position 0x04
        WRITE_SHORT  0x80 THIS + 0x01
      END
      PHP_EACH fj_vertex AS num => off BEGIN
        WRITE_LONG fj_position off
        SET fj_position += 0x04
      END
    END
    PHP_EACH are_container AS num => structure BEGIN
      CLEAR_ARRAY EVAL ~are_container_%num%_vertex~
    END

    // vertices associated with doors
    PHP_EACH are_door      AS num => structure BEGIN
      PATCH_IF( fj_structure_type == 0xa8 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
        SET fj_deleted = 0x01
      END ELSE BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating vertices to door %num%.~ END
        WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x2c fj_vertex_idx
        PHP_EACH ~are_door_open_%num%_vertex~ AS num1 => value1 BEGIN
          INSERT_BYTES fj_position 0x04
          WRITE_LONG   fj_position value1
          SET ++fj_vertex_idx
          SET fj_position += 0x04
          WRITE_SHORT  0x80 THIS + 0x01
        END
        WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x34 fj_vertex_idx
        PHP_EACH ~are_door_closed_%num%_vertex~ AS num1 => value1 BEGIN
          INSERT_BYTES fj_position 0x04
          WRITE_LONG   fj_position value1
          SET ++fj_vertex_idx
          SET fj_position += 0x04
          WRITE_SHORT  0x80 THIS + 0x01
        END
        WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x48 fj_vertex_idx
        PHP_EACH ~are_cell_open_%num%_vertex~ AS num1 => value1 BEGIN
          INSERT_BYTES fj_position 0x04
          WRITE_LONG   fj_position value1
          SET ++fj_vertex_idx
          SET fj_position += 0x04
          WRITE_SHORT  0x80 THIS + 0x01
        END
        WRITE_LONG LONG_AT 0xa8 + 0xc8 * ( num - fj_deleted ) + 0x50 fj_vertex_idx
        PHP_EACH ~are_cell_closed_%num%_vertex~ AS num1 => value1 BEGIN
          INSERT_BYTES fj_position 0x04
          WRITE_LONG   fj_position value1
          SET ++fj_vertex_idx
          SET fj_position += 0x04
          WRITE_SHORT  0x80 THIS + 0x01
        END
      END
    END
    SET fj_deleted = 0x00
    PATCH_FOR_EACH value1 IN
      door_open door_closed cell_open cell_closed
    BEGIN
      CLEAR_ARRAY EVAL ~are_%value1%_%num%_vertex~
    END
    PATCH_IF( fj_structure_type == 0xa8 && fj_delete_mode == ` 0 )BEGIN
      PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding vertices to new door.~ END
      WRITE_LONG fj_return_offset + 0x2c fj_vertex_idx
      WRITE_LONG fj_return_offset + 0x34
        LONG_AT (fj_return_offset + 0x2c) + SHORT_AT (fj_return_offset + 0x30)
      WRITE_LONG fj_return_offset + 0x48
        LONG_AT (fj_return_offset + 0x34) + SHORT_AT (fj_return_offset + 0x32)
      WRITE_LONG fj_return_offset + 0x50
        LONG_AT (fj_return_offset + 0x48) + SHORT_AT (fj_return_offset + 0x4c)
      PATCH_FOR_EACH vertex_type IN
        door_open door_closed cell_open cell_closed
      BEGIN
        PHP_EACH ~fj_%vertex_type%_vert~ AS num => off BEGIN
          INSERT_BYTES fj_position 0x04
          WRITE_SHORT  0x80 THIS + 0x01
        END
        PHP_EACH ~fj_%vertex_type%_vert~ AS num => off BEGIN
          WRITE_LONG fj_position off
          SET fj_position += 0x04
        END
        CLEAR_ARRAY EVAL ~fj_%vertex_type%_vert~
      END
    END

  END // adding vertices

  // reinsert embedded creatures
  PATCH_IF( key_0 == 0x54 )BEGIN
    PHP_EACH are_embedded_cre AS num => value1 BEGIN
      PATCH_IF( fj_structure_type == 0x54 && num == fj_delete_mode && fj_deleted == 0x00 )BEGIN
        SET fj_deleted = 0x01
      END ELSE BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating embedded creature to actor %num%.~ END
        WRITE_LONG   LONG_AT 0x54 + ( num - fj_deleted ) * 0x110 + 0x88 fj_position
        INSERT_BYTES fj_position LONG_AT ( LONG_AT 0x54 + ( num - fj_deleted ) * 0x110 + 0x8c )
        WRITE_ASCIIE fj_position ~%value1%~
        SET fj_position += LONG_AT (LONG_AT 0x54 + ( num - fj_deleted ) * 0x110 + 0x8c)
      END
    END
    CLEAR_ARRAY are_embedded_cre
    SET fj_deleted = 0x00
    PATCH_IF( fj_structure_type == 0x54 && fj_delete_mode == ` 0 && !( fj_loading & 0x01 ) )BEGIN
      PATCH_IF( FILE_EXISTS ~%fj_cre_embedded%~ )BEGIN
        SET off = BUFFER_LENGTH // we do a stupid dance here to avoid INNER_ACTION
        APPEND_FILE_EVALUATE ~%fj_cre_embedded%~
        READ_ASCII   off fj_cre_embedded ( BUFFER_LENGTH - off )
        DELETE_BYTES off STRING_LENGTH EVAL ~%fj_cre_embedded%~
      END ELSE PATCH_IF( FILE_EXISTS_IN_GAME ~%fj_cre_resref%.cre~ ) BEGIN
        INNER_PATCH_FILE ~%fj_cre_resref%.cre~ BEGIN
          READ_ASCII 0x00 fj_cre_embedded ( BUFFER_LENGTH )
        END
      END
      PATCH_IF( ~%fj_cre_embedded%~ STR_CMP ~~ )BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Embedding creature to new actor.~ END
        WRITE_LONG   fj_return_offset + 0x88 fj_position
        WRITE_LONG   fj_return_offset + 0x8c STRING_LENGTH EVAL ~%fj_cre_embedded%~
        INSERT_BYTES fj_position             LONG_AT (fj_return_offset + 0x8c)
        WRITE_ASCIIE fj_position             ~%fj_cre_embedded%~
        SET fj_position += LONG_AT ( fj_return_offset + 0x8c )
      END ELSE BEGIN
        WRITE_LONG   fj_return_offset + 0x28 THIS | 0x01 // if we didn't find a .cre, mark it unembedded
      END
    END
  END ELSE

  // reinsert embedded projectile effects
  PATCH_IF( key_0 == 0xcc )BEGIN
    PHP_EACH are_embedded_eff AS num => value1 BEGIN
      PATCH_IF( fj_structure_type == 0xcc && fj_delete_mode == num && fj_deleted == 0x00 )BEGIN
        SET fj_deleted = 0x01
      END ELSE BEGIN
        PATCH_IF fj_debug BEGIN PATCH_PRINT ~Reassociating embedded effects to projectile %num%.~ END
        WRITE_LONG   LONG_AT 0xcc + ( num - fj_deleted ) * 0x1c + 0x08 fj_position
        INSERT_BYTES fj_position SHORT_AT (LONG_AT 0xcc + ( num - fj_deleted ) * 0x1c + 0x0c)
        WRITE_ASCIIE fj_position ~%value1%~
        SET fj_position += SHORT_AT (LONG_AT 0xcc + ( num - fj_deleted ) * 0x1c + 0x0c)
      END
    END
    SET fj_deleted = 0x00
    CLEAR_ARRAY are_embedded_eff
    PATCH_IF( fj_structure_type == 0xcc && fj_delete_mode == ` 0 )BEGIN
      WRITE_LONG fj_return_offset + 0x08 fj_position
      PHP_EACH fj_embedded_eff AS num => value1 BEGIN
        PATCH_IF( FILE_EXISTS ~%value1%~ )BEGIN
          SET off = BUFFER_LENGTH
          APPEND_FILE_EVALUATE ~%value1%~
          READ_ASCII   off + 0x08 value1 (0x108)
          DELETE_BYTES off 0x110
        END ELSE PATCH_IF( FILE_EXISTS_IN_GAME ~%value1%.eff~ )BEGIN
          INNER_PATCH_FILE ~%value1%.eff~ BEGIN
            READ_ASCII 0x08 value1 (0x108)
          END
        END ELSE BEGIN
          TEXT_SPRINT value1 ~~
        END
        PATCH_IF( ~%value1%~ STR_CMP ~~ )BEGIN
          PATCH_IF fj_debug BEGIN PATCH_PRINT ~Adding effect %num% to new embedded projectile.~ END
          WRITE_SHORT  fj_return_offset + 0x0c THIS + 0x108
          INSERT_BYTES fj_position 0x108
          WRITE_ASCIIE fj_position ~%value1%~
          SET fj_position += 0x108
        END
      END
    END
    CLEAR_ARRAY fj_embedded_eff
  END

END // php $struct: everything added

// restoring Icewind Dale II's special snowflakiness
PATCH_IF is_id2 BEGIN
  PATCH_FOR_EACH off IN
    0x54 0x5c 0x60 0x68 0x70 0x78 0x7c 0x84
    0x88 0xa0 0xa8 0xb0 0xb8 0xbc 0xc0
  BEGIN
    PATCH_IF LONG_AT off BEGIN
      WRITE_LONG off THIS + 0x10
    END
  END
  INSERT_BYTES 0x54 0x10
  WRITE_ASCIIE 0x54 ~%id2_header%~
  SET fj_return_offset += 0x10
  GET_OFFSET_ARRAY fj_id_actor 0x54 0x04 0x58 0x02 0 0 0x110
  PHP_EACH fj_id_actor AS key => value BEGIN
    PATCH_IF( 
      LONG_AT( value + 0x88 ) > 0x00 &&
      LONG_AT( value + 0x28 ) & 0x01 == 0x00 
    )BEGIN
      WRITE_LONG value + 0x88 THIS + 0x10
    END
  END
END

END

// EOF


#9 -Guest-

-Guest-
  • Guest

Posted 09 October 2010 - 07:02 AM

i can't even ctrl-c today without typos, lovely.

GET_OFFSET_ARRAY fj_id_actor 0x54 0x04 0x58 0x02 0 0 0x110

must be

GET_OFFSET_ARRAY fj_id_actor 0x64 0x04 0x58 0x02 0 0 0x110

#10 cmorgan

cmorgan
  • Modder
  • 2301 posts

Posted 09 October 2010 - 09:37 AM

Testing it out now - will update. Trying to read between this and NI and DLTCEP and Miloch's stuff in Auroura and Y's stuff in FFT and throwing myself headlong... into removing the info trigger on this "door" (ABANDON02) in AR0700 ar0700_door.PNG and replicating it as a functional travel trigger for a custom area. Fun stuff! Makes my head hurt.


EDIT: confirmed - doing this works as expected, and in-game test as well as recheck via DLTCEP and NI shows this working just fine.

/* Existing Promenade edits */
COPY_EXISTING ar0700.are override	
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 3183
	fj_loc_y 	= 983
	fj_orientation = 8 // s
	STR_VAR
	fj_structure_type = entrance
	fj_name = Exitc-ar01
 END

/* new area added */ 

/* delete original travel trigger */
COPY_EXISTING c-ar01.are override
 LPF fj_are_structure
	INT_VAR
	fj_delete_mode = 0 // (only one region)
	STR_VAR
	fj_structure_type = region
 END
BUT_ONLY

I am checking on whether or not the animation sets properly, though - I can't seem to apply the animations.

<<snip for lack-of-reading-the-full-text-before-trying-it-out...>>

results in the original noblewoman animation for the .cre in-game.

Edited by cmorgan, 09 October 2010 - 12:34 PM.


#11 cmorgan

cmorgan
  • Modder
  • 2301 posts

Posted 09 October 2010 - 10:43 AM

duh.. RTFM.

\item INT_VAR \verb+fj_animation+ to the actor's animation number (from animate.ids, though the engine uses the animation set on the .cre file);


OK, I'd better shelve this until tomorrow. The docs are clear, and I missed it completely.

#12 cmorgan

cmorgan
  • Modder
  • 2301 posts

Posted 09 October 2010 - 12:33 PM

... and it definitely works well. I can confirm patching and repatching over 14 successive variations and messing-abouts with no problems, no file corruption, you name it. BGMain 2010-10-09 15-18-27-35.png I am giving up on adding the door animation and linking it until I can figure out why the custom area doors are fine when open, but completely graphically bizzare and flickering when closed.


/* Existing Promenade edits */

/* delete original info trigger */
COPY_EXISTING ar0700.are override
 LPF fj_are_structure
	INT_VAR
	fj_delete = 1 // give me feedback
	fj_delete_mode = 24 // (#25 in NI, but count starts at 0 not one...)
	STR_VAR
	fj_structure_type = region
	fj_type = 1 // info
 END
BUT_ONLY

/* add entrance target, add travel region back to c-ar01.are matching original info trigger */
COPY_EXISTING ar0700.are override	
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 3183
	fj_loc_y 	= 983
	fj_orientation = 0 // s
	STR_VAR
	fj_structure_type = entrance
	fj_name = Exitc-ar01
 END
 LPF fj_are_structure
	INT_VAR
	fj_type 	= 2	// travel
	fj_box_left 	= 3175
	fj_box_top 	= 857
	fj_box_right	= 3261
	fj_box_bottom = 919
	fj_cursor_idx = 30 // door
	fj_vertex_0 	= 3175 + (917 << 16)
	fj_vertex_1 	= 3261 + (919 << 16)
	fj_vertex_2 	= 3260 + (882 << 16)
	fj_vertex_3 	= 3255 + (871 << 16)
	fj_vertex_4 	= 3246 + (864 << 16)
	fj_vertex_5 	= 3226 + (858 << 16)
	fj_vertex_6 	= 3213 + (857 << 16)
	fj_vertex_7 	= 3189 + (863 << 16)
	fj_vertex_8 	= 3178 + (868 << 16)
	fj_vertex_9 	= 3175 + (883 << 16)
	STR_VAR
	fj_structure_type = region
	fj_name = Trancar01
	fj_destination_area = c-ar01
	fj_destination_name = Trancar01
 END

/* new area added */ 

COPY_EXISTING c-ar01.are override
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 1170
	fj_loc_y 	= 910
	fj_orientation = 7 
	STR_VAR
	fj_structure_type = entrance
	fj_name = Trancar01
 END
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 897
	fj_loc_y 	= 934
	fj_orientation	= 15 	// SSE
	STR_VAR
	fj_structure_type = actor
	fj_name	= c-test1
	fj_cre_resref = c-test1
 END
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 597
	fj_loc_y 	= 516
	fj_orientation	= 11 // NEE
	STR_VAR
	fj_structure_type = actor
	fj_name = c-test2
	fj_cre_resref 	= c-test2
 END
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 367
	fj_loc_y 	= 729
	fj_animation 	= 0x6110 // fighter female human
	fj_orientation	= 15 	//SSE
	STR_VAR
	fj_structure_type = actor
	fj_name = c-test3
	fj_cre_resref = c-test3
 END	
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 478
	fj_loc_y 	= 528
	fj_animation 	= 0x6110 // fighter female human
	fj_orientation	= 15 	//SSE
	STR_VAR
	fj_structure_type = actor
	fj_name 	= c-test4
	fj_cre_resref 	= c-test4
 END	
 LPF fj_are_structure
	INT_VAR
	fj_loc_x 	= 713
	fj_loc_y 	= 398
	fj_dest_x 	= 1128
	fj_dest_y 	= 926
	fj_orientation	= 15 	// SSE
	STR_VAR
	fj_structure_type = actor
	fj_name = c-test5
	fj_cre_resref = c-test5
 END

(blast. This works well enough that even I can use it... which means I guess I need to learn how to patch .wed and .tis files as well.)

Edited by cmorgan, 09 October 2010 - 01:43 PM.


#13 Miloch

Miloch

    Barbarian

  • Modder
  • 6573 posts

Posted 13 October 2010 - 06:53 AM

I guess I need to learn how to patch .wed and .tis files as well.)

You'll need to patch the .wed to have separate open/closed states for the door. There's an example of that in Gavin-BG1 for Beregost. There's no function for it yet though - maybe a job for "Guest" to do an fj_wed_structure function :).

I'm not sure if you can do separate open/closed states via adding a .bam but I suppose it's possible in theory. If it's flickering try messing with the flags on the animation - the nasty binary ones, like cover actors, visible in darkness, etc. I had to use different flags to get those to work in different areas - even ones that looked otherwise similar.

I think it'd be practically impossible to patch a .tis, hence why we need these workarounds with adding BAMs for doors etc. I tried it but DLTCEP stores imported BMPs using a different method than the game's, so I wasn't able to determine a working diff to patch it. Even importing the same identical .tis resulted in a completely different hexadecimal file.

Infinity Engine Contributions
Aurora * BG1 NPC * BG1 Fixpack * Haiass * Infinity Animations * Level 1 NPCs * P5Tweaks
PnP Free Action * Thrown Hammers * Unique Containers * BG:EE * BGII:EE * IWD:EE
================================================================
Player & Modder Resources
BAM Batcher * Creature Lister * Creature Checker * Creature Fixer * Tutu/BGT Area Map & List * Tutu Mod List
================================================================
"Infinity turns out to be the opposite of what people say it is. It is not 'that which has nothing beyond itself' that is infinite, but 'that which always has something beyond itself'." -Aristotle


#14 cmorgan

cmorgan
  • Modder
  • 2301 posts

Posted 17 December 2010 - 07:34 PM

Miloch, can you please re-reference that code? I can't find it. I am ok with it as is, but if here is a way of getting my brain around turning it into an actual door by editing in a .bam or setting an animation, I should probably try.

Follow up question, please-

Map note structure variables: \begin{itemize} \item INT_VAR \verb+fj_loc_x+ to the X coordinate*; \item INT_VAR \verb+fj_loc_y+ to the Y coordinate*;
[font="Verdana"]\item INT_VAR \verb+fj_note_strref+ to the note string reference (default -1, BGII only)*;
\item STR_VAR \verb+fj_note_text+ to the note text (PST only)*;

\item INT_VAR \verb+fj_strref_loc+ to the strref location (0=external, 1=dialog.tlk, default 1);

\item INT_VAR \verb+fj_color+ to the map marker color (0-7);
\item INT_VAR \verb+fj_note_id+ to the note ID;


I can't see a way of adding a not-yet-existing sttref. Unless it can take a tra ref - For BG2,

fj_loc_x = 3218
fj_loc_y = 898
fj_note_strref] = @9999 // setup.tra "The Broken Sword"
fj_strref_loc = 0
fj_color = 0
fj_note_id = // ??? no reference that I can find...

I can experiment, but if someone knows it will save a good bit of time. I test slowly!

Edited by cmorgan, 17 December 2010 - 09:08 PM.


#15 Mike1072

Mike1072
  • Modder
  • 539 posts

Posted 17 December 2010 - 08:54 PM

I can't see a way of adding a not-yet-existing sttref. Unless it can take a tra ref - For BG2,

fj_loc_x = 3218
fj_loc_y = 898
fj_note_strref = @9999 // setup.tra "The Broken Sword"
fj_strref_loc = 0
fj_color = 0
fj_note_id = // ??? no reference that I can find...

I can experiment, but if someone knows it will save a good bit of time. I test slowly!

The variable takes string references only. the_bigg added RESOLVE_STR_REF to handle this type of situation more easily.

fj_note_strref = RESOLVE_STR_REF (@9999)

You can put a tra reference or an actual string inside the brackets there, and RESOLVE_STR_REF will return the reference to that string from dialog.tlk (after adding it if need be).

#16 cmorgan

cmorgan
  • Modder
  • 2301 posts

Posted 17 December 2010 - 09:18 PM

Way cool - I read RESOLVE_STR_REF but obviously did not understand or connect the dots.

So for an un-traified mod (and for testing), I attempted
/* add entrance target, add travel region back to c-ar01.are matching original info trigger , and map note */
COPY_EXISTING ar0700.are override 
 /* LPF fj_are_structure
 INT_VAR
 fj_loc_x = 3216
 fj_loc_y = 927
 fj_note_strref = RESOLVE_STR_REF (~The Broken Sword~)
 fj_strref_loc = 0
 fj_color = 0
 END */
 LPF fj_are_structure
 INT_VAR
 fj_loc_x = 3183
 fj_loc_y = 983
 fj_orientation = 0 // s
 STR_VAR
 fj_structure_type = entrance
 fj_name = Exitc-ar01
 END
 LPF fj_are_structure
 INT_VAR
 fj_type = 2 // travel
 fj_box_left = 3175
 fj_box_top = 857
 fj_box_right = 3261
 fj_box_bottom = 919
 fj_cursor_idx = 30 // door
 fj_vertex_0 = 3175 + (917 << 16)
 fj_vertex_1 = 3261 + (919 << 16)
 fj_vertex_2 = 3260 + (882 << 16)
 fj_vertex_3 = 3255 + (871 << 16)
 fj_vertex_4 = 3246 + (864 << 16)
 fj_vertex_5 = 3226 + (858 << 16)
 fj_vertex_6 = 3213 + (857 << 16)
 fj_vertex_7 = 3189 + (863 << 16)
 fj_vertex_8 = 3178 + (868 << 16)
 fj_vertex_9 = 3175 + (883 << 16)
 STR_VAR
 fj_structure_type = region
 fj_name = Trancar01
 fj_destination_area = c-ar01
 fj_destination_name = Trancar01
 END

...with the comment in place, everything is fine. But when uncommented, I get

ERROR: cannot convert fj_structure_type or %fj_structure_type% to an integer
ERROR: [ar0700.are] -> [override] Patching Failed (COPY) (Not_found)
Stopping installation because of error.


I must be missing something. Hey - no defined fj_structure_type defined - i must not have understood the "universal structure variable" thing - adding

STR_VAR
fj_structure_type = note


so
 LPF fj_are_structure
 INT_VAR
 	fj_loc_x = 3216
 	fj_loc_y = 927
 	fj_note_strref = RESOLVE_STR_REF (~The Broken Sword~)
 	fj_strref_loc = 0
 	fj_color = 0
 STR_VAR
 	fj_structure_type = note
 END
and, success!

note_added.png

So, I just need to work out if I really, really want to make a working door, or if I am ok with just having the door cursor on mouseover.


(Thanks, Mike1072...)

Edited by cmorgan, 17 December 2010 - 09:38 PM.


#17 Miloch

Miloch

    Barbarian

  • Modder
  • 6573 posts

Posted 18 December 2010 - 06:25 PM

Hey - no defined fj_structure_type defined - i must not have understood the "universal structure variable" thing - adding

STR_VAR
fj_structure_type = note

Um yeah, I guess you figured out you need to STR_VAR that...

So, I just need to work out if I really, really want to make a working door, or if I am ok with just having the door cursor on mouseover.

I recommend the latter. It probably works just fine that way, and is not worth the effort of trying to patch in a door BAM that you can somehow toggle and patching the WED. All that might not even work, or could look crappy. However, if the door is not *visible* (because it's on the north side of a building for example) then you can get away with patching the WED to make an open/closeable (and lockable) door. See Gavin BG1 for an example of that (there isn't a function for it yet).

The latest version of the function is in the WeiDU source code (tph/include/fj_are_struct.tpa) and the latest documentation is in the WeiDU doc, along with examples for adding animations and other common structures.

Note you can also SPRINT 9999 @9999 and then fj_note_strref = %9999% or so I would guess anyway, but I guess RESOLVE_STR_REF is probably easier.

Infinity Engine Contributions
Aurora * BG1 NPC * BG1 Fixpack * Haiass * Infinity Animations * Level 1 NPCs * P5Tweaks
PnP Free Action * Thrown Hammers * Unique Containers * BG:EE * BGII:EE * IWD:EE
================================================================
Player & Modder Resources
BAM Batcher * Creature Lister * Creature Checker * Creature Fixer * Tutu/BGT Area Map & List * Tutu Mod List
================================================================
"Infinity turns out to be the opposite of what people say it is. It is not 'that which has nothing beyond itself' that is infinite, but 'that which always has something beyond itself'." -Aristotle


#18 cmorgan

cmorgan
  • Modder
  • 2301 posts

Posted 19 December 2010 - 07:24 AM

Yeah, I am done messing about. If it works, it works... and it works.

I'll take a look at the weidu docs on it - I then can strip out the macro from the .tp2 and just use the weidu distributed one.

I have to recheck, but so far, I haven't found any conflicting mods. I am a happy dude.

#19 cmorgan

cmorgan
  • Modder
  • 2301 posts

Posted 19 December 2010 - 09:23 AM

Hey, funny thought -

I use this code to remove an info trigger and add a replacement travel trigger. It assumes that the entry I want is 24.

I am pretty sure that as long as stuff is added (like Fishing for Trouble entrances/exits/etc.) everything will be fine, as the structures are added at the end of the sequence. I may need to check that.... but the big question is what if I (or another modder) wants to do the same thing "upstream"?


My mod looks for "Abandon02" (mode 24) and deletes it. Another mod wants to use "Abandon07" and do the same thing.

Aran:
/* delete original info trigger */
COPY_EXISTING ar0700.are override
 LPF fj_are_structure
 INT_VAR
 fj_delete = 1 // give me feedback
 fj_delete_mode = 24 // (#25 in NI, but count starts at 0 not one...)
 STR_VAR
 fj_structure_type = region
 fj_type = 1 // info
 END
BUT_ONLY

Now the follow-on mod has a problem, because "Abandon07" is now #29 in DLTCEP instead of #30 (count starts from 0 so in this code mode is now 28 instead of 29 ). So...

is there a simple way I can make this an "iterate through the region names, find the correct one, read its mode #, use this information to sprint the correct variables, and delete the correct one if it exists ELSE fail" so that the next person can use the same code on the same area and not hose the area?

(I am playing about to figure it out myself, but not likely to get anything done in the nexxt week or so, and the question has come up more pressing in terms of timeframe).

Edited by cmorgan, 19 December 2010 - 09:44 AM.


#20 -guest-

-guest-
  • Guest

Posted 19 December 2010 - 12:27 PM

/* delete original info trigger */
COPY_EXISTING ar0700.are override
  SET region = 0xffffffff
  READ_LONG  0x5c off
  READ_SHORT 0x5a num
  FOR( i = 0 ; i < num && region == 0xffffffff ; ++ i )BEGIN
    READ_ASCII( off + 0xc4 * i ) name (32) NULL
    PATCH_IF!( ~%name%~ STRING_CONTAINS_REGEXP ~Abandon07~ )BEGIN
      SET region = i
    END
  END
  PATCH_IF( region != 0xffffffff )BEGIN
    LPF fj_are_structure
      INT_VAR
      fj_delete_mode = region
      fj_debug = 1
      STR_VAR
      fj_structure_type = region
    END
  END ELSE BEGIN
    PATCH_FAIL ~Couldn't find region "Abandon07" in ar0700.are~
  END
BUT_ONLY

or some such thing.

i didn't include any automated way to scan for this data because you may not always want to filter existing structures by the same parameter.