This code is intended to do one thing: provide module authors with the ability to quickly process scene props at the beginning of a mission, removing some of them from view, or doing something else but equally wicked to them poor propsies.
My original motivator was to make villages more dynamic. So you could build a Messenger Post in the village, then go to village center and voila - there's a Messenger Post standing right there! The main idea was to use variation ID numbers, which are not used by the game anywhere except in the main tutorial. However even when I implemented the very first version of the code, it was already much greater than that, and allowed for pretty generic manipulation of objects on any mission.
Below are two version of the code. Both are tested and confirmed to work. The first code sample explains the basic idea of how to make dynamic scene editing available to everyone, however it's recommended to use the second code sample as it's much more versatile and powerful.
My sincere thanks to:
MadocComadrin for the idea to use ti_on_scene_prop_init triggers, which eradicated the performance problem.
Caba`drin and Vorrne for advice and suggestions.
My original motivator was to make villages more dynamic. So you could build a Messenger Post in the village, then go to village center and voila - there's a Messenger Post standing right there! The main idea was to use variation ID numbers, which are not used by the game anywhere except in the main tutorial. However even when I implemented the very first version of the code, it was already much greater than that, and allowed for pretty generic manipulation of objects on any mission.
Below are two version of the code. Both are tested and confirmed to work. The first code sample explains the basic idea of how to make dynamic scene editing available to everyone, however it's recommended to use the second code sample as it's much more versatile and powerful.
This code allows you to delete marked scene props at the mission start, so the user will not see them.
Insert this anywhere:
Insert this into any mission template where you want conditional object removal (assuming you need mission-based control over objects, otherwise just don't use these lines and remove corresponding check in module_scene_props.py):
Insert this anywhere, preferably at the end of the file:
Insert this at the very end of the file:
Quick guide for scene editors.
1. Put 127 as the variation ID #1 to make the prop subject to situational removal.
2. Put conditional code (supplied by module scripters) as the variation ID #2.
3. Voila, when the condition defined by the conditional code is met, the prop will not appear on the scene.
4. All you need is the list of conditional codes, which will look like follows:
Insert this anywhere:
Code:
situational_scene_var_id = 127 ## When a scene prop has this number as first variation ID, it is subject to situational removal, all other scene props are ignored
Insert this into any mission template where you want conditional object removal (assuming you need mission-based control over objects, otherwise just don't use these lines and remove corresponding check in module_scene_props.py):
Code:
(ti_before_mission_start, 0, 0, [], [(assign, "$g_scene_requires_objects_removal", 1)]),
(ti_after_mission_start, 0, 0, [], [(assign, "$g_scene_requires_objects_removal", 0)]),
Insert this anywhere, preferably at the end of the file:
Code:
# script_scene_prop_needs_removal
# Input: scene_prop_id, variation_id
# Output: reg0 should contain 1 or 0 (needs removal or not)
# This is a dummy script which always tells to never remove anything.
# Replace this with actual code according to your needs.
("scene_prop_needs_removal",
[
#(store_script_param_1, ":scene_prop_instance_id"),
#(prop_instance_get_scene_prop_kind, ":scene_prop_id", ":scene_prop_instance_id"), ## For checks on scene_prop type etc
(assign, reg0, 0),
]
),
Insert this at the very end of the file:
Code:
from ID_scripts import *
append_to_props_list = (ti_on_scene_prop_init,
[
(try_begin),
(eq, "$g_scene_requires_objects_removal", 1), # If scene doesn't want situational control over scene props, we exit immediately
(store_trigger_param_1, ":scene_prop_instance_id"),
(prop_instance_get_variation_id, ":var_id", ":scene_prop_instance_id"),
(eq, ":var_id", situational_scene_var_id), ## We check that first variation ID matches the one defined in constants as situational flag
(call_script, "script_scene_prop_needs_removal", ":scene_prop_instance_id"),
(eq, reg0, 1),
(prop_instance_get_position, pos1, ":scene_prop_instance_id"),
(position_set_z, pos1, -1000),
(prop_instance_set_position, ":scene_prop_instance_id", pos1),
(try_end),
]
)
for key in range(len(scene_props)):
scene_props[key][4].append(append_to_props_list)
Quick guide for scene editors.
1. Put 127 as the variation ID #1 to make the prop subject to situational removal.
2. Put conditional code (supplied by module scripters) as the variation ID #2.
3. Voila, when the condition defined by the conditional code is met, the prop will not appear on the scene.
4. All you need is the list of conditional codes, which will look like follows:
Code:
{1} Village has blacksmith
{2} Village does not have blacksmith
{3} Village has mill
{4} Village does not have mill
(etc)
This code creates the list of all scene_props on the scene and allows user to iterate through them at will. It can be easily adapted to replicate the effects of the previous code, but much more powerful as it allows you to iterate through scene props at any time during the mission, not only at the beginning. It also does not use variation ID numbers as flags, leaving it to the module author to decide how to use variation IDs and whether to use them at all.
Code:
########################################################################################################################
# Scene props iterator code starts here
# These lines should better be placed last in the file, but BEFORE the closing bracket.
########################################################################################################################
["scene_props_list","scene_props_list","scene_props_list", tf_hero, 0, 0, fac_commoners, [], def_attrib|level(1), wp(20), knows_common, 0],
########################################################################################################################
# Scene props iterator code ends here
########################################################################################################################
Code:
########################################################################################################################
# Scene props iterator code starts here
# These lines should be placed LAST in the file, AFTER the closing bracket!!!
########################################################################################################################
from ID_scripts import *
append_to_props_list = (ti_on_scene_prop_init,
[
(store_trigger_param_1, ":scene_prop_instance_id"),
(call_script, "script_scene_props_iterator_append", ":scene_prop_instance_id"),
]
)
for key in range(len(scene_props)):
scene_props[key][4].append(append_to_props_list)
########################################################################################################################
# Scene props iterator code ends here
########################################################################################################################
Code:
########################################################################################################################
# Scene props iterator code starts here
# These triggers should be added to all mission templates where you want to use scene prop iterator
########################################################################################################################
(ti_before_mission_start, 0, 0, [], [(call_script, "script_scene_props_iterator_init")]),
(ti_after_mission_start, 0, 0, [], [(call_script, "script_scene_props_iterator_finalize")]),
# ALTERNATIVELY you could use the following
# (ti_before_mission_start, 0, 0, [], [(call_script, "script_scene_props_iterator_init")]),
# (ti_after_mission_start, 0, 0, [], [(call_script, "script_scene_props_iterator", "script_my_callback_script")]),
########################################################################################################################
# Scene props iterator code ends here
########################################################################################################################
Code:
########################################################################################################################
# Scene props iterator code starts here
########################################################################################################################
# script_scene_props_iterator_init
# Input: none
# Output: none
("scene_props_iterator_init",
[
(assign, "$g_sp_iterator_count", 0),
(assign, "$g_sp_iterator_collecting", 1),
]
),
# script_scene_props_iterator_finalize
# Input: none
# Output: none
("scene_props_iterator_finalize",
[
(assign, "$g_sp_iterator_collecting", 0),
]
),
# script_scene_props_iterator_append
# Input: scene_prop_instance_id
# Output: none
("scene_props_iterator_append",
[
(try_begin),
(eq, "$g_sp_iterator_collecting", 1),
(store_script_param_1, ":scene_prop_instance_id"),
# reg0 version:
#(call_script, "script_scene_props_iterator_filter", ":scene_prop_instance_id"),
#(eq, reg0, 1),
# cf version:
(call_script, "script_cf_scene_props_iterator_filter", ":scene_prop_instance_id"),
(troop_set_slot, "trp_scene_props_list", "$g_sp_iterator_count", ":scene_prop_instance_id"),
(val_add, "$g_sp_iterator_count", 1),
(try_end),
]
),
# script_scene_props_iterator_filter
# Input: scene_prop_instance_id
# Output: reg0
# Note: assign reg0 to zero to prevent scene prop from being appended to iterator list
("scene_props_iterator_filter",
[
#(store_script_param_1, ":scene_prop_instance_id"),
(assign, reg0, 1),
]
),
# script_cf_scene_props_iterator_filter
# Input: scene_prop_instance_id
# Output: none
# Note: return fail to prevent scene prop from being appended to iterator list
("cf_scene_props_iterator_filter",
[
#(store_script_param_1, ":scene_prop_instance_id"),
]
),
# script_scene_props_iterator
# Input: callback script (should accept scene_prop_instance_id as parameter)
# Output: none
("scene_props_iterator",
[
(call_script, "script_scene_props_iterator_finalize"),
(store_script_param_1, ":callback_script"),
(try_begin),
(gt, ":callback_script", 0),
(try_for_range_backwards, ":iterator", 0, "$g_sp_iterator_count"),
(troop_get_slot, ":scene_prop_instance_id", "trp_scene_props_list", ":iterator"),
(call_script, ":callback_script", ":scene_prop_instance_id"),
(try_end),
(try_end),
]
),
# script_delete_scene_prop_instance
# Input: scene_prop_instance_id
# Output: none
# Note: This will hide the prop and remove it from iterator list.
# Use this to prevent "deleted" props from being processed again and again.
# Performance note: THIS IS NOT OPTIMIZED TO BE USED FROM WITHIN ITERATOR!
("delete_scene_prop_instance",
[
(store_script_param_1, ":instance_to_delete"),
(assign, ":index", -1),
(try_for_range, ":iterator", 0, "$g_sp_iterator_count"),
(troop_get_slot, ":found_value", "trp_scene_props_list", ":iterator"),
(eq, ":instance_to_delete", ":found_value"),
(assign, ":index", ":iterator"),
(try_end),
(try_begin),
# Have we found the requested scene prop instance?
(ge, ":index", 0),
# First we hide it from user's view
(prop_instance_get_position, pos1, ":instance_to_delete"),
(position_set_z, pos1, -1000),
(prop_instance_set_position, ":instance_to_delete", pos1),
# Now we remove it from the iterator list
(try_for_range, ":iterator", ":index", "$g_sp_iterator_count"),
(store_add, ":take_from", ":iterator", 1),
(troop_get_slot, ":value", "trp_scene_props_list", ":take_from"),
(troop_set_slot, "trp_scene_props_list", ":iterator", ":value"),
(try_end),
# And reduce iterator list size by 1
(val_sub, "$g_sp_iterator_count", 1),
(try_end),
]
),
########################################################################################################################
# Scene props iterator code ends here
########################################################################################################################
My sincere thanks to:
MadocComadrin for the idea to use ti_on_scene_prop_init triggers, which eradicated the performance problem.
Caba`drin and Vorrne for advice and suggestions.
This is a small script which does just one thing.
It enables you to easily mark objects on any scene for removal on certain conditions.
For example, take a village scene. Mark the mill, it's fan and related objects for removal when there's no mill built in the village. Add a check for that flag in the special script and compile your module. Now whenever you enter the village, for each marked object the script will be called to determine whether this object should be removed or not.
From player's perspective, this looks very easy: when there's no mill built in the village, there's no mill on the scene. When the mill is built, it immediately appears on the village scene.
Marking objects for removal is done with variation ID numbers. There are two for each scene prop, either can be in the range of 0..127. Setting first variation number to 127 (this number can be redefined if necessary) will make the scene prop subject to situational removal. Second variation number will be passed as parameter to the separate script determining whether or not this object should be removed.
Pro. Making dynamic objects on the scenes is as easy as easy can get, and absolutely generic.
Con. Unfortunately, "absolutely generic" also means "freakin' slow when loading the scene".
It enables you to easily mark objects on any scene for removal on certain conditions.
For example, take a village scene. Mark the mill, it's fan and related objects for removal when there's no mill built in the village. Add a check for that flag in the special script and compile your module. Now whenever you enter the village, for each marked object the script will be called to determine whether this object should be removed or not.
From player's perspective, this looks very easy: when there's no mill built in the village, there's no mill on the scene. When the mill is built, it immediately appears on the village scene.
Marking objects for removal is done with variation ID numbers. There are two for each scene prop, either can be in the range of 0..127. Setting first variation number to 127 (this number can be redefined if necessary) will make the scene prop subject to situational removal. Second variation number will be passed as parameter to the separate script determining whether or not this object should be removed.
Code:
from ID_scene_props import *
scene_props_start = spr_chest_a ## This is for Native, redefine if you have other scene props defined or want to limit scene props type available for situational removal
scene_props_end = spr_oil_press_interior + 1 ## This is for Native, redefine if you have other scene props defined or want to limit scene props type available for situational removal
situational_scene_var_id = 127 ## When a scene prop has this number as first variation ID, it is subject to situational removal, all other scene props are ignored
Code:
# Insert this into "village_center" mission template among other mission template triggers
(ti_before_mission_start, 0, 0, [], [(call_script, "script_remove_scene_objects_situational")]),
Code:
# script_remove_scene_objects_situational
# Input: none
# Output: none
("remove_scene_objects_situational",
[
(try_for_range, ":scene_prop_id", scene_props_start, scene_props_end), ## We iterate through all defined scene prop types
(scene_prop_get_num_instances, ":prop_count", ":scene_prop_id"),
(try_begin),
(gt, ":prop_count", 0),
(try_for_range_backwards, ":scene_prop_instance_no", 0, ":prop_count"), ## For each scene prop type, we iterate through all it's instances on current scene
(scene_prop_get_instance, ":scene_prop_instance_id", ":scene_prop_id", ":scene_prop_instance_no"),
(try_begin),
(prop_instance_is_valid, ":scene_prop_instance_id"), ## Not sure what this does, but just in case
(prop_instance_get_variation_id, ":var_id", ":scene_prop_instance_id"),
(eq, ":var_id", situational_scene_var_id), ## We check that first variation ID matches the one defined in constants as situational flag
(prop_instance_get_variation_id_2, ":var_id", ":scene_prop_instance_id"),
(call_script, "script_scene_prop_needs_removal", ":scene_prop_id", ":var_id"), ## Separate script which determines whether the prop should be removed - see below
(eq, reg0, 1),
(replace_prop_instance, ":scene_prop_instance_id", spr_empty),
(try_end),
(try_end),
(try_end),
(try_end),
]
),
# script_scene_prop_needs_removal
# Input: scene_prop_id, variation_id
# Output: reg0 should contain 1 or 0 (needs removal or not)
# This is a dummy script which always tells script_remove_scene_objects_situational to never remove anything.
# Replace this with actual code according to your module's needs.
("scene_prop_needs_removal",
[
(store_script_param_1, ":scene_prop_id"),
(store_script_param_2, ":variation_id"),
(assign, reg0, 0),
]
),
Pro. Making dynamic objects on the scenes is as easy as easy can get, and absolutely generic.
Con. Unfortunately, "absolutely generic" also means "freakin' slow when loading the scene".