entity_handle create_character(cosmos& world, const components::transform spawn_transform, const vec2i screen_size, const std::string name, const assets::animation_response_id torso_set) { const auto character = world.create_entity(name); name_entity(character, entity_name::PERSON); const auto crosshair = create_character_crosshair(world, screen_size); crosshair.get<components::crosshair>().character_entity_to_chase = character; crosshair.set_logic_transform(spawn_transform.pos); ingredients::wsad_character(character, crosshair, torso_set); auto& element = character += components::gui_element(); element.rect_world.last_state.screen_size = screen_size; ingredients::wsad_character_physics(character); character.get<components::physics>().set_transform(spawn_transform); ingredients::character_inventory(character); const auto corpse_of_sentience = world.create_entity("corpse_of_sentience"); name_entity(corpse_of_sentience, entity_name::CORPSE); ingredients::wsad_character_corpse(corpse_of_sentience); character.map_sub_entity(sub_entity_name::CORPSE_OF_SENTIENCE, corpse_of_sentience); character.add_standard_components(); // LOG("Character mass: %x", character.get<components::physics>().get_mass()); return character; }
void viewing_session::advance_audiovisual_systems( const cosmos& cosm, const entity_id viewed_character, const augs::variable_delta dt ) { auto& interp = systems_audiovisual.get<interpolation_system>(); cosm.profiler.start(meter_type::INTERPOLATION); interp.integrate_interpolated_transforms(cosm, dt, dt.get_fixed()); cosm.profiler.stop(meter_type::INTERPOLATION); systems_audiovisual.get<particles_simulation_system>().advance_visible_streams_and_all_particles( camera.smoothed_camera, cosm, dt, interp); auto listener_cone = camera.smoothed_camera; listener_cone.transform = cosm[viewed_character].viewing_transform(interp); systems_audiovisual.get<sound_system>().play_nearby_sound_existences( listener_cone, viewed_character, cosm, cosm.get_total_time_passed_in_seconds() + dt.seconds_after_last_step(), interp ); }
entity_handle create_brick_wall(cosmos& world, const components::transform pos, const vec2 size) { const auto crate = world.create_entity("brick_wall"); ingredients::sprite_scalled(crate, pos, size, assets::texture_id::BRICK_WALL, white, render_layer::DYNAMIC_BODY); ingredients::standard_static_body(crate); crate.get<components::fixtures>().set_restitution(0.0f); crate.get<components::fixtures>().set_density(100); crate.add_standard_components(); return crate; }
entity_handle create_sample_backpack(cosmos& world, vec2 pos) { auto def = world.create_entity("sample_backpack"); name_entity(def, entity_name::VIOLET_BACKPACK); ingredients::backpack(def); ingredients::sprite(def, pos, assets::texture_id::BACKPACK, augs::white, render_layer::SMALL_DYNAMIC_BODY); ingredients::see_through_dynamic_body(def); def.add_standard_components(); return def; }
entity_handle create_crate(cosmos& world, const components::transform pos, const vec2 size) { const auto crate = world.create_entity("crate"); name_entity(crate, entity_name::CRATE); ingredients::sprite_scalled(crate, pos, size, assets::texture_id::CRATE, white, render_layer::DYNAMIC_BODY); ingredients::standard_dynamic_body(crate, true); crate.get<components::fixtures>().set_restitution(0.8f); crate.get<components::fixtures>().set_density(0.03f); crate.add_standard_components(); return crate; }
void interpolation_system::integrate_interpolated_transforms(const cosmos& cosm, const augs::delta variable_delta, const augs::delta fixed_delta_for_slowdowns) { if (!enabled) { return; } const auto seconds = variable_delta.in_seconds(); const float slowdown_multipliers_decrease = seconds / fixed_delta_for_slowdowns.in_seconds(); for (const auto e : cosm.get(processing_subjects::WITH_INTERPOLATION)) { const auto& actual = e.logic_transform(); const auto& info = e.get<components::interpolation>(); auto& integrated = get_interpolated(e); auto& cache = per_entity_cache[make_cache_id(e)]; const float considered_positional_speed = interpolation_speed / (sqrt(cache.positional_slowdown_multiplier)); const float considered_rotational_speed = interpolation_speed / (sqrt(cache.rotational_slowdown_multiplier)); if (cache.positional_slowdown_multiplier > 1.f) { cache.positional_slowdown_multiplier -= slowdown_multipliers_decrease / 4; if (cache.positional_slowdown_multiplier < 1.f) { cache.positional_slowdown_multiplier = 1.f; } } if (cache.rotational_slowdown_multiplier > 1.f) { cache.rotational_slowdown_multiplier -= slowdown_multipliers_decrease / 4; if (cache.rotational_slowdown_multiplier < 1.f) { cache.rotational_slowdown_multiplier = 1.f; } } const float positional_averaging_constant = 1.0f - static_cast<float>(pow(info.base_exponent, considered_positional_speed * seconds)); const float rotational_averaging_constant = 1.0f - static_cast<float>(pow(info.base_exponent, considered_rotational_speed * seconds)); auto& recorded_pob = cache.recorded_place_of_birth; auto& recorded_ver = cache.recorded_version; const auto& pob = info.place_of_birth; const auto& ver = e.get_id().pool.version; if (recorded_pob == pob && recorded_ver == ver) { integrated = actual.interpolated_separate(integrated, positional_averaging_constant, rotational_averaging_constant); } else { integrated = actual; recorded_pob = pob; recorded_ver = ver; } } }
void simulation_receiver::remote_entropy_predictions(guid_mapped_entropy& adjusted_entropy, const entity_id& predictable_entity, const cosmos& predicted_cosmos) { entity_intent release_intent; release_intent.is_pressed = false; for (auto e : predicted_cosmos.get(processing_subjects::WITH_PAST_CONTAGIOUS)) { const bool is_locally_controlled_entity = e == predictable_entity; if (is_locally_controlled_entity) continue; for (const auto g : e.guns_wielded()) { if (g.get<components::gun>().trigger_pressed) { if (g.get_current_slot().raw_id.type == slot_function::PRIMARY_HAND) { release_intent.intent = intent_type::CROSSHAIR_PRIMARY_ACTION; } else { release_intent.intent = intent_type::CROSSHAIR_SECONDARY_ACTION; } adjusted_entropy.entropy_per_entity[e.get_guid()].push_back(release_intent); } } } }
void networked_testbed::populate_world_with_entities(cosmos& cosm) { cosm.advance_deterministic_schemata(cosmic_entropy(), [this](const logic_step step) { populate(step); }, [](const const_logic_step) {}); }
void movement_system::apply_movement_forces(cosmos& cosmos) { auto& physics_sys = cosmos.systems_temporary.get<physics_system>(); const auto& delta = cosmos.get_fixed_delta(); for (const auto& it : cosmos.get(processing_subjects::WITH_MOVEMENT)) { auto& movement = it.get<components::movement>(); if (!movement.apply_movement_forces) { continue; } vec2 resultant; resultant.x = movement.moving_right * movement.input_acceleration_axes.x - movement.moving_left * movement.input_acceleration_axes.x; resultant.y = movement.moving_backward * movement.input_acceleration_axes.y - movement.moving_forward * movement.input_acceleration_axes.y; if (!it.has<components::physics>()) { it.get<components::transform>().pos += resultant * delta.in_seconds(); continue; } const auto& physics = it.get<components::physics>(); if (!physics.is_constructed()) continue; if (movement.make_inert_for_ms > 0.f) { movement.make_inert_for_ms -= static_cast<float>(delta.in_milliseconds()); physics.set_linear_damping(2); } else { physics.set_linear_damping(movement.sprint_enabled ? (movement.standard_linear_damping / 4.f) : movement.standard_linear_damping); } if (resultant.non_zero()) { if (movement.acceleration_length > 0) { resultant.set_length(movement.acceleration_length); } if (movement.make_inert_for_ms > 0.f) { resultant /= 10.f; } if (movement.sprint_enabled) { resultant /= 2.f; } if (movement.walking_enabled) { resultant /= 2.f; } physics.apply_force(resultant * physics.get_mass(), movement.applied_force_offset, true); } /* the player feels less like a physical projectile if we brake per-axis */ if (movement.enable_braking_damping && !(movement.make_inert_for_ms > 0.f)) { physics.set_linear_damping_vec(vec2( resultant.x_non_zero() ? 0.f : movement.braking_damping, resultant.y_non_zero() ? 0.f : movement.braking_damping)); } else { physics.set_linear_damping_vec(vec2(0, 0)); } } }
void viewing_session::view( augs::renderer& renderer, const cosmos& cosmos, const entity_id viewed_character, const augs::variable_delta& dt, const augs::gui::text::fstr& custom_log, const game_drawing_settings settings ) { frame_profiler.new_measurement(); const auto screen_size = camera.smoothed_camera.visible_world_area; const vec2i screen_size_i(static_cast<int>(screen_size.x), static_cast<int>(screen_size.y)); renderer.set_viewport({ viewport_coordinates.x, viewport_coordinates.y, screen_size_i.x, screen_size_i.y }); const auto character_chased_by_camera = cosmos[viewed_character]; camera.tick(systems_audiovisual.get<interpolation_system>(), dt, character_chased_by_camera); world_hover_highlighter.cycle_duration_ms = 700; world_hover_highlighter.update(dt.in_milliseconds()); const auto visible = visible_entities(camera.smoothed_camera, cosmos); auto main_cosmos_viewing_step = viewing_step( cosmos, *this, dt, renderer, camera.smoothed_camera, character_chased_by_camera, visible ); main_cosmos_viewing_step.settings = settings; #if NDEBUG || _DEBUG rendering_scripts::illuminated_rendering(main_cosmos_viewing_step); #else rendering_scripts::illuminated_rendering(main_cosmos_viewing_step); #endif using namespace augs::gui::text; if (show_profile_details) { const auto coords = character_chased_by_camera.alive() ? character_chased_by_camera.logic_transform().pos : vec2(); const auto rot = character_chased_by_camera.alive() ? character_chased_by_camera.logic_transform().rotation : 0.f; const auto vel = character_chased_by_camera.alive() ? character_chased_by_camera.get<components::physics>().velocity() : vec2(); const auto bbox = quick_print_format(renderer.triangles, typesafe_sprintf(L"Entities: %x\nX: %f2\nY: %f2\nRot: %f2\nVelX: %x\nVelY: %x\n", cosmos.entities_count(), coords.x, coords.y, rot, vel.x, vel.y) + summary() + cosmos.profiler.sorted_summary(show_profile_details), style(assets::font_id::GUI_FONT, rgba(255, 255, 255, 150)), vec2i(0, 0), 0); quick_print(renderer.triangles, multiply_alpha(global_log::format_recent_as_text(assets::font_id::GUI_FONT), 150.f / 255), vec2i(screen_size_i.x - 300, 0), 300); quick_print(renderer.triangles, custom_log, vec2i(0, static_cast<int>(bbox.y)), 0); } renderer.call_triangles(); renderer.clear_triangles(); triangles.measure(static_cast<double>(renderer.triangles_drawn_total)); renderer.triangles_drawn_total = 0; fps_profiler.end_measurement(); fps_profiler.new_measurement(); frame_profiler.end_measurement(); }
entity_handle create_character_crosshair(cosmos& world, const vec2i screen_size) { auto root = world.create_entity("crosshair"); auto recoil = world.create_entity("crosshair_recoil_body"); auto zero_target = world.create_entity("zero_target"); { auto& sprite = root += components::sprite(); auto& render = root += components::render(); auto& transform = root += components::transform(); auto& crosshair = root += components::crosshair(); auto& processing = root += components::processing(); sprite.set(assets::texture_id::TEST_CROSSHAIR, rgba(255, 255, 255, 255)); render.layer = render_layer::CROSSHAIR; crosshair.base_offset.set(-20, 0); crosshair.sensitivity.set(3, 3); crosshair.visible_world_area.set(1920, 1080); crosshair.update_bounds(); ingredients::make_always_visible(root); } { auto& force_joint = recoil += components::force_joint(); zero_target += components::transform(); components::physics body; components::fixtures colliders; auto& sprite = recoil += components::sprite(); sprite.set(assets::texture_id::TEST_CROSSHAIR, rgba(0, 255, 0, 0)); auto& render = recoil += components::render(); render.layer = render_layer::OVER_CROSSHAIR; ingredients::make_always_visible(recoil); auto& info = colliders.new_collider(); info.shape.from_renderable(recoil); info.filter = filters::renderable(); //info.filter.categoryBits = 0; info.density = 0.1f; info.sensor = true; body.linear_damping = 5; body.angular_damping = 5; force_joint.chased_entity = zero_target; //force_joint.consider_rotation = false; //force_joint.distance_when_force_easing_starts = 10.f; //force_joint.force_towards_chased_entity = 1000.f; //force_joint.power_of_force_easing_multiplier = 1.f; force_joint.divide_transform_mode = true; recoil += body; recoil += colliders; recoil.get<components::fixtures>().set_owner_body(recoil); } root.map_sub_entity(sub_entity_name::CROSSHAIR_RECOIL_BODY, recoil); root.add_standard_components(); recoil.add_standard_components(); zero_target.add_standard_components(); return root; }
std::optional<const_entity_handle> editor_go_to_entity_gui::perform( const editor_go_to_settings& settings, const cosmos& cosm, const vec2 dialog_pos ) { if (!show) { return std::nullopt; } using namespace ImGui; using namespace augs::imgui; { const auto max_lines = static_cast<std::size_t>(settings.num_lines); const auto left = selected_index / max_lines * max_lines; const auto right = std::min(left + max_lines, matches.size()); const auto size = vec2 { static_cast<float>(settings.dialog_width), (right - left + 2) * ImGui::GetTextLineHeightWithSpacing() }; set_next_window_rect( { dialog_pos - vec2(size.x / 2, 0), size }, ImGuiCond_Always ); } auto go_to_entity = scoped_window( "Go to entity", &show, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings ); static auto arrow_callback = [](ImGuiTextEditCallbackData* data) { auto& input = *reinterpret_cast<text_edit_callback_input*>(data->UserData); auto& self = input.self; auto& cosm = input.cosm; switch (data->EventFlag) { case ImGuiInputTextFlags_CallbackHistory: { const auto max_index = self.matches.size(); if (max_index == 0) { break; } if (data->EventKey == ImGuiKey_UpArrow) { self.moved_since_opening = true; if (self.selected_index == 0) { self.selected_index = max_index - 1; } else { --self.selected_index; } } else if (data->EventKey == ImGuiKey_DownArrow) { self.moved_since_opening = true; ++self.selected_index; self.selected_index %= max_index; } } break; case ImGuiInputTextFlags_CallbackAlways: { const auto current_input_text = std::string(data->Buf, data->BufTextLen); const bool should_rebuild = current_input_text != self.last_input || (current_input_text.empty() && self.matches.empty()) ; if (!should_rebuild) { break; } self.last_input = current_input_text; self.matches.clear(); const auto query = current_input_text; unsigned hits = 0; cosm.for_each_entity([&](const auto& handle) { const auto name = handle.get_name(); if (query.empty() || to_lowercase(name).find(query) != std::string::npos) { ++hits; self.matches.push_back(handle.get_id()); } }); if (self.matches.size() > 0) { self.selected_index %= self.matches.size(); } else { self.selected_index = 0; } } break; case ImGuiInputTextFlags_CallbackCompletion: { if (const auto match = self.get_matching_go_to_entity(cosm)) { const auto name = match.get_name(); if (static_cast<int>(name.length()) < data->BufSize) { std::copy(name.c_str(), name.c_str() + name.length() + 1, data->Buf); data->BufTextLen = name.length(); data->CursorPos = name.length(); data->BufDirty = true; } } break; } default: break; } return 0; }; text("Go to entity"); ImGui::SameLine(); bool was_acquired = false; if (ImGui::GetCurrentWindow()->GetID("##GoToEntityInput") != GImGui->ActiveId) { ImGui::SetKeyboardFocusHere(); was_acquired = true; } text_edit_callback_input input { cosm, *this }; auto scope = augs::scope_guard([&](){ if (!was_acquired && ImGui::GetCurrentWindow()->GetID("##GoToEntityInput") != GImGui->ActiveId) { show = false; } }); if (input_text<256>( "##GoToEntityInput", textbox_data, ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackAlways | ImGuiInputTextFlags_CallbackCompletion , arrow_callback, reinterpret_cast<void*>(&input) )) { return get_matching_go_to_entity(cosm); } { const auto query = last_input; const auto max_lines = static_cast<std::size_t>(settings.num_lines); const auto left = selected_index / max_lines * max_lines; const auto right = std::min(left + max_lines, matches.size()); for (std::size_t i = left; i < right; ++i) { if (i == selected_index) { bool s = true; ImGui::PushID(static_cast<int>(i)); ImGui::Selectable("##gotoentity", &s); ImGui::PopID(); ImGui::SameLine(0.f, 0.f); } const auto m = matches[i]; const auto name = cosm[m].get_name(); const auto matched_name = to_lowercase(name); const auto unmatched_left = matched_name.find(query); const auto unmatched_right = unmatched_left + query.length(); text(name.substr(0, unmatched_left)); ImGui::SameLine(0.f, 0.f); text_color(name.substr(unmatched_left, unmatched_right - unmatched_left), green); ImGui::SameLine(0.f, 0.f); text(name.substr(unmatched_right)); ImGui::SameLine(settings.dialog_width * 0.7); text("(id: %x)", m.raw); } } return std::nullopt; }
void particles_simulation_system::advance_visible_streams_and_all_particles(camera_cone cone, const cosmos& cosmos, const augs::delta delta, interpolation_system& interp) { for (auto& particle_layer : particles) { for (auto& p : particle_layer) { p.integrate(delta.in_seconds()); } erase_remove(particle_layer, [](const resources::particle& a) { return a.lifetime_ms >= a.max_lifetime_ms; }); } cone.visible_world_area *= 2.5f; const auto targets = cosmos[cosmos.systems_temporary.get<dynamic_tree_system>().determine_visible_entities_from_camera(cone, components::dynamic_tree_node::tree_type::PARTICLE_EXISTENCES)]; for (const auto it : targets) { auto& cache = get_cache(it); const auto& existence = it.get<components::particles_existence>(); const bool should_rebuild_cache = cache.recorded_existence != existence; if (should_rebuild_cache) { randomization rng = existence.rng_seed; cache.recorded_existence = existence; cache.emission_instances.clear(); for (auto emission : (*existence.input.effect)) { emission.apply_modifier(existence.input.modifier); cache.emission_instances.push_back(emission_instance()); auto& target_stream = *cache.emission_instances.rbegin(); const auto var_v = rng.randval(emission.base_velocity_variation); //LOG("V: %x", var_v); target_stream.velocity.set(std::max(0.f, emission.base_velocity.first - var_v / 2), emission.base_velocity.second + var_v / 2); //LOG("Vl: %x Vu: %x", target_stream.velocity.first, target_stream.velocity.second); target_stream.stream_info = emission; target_stream.enable_streaming = true; target_stream.stream_lifetime_ms = 0.f; target_stream.angular_offset = rng.randval(emission.angular_offset); target_stream.target_spread = rng.randval(emission.spread_degrees); target_stream.target_particles_per_sec = rng.randval(emission.particles_per_sec); target_stream.swing_spread = rng.randval(emission.swing_spread); target_stream.swings_per_sec = rng.randval(emission.swings_per_sec); target_stream.min_swing_spread = rng.randval(emission.min_swing_spread); target_stream.min_swings_per_sec = rng.randval(emission.min_swings_per_sec); target_stream.max_swing_spread = rng.randval(emission.max_swing_spread); target_stream.max_swings_per_sec = rng.randval(emission.max_swings_per_sec); target_stream.stream_max_lifetime_ms = rng.randval(emission.stream_duration_ms); target_stream.stream_particles_to_spawn = rng.randval(emission.num_of_particles_to_spawn_initially); target_stream.swing_speed_change = rng.randval(emission.swing_speed_change_rate); target_stream.swing_spread_change = rng.randval(emission.swing_spread_change_rate); target_stream.fade_when_ms_remaining = rng.randval(emission.fade_when_ms_remaining); } } const auto& transform = it.viewing_transform(interp) + existence.current_displacement; randomization rng = cosmos.get_rng_seed_for(it) + cosmos.get_total_steps_passed(); bool should_destroy = true; for (auto& instance : cache.emission_instances) { const float stream_delta = std::min(delta.in_milliseconds(), instance.stream_max_lifetime_ms - instance.stream_lifetime_ms); instance.stream_lifetime_ms += stream_delta; if (instance.stream_lifetime_ms > instance.stream_max_lifetime_ms) { continue; } auto new_particles_to_spawn_by_time = instance.target_particles_per_sec * (stream_delta / 1000.f); instance.stream_particles_to_spawn += new_particles_to_spawn_by_time; instance.swings_per_sec += rng.randval(-instance.swing_speed_change, instance.swing_speed_change); instance.swing_spread += rng.randval(-instance.swing_spread_change, instance.swing_spread_change); if (instance.max_swing_spread > 0) { augs::clamp(instance.swing_spread, instance.min_swing_spread, instance.max_swing_spread); } if (instance.max_swings_per_sec > 0) { augs::clamp(instance.swings_per_sec, instance.min_swings_per_sec, instance.max_swings_per_sec); } const int to_spawn = static_cast<int>(std::floor(instance.stream_particles_to_spawn)); const auto segment_length = existence.distribute_within_segment_of_length; const vec2 segment_A = transform.pos + vec2().set_from_degrees(transform.rotation + 90).set_length(segment_length / 2); const vec2 segment_B = transform.pos - vec2().set_from_degrees(transform.rotation + 90).set_length(segment_length / 2); for (int i = 0; i < to_spawn; ++i) { const float t = (static_cast<float>(i) / to_spawn); const float time_elapsed = (1.f - t) * delta.in_seconds(); const vec2 segment_position = augs::interp(segment_A, segment_B, rng.randval(0.f, 1.f)); spawn_particle(rng, instance, segment_position, transform.rotation + instance.swing_spread * static_cast<float>(sin((instance.stream_lifetime_ms / 1000.f) * 2 * PI_f * instance.swings_per_sec)) , instance.target_spread, instance.stream_info).integrate(time_elapsed); instance.stream_particles_to_spawn -= 1.f; } } } }