add $whatene command
This commit is contained in:
+105
-91
@@ -2771,100 +2771,114 @@ ChatCommandDefinition cc_what(
|
||||
},
|
||||
unavailable_on_proxy_server);
|
||||
|
||||
static void whatobj_whatene_fn(const ServerArgs& a, bool include_objs, bool include_enes) {
|
||||
auto s = a.c->require_server_state();
|
||||
auto l = a.c->require_lobby();
|
||||
a.check_is_game(true);
|
||||
|
||||
if (!l->map_state) {
|
||||
throw precondition_failed("$C4No map loaded");
|
||||
}
|
||||
|
||||
// TODO: We should use the actual area if a loaded quest has reassigned
|
||||
// them; it's likely that the variations will be wrong if we don't
|
||||
uint8_t area, layout_var;
|
||||
if (l->episode != Episode::EP3) {
|
||||
auto sdt = s->set_data_table(a.c->version(), l->episode, l->mode, l->difficulty);
|
||||
area = sdt->default_area_for_floor(l->episode, a.c->floor);
|
||||
layout_var = (a.c->floor < 0x10) ? l->variations.entries[a.c->floor].layout.load() : 0x00;
|
||||
} else {
|
||||
area = a.c->floor;
|
||||
layout_var = 0;
|
||||
}
|
||||
|
||||
double min_dist2 = -1.0;
|
||||
VectorXYZF nearest_worldspace_pos;
|
||||
shared_ptr<const MapState::ObjectState> nearest_obj;
|
||||
shared_ptr<const MapState::EnemyState> nearest_ene;
|
||||
|
||||
auto check_entity = [&](auto& nearest_entity, auto entity, const auto& def) -> void {
|
||||
VectorXYZF worldspace_pos;
|
||||
if (l->episode != Episode::EP3) {
|
||||
try {
|
||||
const auto& room = s->room_layout_index->get_room(area, layout_var, def.set_entry->room);
|
||||
// This is the order in which the game does the rotations; not sure why
|
||||
worldspace_pos = def.set_entry->pos.rotate_x(room.angle.x).rotate_z(room.angle.z).rotate_y(room.angle.y) + room.position;
|
||||
} catch (const out_of_range&) {
|
||||
a.c->log.warning("Can't find definition for room %02hhX:%02hhX:%08hX", area, layout_var, def.set_entry->room.load());
|
||||
worldspace_pos = def.set_entry->pos;
|
||||
}
|
||||
} else {
|
||||
worldspace_pos = def.set_entry->pos;
|
||||
}
|
||||
|
||||
float dist2 = (VectorXZF(worldspace_pos) - a.c->pos).norm2();
|
||||
if ((min_dist2 < 0.0) || (dist2 < min_dist2)) {
|
||||
nearest_entity = entity;
|
||||
nearest_worldspace_pos = worldspace_pos;
|
||||
min_dist2 = dist2;
|
||||
}
|
||||
};
|
||||
|
||||
if (include_objs) {
|
||||
for (const auto& it : l->map_state->iter_object_states(a.c->version())) {
|
||||
if (it->super_obj && (it->super_obj->floor == a.c->floor)) {
|
||||
const auto& def = it->super_obj->version(a.c->version());
|
||||
if (def.set_entry) {
|
||||
check_entity(nearest_obj, it, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (include_enes) {
|
||||
for (const auto& it : l->map_state->iter_enemy_states(a.c->version())) {
|
||||
if (it->super_ene && (it->super_ene->floor == a.c->floor)) {
|
||||
const auto& def = it->super_ene->version(a.c->version());
|
||||
if (def.set_entry) {
|
||||
check_entity(nearest_ene, it, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since we check all objects first, nearest_ene will only be set if
|
||||
// there is an enemy closer than all objects. So, we print that if it's
|
||||
// set, and print the object if not.
|
||||
if (nearest_ene) {
|
||||
const auto* set_entry = nearest_ene->super_ene->version(a.c->version()).set_entry;
|
||||
send_text_message_printf(a.c, "$C5E-%03zX\n$C6%s\n$C2%s\n$C7X:%.2f Z:%.2f",
|
||||
nearest_ene->e_id, phosg::name_for_enum(nearest_ene->type(a.c->version(), l->episode, l->event)),
|
||||
MapFile::name_for_enemy_type(set_entry->base_type),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.z.load());
|
||||
auto set_str = set_entry->str();
|
||||
a.c->log.info("Enemy found via $whatobj: E-%03zX %s at x=%g y=%g z=%g",
|
||||
nearest_ene->e_id, set_str.c_str(),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.y.load(), nearest_worldspace_pos.z.load());
|
||||
|
||||
} else if (nearest_obj) {
|
||||
send_text_message_printf(a.c, "$C5K-%03zX\n$C6%s\n$C7X:%.2f Z:%.2f",
|
||||
nearest_obj->k_id, nearest_obj->type_name(a.c->version()),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.z.load());
|
||||
auto set_str = nearest_obj->super_obj->version(a.c->version()).set_entry->str();
|
||||
a.c->log.info("Object found via $whatobj: K-%03zX %s at x=%g y=%g z=%g",
|
||||
nearest_obj->k_id, set_str.c_str(),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.y.load(), nearest_worldspace_pos.z.load());
|
||||
|
||||
} else {
|
||||
throw precondition_failed("$C4No objects or\nenemies are nearby");
|
||||
}
|
||||
}
|
||||
|
||||
ChatCommandDefinition cc_whatobj(
|
||||
{"$whatobj"},
|
||||
+[](const ServerArgs& a) -> void {
|
||||
auto s = a.c->require_server_state();
|
||||
auto l = a.c->require_lobby();
|
||||
a.check_is_game(true);
|
||||
|
||||
if (!l->map_state) {
|
||||
throw precondition_failed("$C4No map loaded");
|
||||
}
|
||||
|
||||
// TODO: We should use the actual area if a loaded quest has reassigned
|
||||
// them; it's likely that the variations will be wrong if we don't
|
||||
uint8_t area, layout_var;
|
||||
if (l->episode != Episode::EP3) {
|
||||
auto sdt = s->set_data_table(a.c->version(), l->episode, l->mode, l->difficulty);
|
||||
area = sdt->default_area_for_floor(l->episode, a.c->floor);
|
||||
layout_var = (a.c->floor < 0x10) ? l->variations.entries[a.c->floor].layout.load() : 0x00;
|
||||
} else {
|
||||
area = a.c->floor;
|
||||
layout_var = 0;
|
||||
}
|
||||
|
||||
double min_dist2 = -1.0;
|
||||
VectorXYZF nearest_worldspace_pos;
|
||||
shared_ptr<const MapState::ObjectState> nearest_obj;
|
||||
shared_ptr<const MapState::EnemyState> nearest_ene;
|
||||
|
||||
auto check_entity = [&](auto& nearest_entity, auto entity, const auto& def) -> void {
|
||||
VectorXYZF worldspace_pos;
|
||||
if (l->episode != Episode::EP3) {
|
||||
try {
|
||||
const auto& room = s->room_layout_index->get_room(area, layout_var, def.set_entry->room);
|
||||
// This is the order in which the game does the rotations; not sure why
|
||||
worldspace_pos = def.set_entry->pos.rotate_x(room.angle.x).rotate_z(room.angle.z).rotate_y(room.angle.y) + room.position;
|
||||
} catch (const out_of_range&) {
|
||||
a.c->log.warning("Can't find definition for room %02hhX:%02hhX:%08hX", area, layout_var, def.set_entry->room.load());
|
||||
worldspace_pos = def.set_entry->pos;
|
||||
}
|
||||
} else {
|
||||
worldspace_pos = def.set_entry->pos;
|
||||
}
|
||||
|
||||
float dist2 = (VectorXZF(worldspace_pos) - a.c->pos).norm2();
|
||||
if ((min_dist2 < 0.0) || (dist2 < min_dist2)) {
|
||||
nearest_entity = entity;
|
||||
nearest_worldspace_pos = worldspace_pos;
|
||||
min_dist2 = dist2;
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& it : l->map_state->iter_object_states(a.c->version())) {
|
||||
if (it->super_obj && (it->super_obj->floor == a.c->floor)) {
|
||||
const auto& def = it->super_obj->version(a.c->version());
|
||||
if (def.set_entry) {
|
||||
check_entity(nearest_obj, it, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& it : l->map_state->iter_enemy_states(a.c->version())) {
|
||||
if (it->super_ene && (it->super_ene->floor == a.c->floor)) {
|
||||
const auto& def = it->super_ene->version(a.c->version());
|
||||
if (def.set_entry) {
|
||||
check_entity(nearest_ene, it, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since we check all objects first, nearest_ene will only be set if
|
||||
// there is an enemy closer than all objects. So, we print that if it's
|
||||
// set, and print the object if not.
|
||||
if (nearest_ene) {
|
||||
const auto* set_entry = nearest_ene->super_ene->version(a.c->version()).set_entry;
|
||||
send_text_message_printf(a.c, "$C5E-%03zX\n$C6%s\n$C2%s\n$C7X:%.2f Z:%.2f",
|
||||
nearest_ene->e_id, phosg::name_for_enum(nearest_ene->type(a.c->version(), l->episode, l->event)),
|
||||
MapFile::name_for_enemy_type(set_entry->base_type),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.z.load());
|
||||
auto set_str = set_entry->str();
|
||||
a.c->log.info("Enemy found via $whatobj: E-%03zX %s at x=%g y=%g z=%g",
|
||||
nearest_ene->e_id, set_str.c_str(),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.y.load(), nearest_worldspace_pos.z.load());
|
||||
|
||||
} else if (nearest_obj) {
|
||||
send_text_message_printf(a.c, "$C5K-%03zX\n$C6%s\n$C7X:%.2f Z:%.2f",
|
||||
nearest_obj->k_id, nearest_obj->type_name(a.c->version()),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.z.load());
|
||||
auto set_str = nearest_obj->super_obj->version(a.c->version()).set_entry->str();
|
||||
a.c->log.info("Object found via $whatobj: K-%03zX %s at x=%g y=%g z=%g",
|
||||
nearest_obj->k_id, set_str.c_str(),
|
||||
nearest_worldspace_pos.x.load(), nearest_worldspace_pos.y.load(), nearest_worldspace_pos.z.load());
|
||||
|
||||
} else {
|
||||
throw precondition_failed("$C4No objects or\nenemies are nearby");
|
||||
}
|
||||
whatobj_whatene_fn(a, true, false);
|
||||
},
|
||||
unavailable_on_proxy_server);
|
||||
ChatCommandDefinition cc_whatene(
|
||||
{"$whatene"},
|
||||
+[](const ServerArgs& a) -> void {
|
||||
whatobj_whatene_fn(a, false, true);
|
||||
},
|
||||
unavailable_on_proxy_server);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user