diff --git a/source/game/game_engine.c b/source/game/game_engine.c index 84bb011c..51063a48 100644 --- a/source/game/game_engine.c +++ b/source/game/game_engine.c @@ -4662,9 +4662,64 @@ static struct real_vector3d forward; real_vector3d up; long idle_since; + /* (the scenario name the map's scripts teleport; NONE when it had none) */ + short name_index; } game_engine_vehicle_homes[MAXIMUM_VEHICLE_HOMES]; static short game_engine_vehicle_home_count = NONE; +/* still on the pad the map put it on: further than this, and empty, it can +come back */ +#define VEHICLE_HOME_AWAY_SQUARED 4.0f + +/* the vehicle this home should follow. The recorded one, if it is still +alive. A map script often destroys that one and creates it again under the +same name: that one is this home's vehicle, and a second is not put on the +pad with it. */ +static long game_engine_vehicle_home_current( + short home) +{ + long object_index = game_engine_vehicle_homes[home].object_index; + struct unit_datum *vehicle = unit_try_and_get(object_index); + long named_index; + struct object_iterator iterator; + + if (vehicle && !TEST_FLAG(vehicle->object.damage_flags, _object_dead_bit)) + return object_index; + + named_index = game_engine_vehicle_homes[home].name_index == NONE ? NONE : + object_index_from_name_index(game_engine_vehicle_homes[home].name_index); + vehicle = unit_try_and_get(named_index); + if (vehicle && !TEST_FLAG(vehicle->object.damage_flags, _object_dead_bit)) + { + game_engine_vehicle_homes[home].object_index = named_index; + return named_index; + } + + object_iterator_new(&iterator, _object_mask_vehicle, 0); + while (object_iterator_next(&iterator)) + { + struct object_datum *object = object_get(iterator.index); + real dx = object->object.position.x - game_engine_vehicle_homes[home].position.x; + real dy = object->object.position.y - game_engine_vehicle_homes[home].position.y; + real dz = object->object.position.z - game_engine_vehicle_homes[home].position.z; + + if (TEST_FLAG(object->object.damage_flags, _object_dead_bit) || + object->definition_index != game_engine_vehicle_homes[home].definition_index || + dx * dx + dy * dy + dz * dz > VEHICLE_HOME_AWAY_SQUARED) + { + continue; + } + game_engine_vehicle_homes[home].object_index = iterator.index; + if (game_engine_vehicle_homes[home].name_index == NONE) + game_engine_vehicle_homes[home].name_index = object->object.name_index; + else + object_claim_scenario_name(iterator.index, game_engine_vehicle_homes[home].name_index); + return iterator.index; + } + + return NONE; +} + static void game_engine_update_vehicle_respawn( void) { @@ -4692,11 +4747,13 @@ static void game_engine_update_vehicle_respawn( object_get_orientation(iterator.index, &game_engine_vehicle_homes[home].forward, &game_engine_vehicle_homes[home].up); game_engine_vehicle_homes[home].idle_since = NONE; + game_engine_vehicle_homes[home].name_index = object->object.name_index; } } for (index = 0; index < game_engine_vehicle_home_count; index++) { - struct unit_datum *vehicle = unit_try_and_get(game_engine_vehicle_homes[index].object_index); + long object_index = game_engine_vehicle_home_current(index); + struct unit_datum *vehicle = unit_try_and_get(object_index); boolean waiting = TRUE; if (vehicle && !TEST_FLAG(vehicle->object.damage_flags, _object_dead_bit)) @@ -4710,7 +4767,7 @@ static void game_engine_update_vehicle_respawn( /* (empty: no rider among its children, which hold its own weapons too) */ waiting = vehicle->unit.driver_object_index == NONE && vehicle->unit.gunner_object_index == NONE && - dx * dx + dy * dy + dz * dz > 4.0f; + dx * dx + dy * dy + dz * dz > VEHICLE_HOME_AWAY_SQUARED; for (child_index = vehicle->object.first_child_object_index; waiting && child_index != NONE; child_index = object_get(child_index)->object.next_object_index) { @@ -4725,14 +4782,54 @@ static void game_engine_update_vehicle_respawn( else if (now - game_engine_vehicle_homes[index].idle_since >= respawn_ticks) { struct object_placement_data placement_data; + struct object_iterator iterator; + long stale_index = game_engine_vehicle_homes[index].object_index; + long named_index = game_engine_vehicle_homes[index].name_index == NONE ? NONE : + object_index_from_name_index(game_engine_vehicle_homes[index].name_index); + long on_pad = NONE; - if (vehicle) - object_delete(game_engine_vehicle_homes[index].object_index); + /* (a vehicle of this kind is already there: follow that one, + rather than putting another on top of it) */ + object_iterator_new(&iterator, _object_mask_vehicle, 0); + while (object_iterator_next(&iterator)) + { + struct object_datum *occupant = object_get(iterator.index); + real dx = occupant->object.position.x - game_engine_vehicle_homes[index].position.x; + real dy = occupant->object.position.y - game_engine_vehicle_homes[index].position.y; + real dz = occupant->object.position.z - game_engine_vehicle_homes[index].position.z; + + if (iterator.index == object_index || + TEST_FLAG(occupant->object.damage_flags, _object_dead_bit) || + occupant->definition_index != game_engine_vehicle_homes[index].definition_index || + dx * dx + dy * dy + dz * dz > VEHICLE_HOME_AWAY_SQUARED) + { + continue; + } + on_pad = iterator.index; + break; + } + if (on_pad != NONE) + { + game_engine_vehicle_homes[index].object_index = on_pad; + if (game_engine_vehicle_homes[index].name_index == NONE) + game_engine_vehicle_homes[index].name_index = object_get(on_pad)->object.name_index; + else + object_claim_scenario_name(on_pad, game_engine_vehicle_homes[index].name_index); + game_engine_vehicle_homes[index].idle_since = NONE; + continue; + } + if (unit_try_and_get(stale_index)) + object_delete(stale_index); + if (named_index != stale_index && unit_try_and_get(named_index)) + object_delete(named_index); object_placement_data_new(&placement_data, game_engine_vehicle_homes[index].definition_index, NONE); placement_data.position = game_engine_vehicle_homes[index].position; placement_data.forward = game_engine_vehicle_homes[index].forward; placement_data.up = game_engine_vehicle_homes[index].up; game_engine_vehicle_homes[index].object_index = object_new(&placement_data); + object_claim_scenario_name( + game_engine_vehicle_homes[index].object_index, + game_engine_vehicle_homes[index].name_index); game_engine_vehicle_homes[index].idle_since = NONE; } } diff --git a/source/objects/objects.c b/source/objects/objects.c index 4c9f6a1d..4f8a8b0b 100644 --- a/source/objects/objects.c +++ b/source/objects/objects.c @@ -4665,6 +4665,23 @@ static void object_name_list_new( return; } +/* the name is free: deleting the vehicle that held it cleared the slot. +A name something else still holds stays with that object. */ +void object_claim_scenario_name( + long object_index, + short name_index) +{ + if (object_index == NONE || + !VALID_INDEX(name_index, MIN(global_scenario_get()->object_names.count, MAXIMUM_OBJECT_NAMES_PER_SCENARIO)) || + object_name_list[name_index] != NONE) + { + return; + } + + object_name_list_new(object_index, name_index); + return; +} + static void object_name_list_delete( long object_index) { diff --git a/source/objects/objects.h b/source/objects/objects.h index ee691bc0..96dbee3f 100644 --- a/source/objects/objects.h +++ b/source/objects/objects.h @@ -432,6 +432,10 @@ void object_permute_region(long object_index, char *permutation_name, short desi boolean object_get_function_value(long object_index, short function_index, real *value_reference); short objects_in_clusters_by_indices(unsigned long class_flags, short cluster_count, short const *cluster_indices, short maximum_object_count, long *object_indices); long object_index_from_name_index(short name_index); +/* port: this object takes a scenario name nobody holds, the way a placed +vehicle does. A respawned vehicle keeps the name of the one it replaces, so +a script that teleports that name still finds it. */ +void object_claim_scenario_name(long object_index, short name_index); void objects_disconnect_from_structure_bsp(void); boolean object_visible_to_any_player(long object_index); void object_pvs_activate(long object_index);