void melee_system::consume_melee_intents(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& events = step.transient.messages.get_queue<messages::intent_message>(); for (const auto& it : events) { /* we search the whole intent message queue for events of interest; here we can even find gunshot requests for some distant enemy AI, therefore we need to filter out events we're interested in, and that would be melee-related intents and only these applied to an entity with a melee component */ auto* const maybe_melee = cosmos[it.subject].find<components::melee>(); if (maybe_melee == nullptr) continue; auto& melee = *maybe_melee; if (it.intent == intent_type::MELEE_PRIMARY_MOVE) { melee.primary_move_flag = it.is_pressed; } if (it.intent == intent_type::MELEE_SECONDARY_MOVE) { melee.secondary_move_flag = it.is_pressed; } if (it.intent == intent_type::MELEE_TERTIARY_MOVE) { melee.tertiary_move_flag = it.is_pressed; } } }
void trace_system::lengthen_sprites_of_traces(logic_step& step) const { auto& cosmos = step.cosm; const auto delta = step.get_delta(); for (const auto t : cosmos.get(processing_subjects::WITH_TRACE)) { auto& trace = t.get<components::trace>(); auto& sprite = t.get<components::sprite>(); if (trace.chosen_lengthening_duration_ms < 0.f) { trace.reset(cosmos.get_rng_for(t)); } vec2 surplus_multiplier; if (!trace.is_it_finishing_trace) { surplus_multiplier = trace.chosen_multiplier * trace.lengthening_time_passed_ms / trace.chosen_lengthening_duration_ms; } else { surplus_multiplier = (trace.chosen_multiplier + vec2(1, 1)) * (1.f - (trace.lengthening_time_passed_ms / trace.chosen_lengthening_duration_ms)) - vec2(1, 1); } sprite.size_multiplier = vec2(1, 1) + surplus_multiplier; sprite.center_offset = sprite.size * (surplus_multiplier / 2.f); trace.lengthening_time_passed_ms += static_cast<float>(delta.in_milliseconds()); } }
void physics_system::step_and_set_new_transforms(logic_step& step) { auto& cosmos = step.cosm; const auto delta = step.get_delta(); int32 velocityIterations = 8; int32 positionIterations = 3; ray_casts_since_last_step = 0; b2world->Step(static_cast<float32>(delta.in_seconds()), velocityIterations, positionIterations); post_and_clear_accumulated_collision_messages(step); for (b2Body* b = b2world->GetBodyList(); b != nullptr; b = b->GetNext()) { if (b->GetType() == b2_staticBody) continue; entity_handle entity = cosmos[b->GetUserData()]; auto& physics = entity.get<components::physics>(); recurential_friction_handler(step, b, b->m_ownerFrictionGround); physics.component.transform = b->m_xf; physics.component.sweep = b->m_sweep; physics.component.velocity = b->GetLinearVelocity(); physics.component.angular_velocity = b->GetAngularVelocity(); } }
void movement_system::generate_movement_responses(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); step.transient.messages.get_queue<messages::movement_response>().clear(); for (const auto& it : cosmos.get(processing_subjects::WITH_MOVEMENT)) { const auto& movement = it.get<components::movement>(); float32 speed = 0.0f; if (movement.enable_animation) { if (it.has<components::physics>()) { speed = it.get<components::physics>().velocity().length(); } } messages::movement_response msg; if (movement.max_speed_for_movement_response == 0.f) msg.speed = 0.f; else msg.speed = speed / movement.max_speed_for_movement_response; for (const auto receiver : movement.response_receivers) { messages::movement_response copy(msg); copy.stop_response_at_zero_speed = receiver.stop_response_at_zero_speed; copy.subject = receiver.target; step.transient.messages.post(copy); } } }
void item_system::handle_trigger_confirmations_as_pick_requests(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& confirmations = step.transient.messages.get_queue<messages::trigger_hit_confirmation_message>(); for (const auto& e : confirmations) { const auto detector = cosmos[e.detector_body]; auto* const item_slot_transfers = detector.find<components::item_slot_transfers>(); const auto item_entity = cosmos[e.trigger].get_owner_body(); const auto* const item = item_entity.find<components::item>(); if (item_slot_transfers && item && item_entity.get_owning_transfer_capability().dead()) { const auto& pick_list = item_slot_transfers->only_pick_these_items; const bool found_on_subscription_list = found_in(pick_list, item_entity); const bool item_subscribed = (pick_list.empty() && item_slot_transfers->pick_all_touched_items_if_list_to_pick_empty) || found_in(item_slot_transfers->only_pick_these_items, item_entity); if (item_subscribed) { const auto pickup_slot = detector.determine_pickup_target_slot_for(item_entity); const bool can_pick_already = item_slot_transfers->pickup_timeout.try_to_fire_and_reset(cosmos.get_timestamp(), delta); if (pickup_slot.alive() && can_pick_already) { const item_slot_transfer_request request(item_entity, pickup_slot); perform_transfer(request, step); } else { // TODO: post gui message } } } } }
void destruction_system::apply_damages_and_split_fixtures(const logic_step step) const { auto& cosmos = step.cosm; const auto delta = step.get_delta(); const auto& damages = step.transient.messages.get_queue<messages::damage_message>(); for (const auto& d : damages) { const auto subject = cosmos[d.subject]; if (subject.has<components::fixtures>()) { auto& fixtures = subject.get<components::fixtures>(); const auto& data_indices = d.subject_collider_and_convex_indices; const auto& coll = fixtures.get_collider_data(data_indices.first); if (coll.destructible) { auto& dest_data = fixtures.get_modifiable_destruction_data(data_indices); dest_data.scars.resize(1); dest_data.scars[0].first_impact = d.point_of_impact; dest_data.scars[0].depth_point = d.point_of_impact + d.impact_velocity; //LOG("Destructible fixture has been applied damage to with direction: %x", d.impact_velocity); } } } }
void movement_system::set_movement_flags_from_input(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& events = step.transient.messages.get_queue<messages::intent_message>(); for (const auto& it : events) { auto* const movement = cosmos[it.subject].find<components::movement>(); if (movement == nullptr) { continue; } switch (it.intent) { case intent_type::MOVE_FORWARD: movement->moving_forward = it.is_pressed; break; case intent_type::MOVE_BACKWARD: movement->moving_backward = it.is_pressed; break; case intent_type::MOVE_LEFT: movement->moving_left = it.is_pressed; break; case intent_type::MOVE_RIGHT: movement->moving_right = it.is_pressed; break; case intent_type::WALK: movement->walking_enabled = it.is_pressed; break; case intent_type::SPRINT: movement->sprint_enabled = it.is_pressed; break; default: break; } } }
void intent_contextualization_system::contextualize_crosshair_action_intents(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); auto& events = step.transient.messages.get_queue<messages::intent_message>(); for (auto& it : events) { entity_id callee; const auto subject = cosmos[it.subject]; const auto maybe_crosshair = subject[sub_entity_name::CHARACTER_CROSSHAIR]; if (it.intent == intent_type::MOVE_CROSSHAIR && maybe_crosshair.alive()) { it.subject = maybe_crosshair; continue; } if (subject.has<components::container>()) { if (it.intent == intent_type::CROSSHAIR_PRIMARY_ACTION) { const auto hand = subject[slot_function::PRIMARY_HAND]; if (hand.alive() && hand->items_inside.size() > 0) callee = hand.get_items_inside()[0]; } if (it.intent == intent_type::CROSSHAIR_SECONDARY_ACTION) { const auto hand = subject[slot_function::SECONDARY_HAND]; if (hand.alive() && hand->items_inside.size() > 0) callee = hand.get_items_inside()[0]; else { const auto prim_hand = subject[slot_function::PRIMARY_HAND]; if (prim_hand.alive() && prim_hand->items_inside.size() > 0) callee = prim_hand.get_items_inside()[0]; } } } const auto callee_handle = cosmos[callee]; if (callee_handle.alive()) { if (callee_handle.find<components::gun>()) { it.intent = intent_type::PRESS_GUN_TRIGGER; it.subject = callee; continue; } if (callee_handle.find<components::melee>()) { if (it.intent == intent_type::CROSSHAIR_PRIMARY_ACTION) it.intent = intent_type::MELEE_PRIMARY_MOVE; else if (it.intent == intent_type::CROSSHAIR_SECONDARY_ACTION) it.intent = intent_type::MELEE_SECONDARY_MOVE; it.subject = callee; continue; } } } }
void driver_system::release_drivers_due_to_requests(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& intents = step.transient.messages.get_queue<messages::intent_message>(); for (const auto& e : intents) { if (e.intent == intent_type::RELEASE_CAR && e.is_pressed) { release_car_ownership(cosmos[e.subject]); } } }
void intent_contextualization_system::contextualize_movement_intents(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); auto& intents = step.transient.messages.get_queue<messages::intent_message>(); for (auto& e : intents) { entity_id callee; bool callee_resolved = false; const auto subject = cosmos[e.subject]; const auto* const maybe_driver = subject.find<components::driver>(); const auto* const maybe_container = subject.find<components::container>(); if (maybe_driver && cosmos[maybe_driver->owned_vehicle].alive()) { if (e.intent == intent_type::MOVE_FORWARD || e.intent == intent_type::MOVE_BACKWARD || e.intent == intent_type::MOVE_LEFT || e.intent == intent_type::MOVE_RIGHT || e.intent == intent_type::WALK || e.intent == intent_type::SPRINT ) { callee = maybe_driver->owned_vehicle; callee_resolved = true; } else if (e.intent == intent_type::SPACE_BUTTON) { callee = maybe_driver->owned_vehicle; callee_resolved = true; e.intent = intent_type::HAND_BRAKE; } } if (!callee_resolved) { if (maybe_container) { if (e.intent == intent_type::SPACE_BUTTON) { const auto hand = subject[slot_function::PRIMARY_HAND]; if (hand.alive() && hand->items_inside.size() > 0) { e.intent = intent_type::MELEE_TERTIARY_MOVE; callee = hand.get_items_inside()[0]; callee_resolved = true; } } } } if (callee_resolved) { e.subject = callee; } } }
void melee_system::initiate_and_update_moves(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); /* - fixed delta timestep if it's a logic procedure - variable frame time if it is a rendering-time procedure */ const auto dt = static_cast<float>(delta.in_milliseconds()); /* clear melee swing response queue because this is the only place where we send them */ step.transient.messages.get_queue<messages::melee_swing_response>().clear(); /* all entities in the "targets" vector are guaranteed to have both melee and damage components therefore we use get instead of find */ const auto targets_copy = cosmos.get(processing_subjects::WITH_MELEE); for (const auto& t : targets_copy) { auto& melee = t.get<components::melee>(); auto& damage = t.get<components::damage>(); // LOG("P: %x, S: %x, T: %x CDT: %x MVT: %x STATE: %x", melee.primary_move_flag, melee.secondary_move_flag, melee.tertiary_move_flag,melee.swing_current_cooldown_time,melee.swing_current_time,melee.state); switch (melee.current_state) { case melee_state::FREE: if (melee.primary_move_flag) { melee.current_state = primary_action(step, dt, t, melee, damage); } /* send a response message so that the rest of the game knows that a swing has occured; the message could be read by particles system, audio system and possibly animation system to apply each their own effects. */ break; case melee_state::ONCOOLDOWN: melee.swing_current_cooldown_time += dt; if (melee.swing_current_cooldown_time >= melee.swings[0].cooldown_ms) { melee.swing_current_cooldown_time = 0; melee.current_state = melee_state::FREE; } break; case melee_state::PRIMARY: melee.current_state = primary_action(step,dt,t,melee,damage); break; default: LOG("Uknown action in melee_system.cpp"); } } }
void trigger_detector_system::post_trigger_requests_from_continuous_detectors(const logic_step step) const { auto& cosmos = step.cosm; const auto delta = step.get_delta(); auto targets_copy = cosmos.get(processing_subjects::WITH_TRIGGER_QUERY_DETECTOR); for (auto& t : targets_copy) { if (!t.get<components::trigger_query_detector>().detection_intent_enabled) { t.get<components::processing>().disable_in(processing_subjects::WITH_TRIGGER_QUERY_DETECTOR); } else { messages::trigger_hit_request_message request; request.detector = t; step.transient.messages.post(request); } } }
void driver_system::assign_drivers_from_successful_trigger_hits(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& confirmations = step.transient.messages.get_queue<messages::trigger_hit_confirmation_message>(); for (const auto& e : confirmations) { const auto& subject_car = cosmos[cosmos[e.trigger].get<components::trigger>().entity_to_be_notified]; if (subject_car.dead()) { continue; } const auto* const maybe_car = subject_car.find<components::car>(); if (maybe_car && e.trigger == maybe_car->left_wheel_trigger) { assign_car_ownership(cosmos[e.detector_body], subject_car); } } }
void item_system::handle_holster_item_intents(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& requests = step.transient.messages.get_queue<messages::intent_message>(); for (const auto& r : requests) { if (r.is_pressed && (r.intent == intent_type::HOLSTER_PRIMARY_ITEM || r.intent == intent_type::HOLSTER_SECONDARY_ITEM) ) { const auto subject = cosmos[r.subject]; if (subject.has<components::gui_element>() && subject.find<components::item_slot_transfers>()) { const auto hand_type = subject.map_primary_action_to_secondary_hand_if_primary_empty(intent_type::HOLSTER_SECONDARY_ITEM == r.intent).get_id().type; auto new_setup = components::gui_element::get_actual_selection_setup(subject); if (hand_type == slot_function::PRIMARY_HAND) { new_setup.primary_selection.unset(); } else if (hand_type == slot_function::SECONDARY_HAND) { new_setup.secondary_selection.unset(); } components::gui_element::apply_and_save_hotbar_selection_setup(step, new_setup, subject); } else if (subject.has<components::item_slot_transfers>()) { const auto hand = subject.map_primary_action_to_secondary_hand_if_primary_empty(intent_type::HOLSTER_SECONDARY_ITEM == r.intent); const auto item_inside = hand.get_item_if_any(); if (item_inside.alive()) { const auto holstering_slot = subject.determine_hand_holstering_slot_for(item_inside); if (holstering_slot.alive()) { const item_slot_transfer_request request(item_inside, holstering_slot); perform_transfer(request, step); } } } } } }
void animation_system::game_responses_to_animation_messages(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& movements = step.transient.messages.get_queue<movement_response>(); const auto& gunshots = step.transient.messages.get_queue<gunshot_response>(); for (auto it : movements) { animation_message msg; msg.subject = it.subject; msg.change_speed = true; msg.change_animation = true; msg.preserve_state_if_animation_changes = false; msg.action = ((it.speed <= 1.f) ? animation_message::STOP : animation_message::CONTINUE); if (!it.stop_response_at_zero_speed) msg.action = animation_message::CONTINUE; msg.animation_priority = 0; msg.set_animation = (*(cosmos[it.subject].get<components::animation_response>().response))[animation_response_type::MOVE]; msg.speed_factor = it.speed; step.transient.messages.post(msg); } for (auto it : gunshots) { // animation_message msg; // msg.preserve_state_if_animation_changes = false; // msg.change_animation = true; // msg.change_speed = true; // msg.speed_factor = 1.f; // msg.subject = it.subject; // msg.action = messages::animation_message::START; // msg.animation_priority = 1; // msg.set_animation = (*(it.subject.get<components::animation_response>().response))[animation_response_type::SHOT]; // // step.transient.messages.post(msg); } }
void driver_system::release_drivers_due_to_ending_contact_with_wheel(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& contacts = step.transient.messages.get_queue<messages::collision_message>(); const auto& physics = cosmos.systems_temporary.get<physics_system>(); for (const auto& c : contacts) { if (c.type == messages::collision_message::event_type::END_CONTACT) { const auto& driver = cosmos[c.subject]; const auto& car = cosmos[c.collider].get_owner_body(); const auto* const maybe_driver = driver.find<components::driver>(); if (maybe_driver) { if (maybe_driver->owned_vehicle == car) { release_car_ownership(driver); driver.get<components::movement>().make_inert_for_ms = 500.f; } } } } }
void item_system::handle_throw_item_intents(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& requests = step.transient.messages.get_queue<messages::intent_message>(); for (const auto& r : requests) { if (r.is_pressed && (r.intent == intent_type::THROW_PRIMARY_ITEM || r.intent == intent_type::THROW_SECONDARY_ITEM) ) { const auto subject = cosmos[r.subject]; if (subject.find<components::item_slot_transfers>()) { const auto hand = subject.map_primary_action_to_secondary_hand_if_primary_empty(intent_type::THROW_SECONDARY_ITEM == r.intent); const auto item_inside = hand.get_item_if_any(); if (item_inside.alive()) { perform_transfer({ item_inside, cosmos[inventory_slot_id()] }, step); } } } } }
void intent_contextualization_system::contextualize_use_button_intents(const logic_step step) { auto& cosmos = step.cosm; auto& delta = step.get_delta(); auto& intents = step.transient.messages.get_queue<messages::intent_message>(); for (auto& e : intents) { const auto subject = cosmos[e.subject]; const auto* const query_detector = subject.find<components::trigger_query_detector>(); const auto* const collision_detector = subject.find<components::trigger_collision_detector>(); if (e.intent == intent_type::USE_BUTTON) { const auto* const maybe_driver = subject.find<components::driver>(); if (maybe_driver) { const auto car_id = maybe_driver->owned_vehicle; const auto car = cosmos[car_id]; if (car.alive() && car.get<components::car>().current_driver == e.subject) { e.intent = intent_type::RELEASE_CAR; continue; } } if (query_detector) { e.intent = intent_type::QUERY_TOUCHING_TRIGGERS; continue; } } else if (e.intent == intent_type::START_PICKING_UP_ITEMS) { if (collision_detector) { e.intent = intent_type::DETECT_TRIGGER_COLLISIONS; continue; } } } }
void item_system::process_mounting_and_unmounting(const logic_step step) { ensure(false); auto& cosmos = step.cosm; const auto& delta = step.get_delta(); for (const auto& e : cosmos.get(processing_subjects::WITH_ITEM_SLOT_TRANSFERS)) { auto& item_slot_transfers = e.get<components::item_slot_transfers>(); const auto currently_mounted_item = cosmos[item_slot_transfers.mounting.current_item]; if (currently_mounted_item.alive()) { auto& item = currently_mounted_item.get<components::item>(); if (item.current_slot != item_slot_transfers.mounting.intented_mounting_slot) { item_slot_transfers.interrupt_mounting(); } else { ensure(item.intended_mounting != item.current_mounting); if (item.montage_time_left_ms > 0) { item.montage_time_left_ms -= static_cast<float>(delta.in_milliseconds()); } else { item.current_mounting = item.intended_mounting; if (item.current_mounting == components::item::UNMOUNTED) { perform_transfer({ currently_mounted_item, cosmos[item.target_slot_after_unmount] }, step); } } } } if (currently_mounted_item.dead()) { item_slot_transfers.mounting = components::item_slot_transfers::find_suitable_montage_operation(e); } } }
void animation_system::handle_animation_messages(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); const auto& events = step.transient.messages.get_queue<animation_message>(); for (auto it : events) { auto ptr = cosmos[it.subject].find<components::animation>(); if (!ptr) continue; auto& animation = *ptr; if (it.animation_priority >= animation.priority || animation.state == components::animation::playing_state::PAUSED) { if (it.change_speed) animation.speed_factor = it.speed_factor; //if (it.change_animation) { assets::animation_id new_instance = it.set_animation; if (new_instance != animation.current_animation) { animation.current_animation = new_instance; if (it.action == animation_message::CONTINUE) { it.action = animation_message::START; } //if (!it.preserve_state_if_animation_changes) { // animation.set_current_frame(0, it.subject); // animation.player_position_ms = 0.f; //} //else { /* update callback */ //animation.set_current_frame(animation.get_current_frame(), it.subject); //} } //} animation.priority = it.animation_priority; switch (it.action) { case animation_message::PAUSE: if (animation.state != components::animation::playing_state::PAUSED) { animation.paused_state = animation.state; animation.state = components::animation::playing_state::PAUSED; } break; case animation_message::STOP: animation.paused_state = components::animation::playing_state::PAUSED; animation.state = components::animation::playing_state::PAUSED; animation.set_current_frame(0); animation.player_position_ms = 0.f; break; case animation_message::START: animation.state = components::animation::playing_state::INCREASING; animation.set_current_frame(0); animation.player_position_ms = 0.f; break; case animation_message::CONTINUE: if (animation.state == components::animation::playing_state::PAUSED) { if (animation.paused_state == components::animation::playing_state::PAUSED) animation.paused_state = components::animation::playing_state::INCREASING; animation.state = animation.paused_state; } break; default: break; } } } step.transient.messages.get_queue<animation_message>().clear(); }
void animation_system::progress_animation_states(const logic_step step) { auto& cosmos = step.cosm; const auto& delta = step.get_delta(); for (const auto& it : cosmos.get(processing_subjects::WITH_ANIMATION)) { auto& animation_state = it.get<components::animation>(); if (animation_state.state != components::animation::playing_state::PAUSED) { auto& animation = *get_resource_manager().find(animation_state.current_animation); if (animation.frames.empty()) continue; animation_state.player_position_ms += static_cast<float>(delta.in_milliseconds()) * animation_state.speed_factor; while (true) { float frame_duration = animation.frames[animation_state.get_current_frame()].duration_milliseconds; if (animation_state.player_position_ms > frame_duration) { animation_state.player_position_ms -= frame_duration; if (animation.loop_mode == animation::loop_type::INVERSE) { if (animation_state.state == components::animation::playing_state::INCREASING) { if (animation_state.get_current_frame() < animation.frames.size() - 1) animation_state.increase_frame(); else { animation_state.decrease_frame(); animation_state.state = components::animation::playing_state::DECREASING; } } else if (animation_state.state == components::animation::playing_state::DECREASING) { if (animation_state.get_current_frame() > 0) animation_state.decrease_frame(); else { animation_state.increase_frame(); animation_state.state = components::animation::playing_state::INCREASING; } } } else if (animation.loop_mode == animation::loop_type::REPEAT) { if (animation_state.state == components::animation::playing_state::INCREASING) { if (animation_state.get_current_frame() < animation.frames.size() - 1) animation_state.increase_frame(); else animation_state.set_current_frame(0); } else if (animation_state.state == components::animation::playing_state::DECREASING) { if (animation_state.get_current_frame() > 0) animation_state.decrease_frame(); else animation_state.set_current_frame(animation.frames.size() - 1); } } else if (animation.loop_mode == animation::loop_type::NONE) { if (animation_state.state == components::animation::playing_state::INCREASING) { if (animation_state.get_current_frame() < animation.frames.size() - 1) animation_state.increase_frame(); else animation_state.state = components::animation::playing_state::PAUSED; } else if (animation_state.state == components::animation::playing_state::DECREASING) { if (animation_state.get_current_frame() > 0) animation_state.decrease_frame(); else animation_state.state = components::animation::playing_state::PAUSED; } } } else break; } auto& sprite = it.get<components::sprite>(); sprite = animation.frames[animation_state.get_current_frame()].sprite; } } }
void force_joint_system::apply_forces_towards_target_entities(logic_step& step) { auto& cosmos = step.cosm; const auto delta = step.get_delta(); for (const auto& it : cosmos.get(processing_subjects::WITH_FORCE_JOINT)) { if (!it.has<components::physics>()) continue; const auto& physics = it.get<components::physics>(); if (!physics.is_constructed()) continue; const auto& force_joint = it.get<components::force_joint>(); const auto& chased_entity = cosmos[force_joint.chased_entity]; if (chased_entity.dead()) continue; const auto& chased_entity_transform = chased_entity.logic_transform(); const auto& chased_transform = chased_entity_transform + force_joint.chased_entity_offset; auto direction = chased_transform.pos - physics.get_position(); const auto& distance = direction.length(); direction.normalize_hint(distance); if (force_joint.divide_transform_mode) { const auto current_transform = it.logic_transform(); const auto interpolated = augs::interp(current_transform, chased_transform, 1.0 - 1.0 / (1.0 + delta.in_seconds() * (60.0))); //LOG("Cur: %x,%x, Chas: %x,%x, Inter: %x,%x", current_transform.pos, current_transform.rotation, chased_entity_transform.pos, chased_entity_transform.rotation, interpolated.pos, interpolated.rotation); physics.set_transform(interpolated); } else { float force_length = force_joint.force_towards_chased_entity; if (distance < force_joint.distance_when_force_easing_starts) { auto mult = distance / force_joint.distance_when_force_easing_starts; force_length *= pow(mult, force_joint.power_of_force_easing_multiplier); } const auto& force_for_chaser = vec2(direction).set_length(force_length * 1.f - force_joint.percent_applied_to_chased_entity); const auto& force_for_chased = -force_for_chaser * force_joint.percent_applied_to_chased_entity; const bool is_force_epsilon = force_for_chaser.length() < 500; const auto& offsets = force_joint.force_offsets; const int& offsets_count = static_cast<int>(offsets.size()); //if (!is_force_epsilon) { for (const auto& offset : offsets) physics.apply_force(force_for_chaser * physics.get_mass() / offsets_count, offset); //LOG("F: %x, %x, %x", force_for_chaser, physics.velocity(), AS_INTV physics.get_position()); } //else if (is_force_epsilon && physics.velocity().is_epsilon(1.f)) { // physics.set_velocity(vec2(0, 0)); // //physics.set_transform(components::transform(chased_transform.pos, physics.get_angle())); // LOG("Zeroed"); //} if (force_for_chased.length() > 5) { const auto& chased_physics = cosmos[force_joint.chased_entity].get<components::physics>(); chased_physics.apply_force(force_for_chaser * chased_physics.get_mass()); } //if (force_joint.consider_rotation) // it.get<components::rotation_copying>().target_angle = chased_transform.rotation; //LOG("F: %x", physics.body->GetLinearDamping()); } } }