#include "behavior.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 namespace { uf::Serializer masterDataGet( const std::string& table, const std::string& key ) { uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& mastertable = scene.getComponent(); return mastertable["system"]["mastertable"][table][key]; } } #endif UF_BEHAVIOR_REGISTER_CPP(ext::PlayerBehavior) #define this (&self) void ext::PlayerBehavior::initialize( uf::Object& self ) { auto& transform = this->getComponent>(); auto& camera = this->getComponent(); camera.getTransform().reference = &transform; uf::Serializer& metadata = this->getComponent(); /* Load Config */ { struct { int mode = 0; struct { pod::Vector2 lr, bt, nf; } ortho; struct { pod::Math::num_t fov = 120; pod::Vector2 size = {640, 480}; pod::Vector2 bounds = {0.5, 128.0}; } perspective; pod::Vector3 offset = {0, 0, 0}; bool stereoscopic = true; } settings; uf::Camera& camera = this->getComponent(); settings.mode = metadata["camera"]["ortho"].as() ? -1 : 1; settings.perspective.size.x = metadata["camera"]["settings"]["size"]["x"].as(); settings.perspective.size.y = metadata["camera"]["settings"]["size"]["y"].as(); camera.setSize(settings.perspective.size); if ( settings.mode < 0 ) { settings.ortho.lr.x = metadata["camera"]["settings"]["left"].as(); settings.ortho.lr.y = metadata["camera"]["settings"]["right"].as(); settings.ortho.bt.x = metadata["camera"]["settings"]["bottom"].as(); settings.ortho.bt.y = metadata["camera"]["settings"]["top"].as(); settings.ortho.nf.x = metadata["camera"]["settings"]["near"].as(); settings.ortho.nf.y = metadata["camera"]["settings"]["far"].as(); camera.ortho( settings.ortho.lr, settings.ortho.bt, settings.ortho.nf ); } else { settings.perspective.fov = metadata["camera"]["settings"]["fov"].as(); settings.perspective.bounds.x = metadata["camera"]["settings"]["clip"]["near"].as(); settings.perspective.bounds.y = metadata["camera"]["settings"]["clip"]["far"].as(); camera.setFov(settings.perspective.fov); camera.setBounds(settings.perspective.bounds); } camera.setStereoscopic(true); settings.offset.x = metadata["camera"]["offset"][0].as(); settings.offset.y = metadata["camera"]["offset"][1].as(); settings.offset.z = metadata["camera"]["offset"][2].as(); pod::Transform<>& transform = camera.getTransform(); /* Transform initialization */ { transform.position.x = metadata["camera"]["position"][0].as(); transform.position.y = metadata["camera"]["position"][1].as(); transform.position.z = metadata["camera"]["position"][2].as(); transform.scale.x = metadata["camera"]["scale"][0].as(); transform.scale.y = metadata["camera"]["scale"][1].as(); transform.scale.z = metadata["camera"]["scale"][2].as(); } camera.setOffset(settings.offset); camera.update(true); // Update viewport if ( metadata["camera"]["settings"]["size"]["auto"].as() ) { this->addHook( "window:Resized", [&](ext::json::Value& json){ // Update persistent window sized (size stored to JSON file) pod::Vector2ui size; { size.x = json["window"]["size"]["x"].as(); size.y = json["window"]["size"]["y"].as(); } /* Update camera's viewport */ { uf::Camera& camera = this->getComponent(); camera.setSize({(pod::Math::num_t)size.x, (pod::Math::num_t)size.y}); } } ); } } metadata["system"]["control"] = true; this->addHook( "window:Mouse.CursorVisibility", [&](ext::json::Value& json){ metadata["system"]["control"] = !json["state"].as(); }); // Rotate Camera this->addHook( "window:Mouse.Moved", [&](ext::json::Value& json){ // discard events sent by os, only trust client now if ( !ext::json::isObject(json) ) return; if ( json["invoker"] != "client" ) return; pod::Vector2i delta = { json["mouse"]["delta"]["x"].as(), json["mouse"]["delta"]["y"].as() }; pod::Vector2i size = { json["mouse"]["size"]["x"].as(), json["mouse"]["size"]["y"].as() }; pod::Vector2 relta = { (float) delta.x / size.x, (float) delta.y / size.y }; relta *= 2; if ( delta.x == 0 && delta.y == 0 ) return; if ( !metadata["system"]["control"].as() ) return; bool updateCamera = false; uf::Camera& camera = this->getComponent(); pod::Transform<>& transform = this->getComponent>(); pod::Transform<>& cameraTransform = camera.getTransform(); auto& collider = this->getComponent(); if ( delta.x != 0 ) { double current, minima, maxima; { current = !ext::json::isNull( metadata["camera"]["limit"]["current"][0] ) ? metadata["camera"]["limit"]["current"][0].as() : NAN; minima = !ext::json::isNull( metadata["camera"]["limit"]["minima"][0] ) ? metadata["camera"]["limit"]["minima"][0].as() : NAN; maxima = !ext::json::isNull( metadata["camera"]["limit"]["maxima"][0] ) ? metadata["camera"]["limit"]["maxima"][0].as() : NAN; } if ( metadata["camera"]["invert"][0].as() ) relta.x *= -1; current += relta.x; if ( current != current || ( current < maxima && current > minima ) ) { if ( collider.body && !collider.shared ) { #if UF_USE_BULLET ext::bullet::applyRotation( collider, transform.up, relta.x ); #endif } else { uf::transform::rotate( transform, transform.up, relta.x ), updateCamera = true; } } else current -= relta.x; if ( !ext::json::isNull( metadata["camera"]["limit"]["current"][0] ) ) metadata["camera"]["limit"]["current"][0] = current; } if ( delta.y != 0 ) { double current, minima, maxima; { current = !ext::json::isNull( metadata["camera"]["limit"]["current"][1] ) ? metadata["camera"]["limit"]["current"][1].as() : NAN; minima = !ext::json::isNull( metadata["camera"]["limit"]["minima"][1] ) ? metadata["camera"]["limit"]["minima"][1].as() : NAN; maxima = !ext::json::isNull( metadata["camera"]["limit"]["maxima"][1] ) ? metadata["camera"]["limit"]["maxima"][1].as() : NAN; } if ( metadata["camera"]["invert"][1].as() ) relta.y *= -1; current += relta.y; if ( current != current || ( current < maxima && current > minima ) ) { // if ( collider.body && !collider.shared ) { // ext::bullet::applyRotation( collider, cameraTransform.right, relta.y ); // } else { uf::transform::rotate( cameraTransform, cameraTransform.right, relta.y ); // } updateCamera = true; } else current -= relta.y; if ( !ext::json::isNull( metadata["camera"]["limit"]["current"][1] ) ) metadata["camera"]["limit"]["current"][1] = current; } if ( updateCamera ) { camera.updateView(); } }); #if 0 this->addHook( ":Update.%UID%", [&](ext::json::Value& json){ // for ( auto& member : json[""]["transients"] ) { ext::json::forEach(metadata[""]["transients"], [&](ext::json::Value& member){ if ( member["type"] != "player" ) return; std::string id = member["id"].as(); metadata[""]["transients"][id]["hp"] = member["hp"]; metadata[""]["transients"][id]["mp"] = member["mp"]; }); }); // handle after battles and establishes cooldowns this->addHook( "world:Battle.End.%UID%", [&](ext::json::Value& json){ // update { uf::Serializer payload; payload[""]["transients"] = json["battle"]["transients"]; this->callHook( ":Update.%UID%", payload ); } metadata["system"].erase("battle"); metadata["system"]["cooldown"] = uf::physics::time::current + 5; }); // detect collision against transients, engage in battle this->addHook( "world:Collision.%UID%", [&](ext::json::Value& json){ if ( metadata["system"]["cooldown"].as() > uf::physics::time::current ) return; if ( !metadata["system"]["control"].as() ) return; std::string state = metadata["system"]["state"].as(); if ( state != "" && state != "null" ) return; uf::Scene& scene = uf::scene::getCurrentScene(); uf::Entity* entity = scene.findByUid(json["entity"].as()); if ( !entity ) return; uf::Serializer& pMetadata = entity->getComponent(); std::string onCollision = pMetadata["system"]["onCollision"].as(); if ( onCollision == "battle" ) { uf::Serializer payload; payload["battle"]["enemy"] = pMetadata[""]; payload["battle"]["enemy"]["uid"] = json["entity"].as(); payload["battle"]["player"] = metadata[""]; payload["battle"]["player"]["uid"] = this->getUid(); payload["battle"]["music"] = pMetadata["music"]; this->callHook("world:Battle.Start", payload); } else if ( onCollision == "npc" ) { if ( !uf::Window::isKeyPressed("E") ) return; uf::Serializer payload; payload["dialogue"] = pMetadata["dialogue"]; payload["uid"] = json["entity"].as(); this->callHook("menu:Dialogue.Start", payload); } // metadata["system"]["cooldown"] = uf::physics::time::current + 5; metadata["system"]["control"] = false; metadata["system"]["menu"] = onCollision; }); this->addHook( "world:Battle.End", [&](ext::json::Value& json){ metadata["system"]["menu"] = ""; metadata["system"]["cooldown"] = uf::physics::time::current + 5; // update { uf::Serializer payload; payload[""]["transients"] = json["battle"]["transients"]; this->callHook( ":Update.%UID%", payload ); } }); this->addHook( "menu:Dialogue.End", [&](ext::json::Value& json){ metadata["system"]["menu"] = ""; metadata["system"]["cooldown"] = uf::physics::time::current + 1; }); #endif #if 0 #if UF_USE_DISCORD // Discord Integration this->addHook( "discord.Activity.Update.%UID%", [&](ext::json::Value& json){ std::string leaderId = metadata[""]["party"][0].as(); uf::Serializer cardData = masterDataGet("Card", leaderId); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); std::string leader = charaData["name"].as(); uf::Serializer payload = json; payload["details"] = "Leader: " + leader; uf::hooks.call( "discord:Activity.Update", payload ); }); this->queueHook("discord.Activity.Update.%UID%", ext::json::null(), 1.0); #endif #endif #if !UF_ENTITY_METADATA_USE_JSON { auto& metadataBehavior = this->getComponent(); auto& metadataSystem = metadata["system"]; metadataBehavior.system.menu = metadataSystem["menu"].as(); metadataBehavior.system.control = metadataSystem["control"].as(); metadataBehavior.system.crouching = metadataSystem["crouching"].as(); metadataBehavior.system.noclipped = metadataSystem["noclipped"].as(); auto& metadataSystemPhysics = metadataSystem["physics"]; metadataBehavior.system.physics.impulse = metadataSystemPhysics["impulse"].as(); metadataBehavior.system.physics.rotate = metadataSystemPhysics["rotate"].as(); metadataBehavior.system.physics.move = metadataSystemPhysics["move"].as(); metadataBehavior.system.physics.run = metadataSystemPhysics["run"].as(); metadataBehavior.system.physics.walk = metadataSystemPhysics["walk"].as(); metadataBehavior.system.physics.collision = metadataSystemPhysics["collision"].as(); metadataBehavior.system.physics.jump = uf::vector::decode(metadataSystemPhysics["jump"], pod::Vector3f{}); metadataBehavior.system.physics.crouch = metadataSystemPhysics["crouch"].as(); auto& metadataAudioFootstep = metadata["audio"]["footstep"]; ext::json::forEach( metadataAudioFootstep["list"], [&]( const ext::json::Value& value ){ metadataBehavior.audio.footstep.list.emplace_back(value); }); metadataBehavior.audio.footstep.volume = metadataAudioFootstep["volume"].as(); } #endif } void ext::PlayerBehavior::tick( uf::Object& self ) { auto& camera = this->getComponent(); auto& transform = this->getComponent>(); auto& physics = this->getComponent(); auto& scene = uf::scene::getCurrentScene(); struct { bool forward; bool backwards; bool left; bool right; bool lookLeft; bool lookRight; bool running; bool walk; bool jump; bool crouch; bool paused; bool vee; } keys = { .forward = uf::Window::isKeyPressed("W"), .backwards = uf::Window::isKeyPressed("S"), .left = uf::Window::isKeyPressed("A"), .right = uf::Window::isKeyPressed("D"), .lookLeft = uf::Window::isKeyPressed("Left"), .lookRight = uf::Window::isKeyPressed("Right"), .running = uf::Window::isKeyPressed("LShift"), .walk = uf::Window::isKeyPressed("LAlt"), .jump = uf::Window::isKeyPressed(" "), .crouch = uf::Window::isKeyPressed("LControl"), .paused = uf::Window::isKeyPressed("Escape"), .vee = uf::Window::isKeyPressed("V"), }; if ( spec::controller::connected() ) { #if UF_USE_OPENVR if ( spec::controller::pressed( "R_DPAD_UP" ) ) keys.forward = true; if ( spec::controller::pressed( "R_DPAD_DOWN" ) ) keys.backwards = true; if ( spec::controller::pressed( "R_DPAD_LEFT" ) ) keys.lookLeft = true; // keys.left = true; if ( spec::controller::pressed( "R_DPAD_RIGHT" ) ) keys.lookRight = true; // keys.right = true; if ( spec::controller::pressed( "R_JOYSTICK" ) ) keys.running = true; if ( spec::controller::pressed( "R_A" ) ) keys.jump = true; if ( spec::controller::pressed( "L_DPAD_UP" ) ) keys.forward = true; if ( spec::controller::pressed( "L_DPAD_DOWN" ) ) keys.backwards = true; if ( spec::controller::pressed( "L_DPAD_LEFT" ) ) keys.lookLeft = true; if ( spec::controller::pressed( "L_DPAD_RIGHT" ) ) keys.lookRight = true; if ( spec::controller::pressed( "L_JOYSTICK" ) ) keys.crouch = true, keys.walk = true; if ( spec::controller::pressed( "L_A" ) ) keys.paused = true; #else if ( spec::controller::pressed( "DPAD_UP" ) ) keys.forward = true; if ( spec::controller::pressed( "DPAD_DOWN" ) ) keys.backwards = true; if ( spec::controller::pressed( "DPAD_LEFT" ) ) keys.lookLeft = true; if ( spec::controller::pressed( "DPAD_RIGHT" ) ) keys.lookRight = true; if ( spec::controller::pressed( "A" ) ) keys.jump = true; if ( spec::controller::pressed( "B" ) ) keys.running = true; if ( spec::controller::pressed( "X" ) ) keys.crouch = true, keys.walk = true; if ( spec::controller::pressed( "Y" ) ) keys.vee = true; if ( spec::controller::pressed( "L_TRIGGER" ) ) keys.left = true; if ( spec::controller::pressed( "R_TRIGGER" ) ) keys.right = true; if ( spec::controller::pressed( "START" ) ) keys.paused = true; #endif } struct { bool updateCamera = true; bool deltaCrouch = false; bool walking = false; bool floored = true; bool impulse = true; bool noclipped = false; std::string menu = ""; std::string targetAnimation = ""; } stats; auto& metadata = this->getComponent(); #if UF_ENTITY_METADATA_USE_JSON auto& metadataJson = this->getComponent(); auto& metadataSystem = metadataJson["system"]; metadata.system.menu = metadataSystem["menu"].as(); metadata.system.control = metadataSystem["control"].as(); metadata.system.crouching = metadataSystem["crouching"].as(); metadata.system.noclipped = metadataSystem["noclipped"].as(); auto& metadataSystemPhysics = metadataSystem["physics"]; metadata.system.physics.impulse = metadataSystemPhysics["impulse"].as(); metadata.system.physics.rotate = metadataSystemPhysics["rotate"].as(); metadata.system.physics.move = metadataSystemPhysics["move"].as(); metadata.system.physics.run = metadataSystemPhysics["run"].as(); metadata.system.physics.walk = metadataSystemPhysics["walk"].as(); metadata.system.physics.collision = metadataSystemPhysics["collision"].as(); metadata.system.physics.jump = uf::vector::decode(metadataSystemPhysics["jump"], pod::Vector3f{}); metadata.system.physics.crouch = metadataSystemPhysics["crouch"].as(); auto& metadataAudioFootstep = metadataJson["audio"]["footstep"]; ext::json::forEach( metadataAudioFootstep["list"], [&]( const ext::json::Value& value ){ metadata.audio.footstep.list.emplace_back(value); }); metadata.audio.footstep.volume = metadataAudioFootstep["volume"].as(); #endif stats.floored = fabs(physics.linear.velocity.y) < 0.01f; stats.menu = metadata.system.menu; stats.impulse = metadata.system.physics.impulse; stats.noclipped = metadata.system.noclipped; struct { float move = 4; float walk = 1; float run = 8; float rotate = uf::physics::time::delta; float limitSquared = 4*4; } speed; { speed.rotate *= metadata.system.physics.rotate; speed.move = metadata.system.physics.move; speed.run = metadata.system.physics.run / speed.move; speed.walk = metadata.system.physics.walk / speed.move; } if ( !metadata.system.physics.collision ) { stats.impulse = true; } if ( keys.running ) speed.move *= speed.run; else if ( keys.walk ) speed.move *= speed.walk; speed.limitSquared = speed.move * speed.move; uf::Object* menu = (uf::Object*) this->getRootParent().findByName("Gui: Menu"); if ( !menu ) stats.menu = ""; // make assumptions if ( stats.menu == "" && keys.paused ) { stats.menu = "paused"; metadata.system.control = false; uf::hooks.call("menu:Pause"); } else if ( !metadata.system.control ) { stats.menu = "menu"; } else if ( stats.menu == "" ) { metadata.system.control = true; } else { metadata.system.control = false; } metadata.system.menu = stats.menu; auto& collider = this->getComponent(); if ( metadata.system.control ) { { TIMER(0.25, keys.vee && ) { bool state = !stats.noclipped; metadata.system.noclipped = state; UF_DEBUG_MSG( (state ? "En" : "Dis") << "abled noclip: " << uf::vector::toString(transform.position)); if ( state ) { #if UF_USE_BULLET if ( collider.body ) { collider.body->setGravity(btVector3(0,0.0,0)); collider.body->setCollisionFlags(collider.body->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE); collider.body->setActivationState(DISABLE_SIMULATION); } #endif } else { #if UF_USE_BULLET if ( collider.body ) { collider.body->setGravity(btVector3(0,-9.81,0)); collider.body->setCollisionFlags(collider.body->getCollisionFlags() & ~btCollisionObject::CF_NO_CONTACT_RESPONSE); collider.body->setActivationState(DISABLE_DEACTIVATION); } #endif } stats.noclipped = state; } } if ( stats.floored ) { pod::Transform<> translator = transform; #if UF_USE_OPENVR if ( ext::openvr::context ) { bool useController = true; translator.orientation = uf::quaternion::multiply( transform.orientation * pod::Vector4f{1,1,1,1}, useController ? (ext::openvr::controllerQuaternion( vr::Controller_Hand::Hand_Right ) * pod::Vector4f{1,1,1,1}) : ext::openvr::hmdQuaternion() ); translator = uf::transform::reorient( translator ); translator.forward *= { 1, 0, 1 }; translator.right *= { 1, 0, 1 }; translator.forward = uf::vector::normalize( translator.forward ); translator.right = uf::vector::normalize( translator.right ); } #endif pod::Vector3f queued = {}; if ( keys.forward || keys.backwards ) { int polarity = keys.forward ? 1 : -1; float mag = uf::vector::magnitude(physics.linear.velocity * pod::Vector3{1, 0, 1}); if ( mag < speed.limitSquared ) { mag = uf::vector::magnitude(physics.linear.velocity + translator.forward * speed.move * polarity); } else mag = speed.limitSquared; pod::Vector3 correction = translator.forward * ::sqrt(mag) * polarity; if ( collider.body && !collider.shared ) { queued += correction; } else { if ( stats.impulse && stats.noclipped ) { physics.linear.velocity.x = correction.x; physics.linear.velocity.z = correction.z; } else { correction *= uf::physics::time::delta; transform.position.x += correction.x; transform.position.z += correction.z; } } stats.updateCamera = (stats.walking = true); } if ( keys.left || keys.right ) { int polarity = keys.right ? 1 : -1; float mag = uf::vector::magnitude(physics.linear.velocity * pod::Vector3{1, 0, 1}); if ( mag < speed.limitSquared ) { mag = uf::vector::magnitude(physics.linear.velocity + translator.right * speed.move * polarity); } else mag = speed.limitSquared; pod::Vector3 correction = translator.right * ::sqrt(mag) * polarity; if ( collider.body && !collider.shared ) { queued += correction; } else { if ( stats.impulse && stats.noclipped ) { physics.linear.velocity.x = correction.x; physics.linear.velocity.z = correction.z; } else { correction *= uf::physics::time::delta; transform.position.x += correction.x; transform.position.z += correction.z; } } stats.updateCamera = (stats.walking = true); } if ( keys.left || keys.right || keys.forward || keys.backwards ) { if ( collider.body && !collider.shared ) { physics.linear.velocity.x = queued.x; physics.linear.velocity.z = queued.z; #if UF_USE_BULLET ext::bullet::move( collider, physics.linear.velocity ); #endif } } if ( !keys.forward && !keys.backwards && !keys.left && !keys.right ) { if ( collider.body && !collider.shared ) { physics.linear.velocity.x = 0; physics.linear.velocity.z = 0; #if UF_USE_BULLET ext::bullet::move( collider, physics.linear.velocity ); #endif } } if ( keys.jump ) { pod::Vector3f yump = metadata.system.physics.jump; if ( collider.body && !collider.shared ) { if ( fabs(yump.x) > 0.001f ) physics.linear.velocity.x = yump.x; if ( fabs(yump.y) > 0.001f ) physics.linear.velocity.y = yump.y; if ( fabs(yump.z) > 0.001f ) physics.linear.velocity.z = yump.z; #if UF_USE_BULLET ext::bullet::move( collider, physics.linear.velocity ); #endif } else { transform.position += yump * uf::physics::time::delta; } } } if ( keys.lookLeft ) { if ( collider.body && !collider.shared ) { #if UF_USE_BULLET ext::bullet::applyRotation( collider, transform.up, -speed.rotate ); #endif } else { uf::transform::rotate( transform, transform.up, -speed.rotate ); } stats.updateCamera = true; } if ( keys.lookRight ) { if ( collider.body && !collider.shared ) { #if UF_USE_BULLET ext::bullet::applyRotation( collider, transform.up, speed.rotate ); #endif } else { uf::transform::rotate( transform, transform.up, speed.rotate ); } stats.updateCamera = true; } if ( keys.crouch ) { pod::Vector3f yump = metadata.system.physics.jump; if ( stats.noclipped ) { if ( collider.body && !collider.shared ) { if ( fabs(yump.x) > 0.001f ) physics.linear.velocity.x = -yump.x; if ( fabs(yump.y) > 0.001f ) physics.linear.velocity.y = -yump.y; if ( fabs(yump.z) > 0.001f ) physics.linear.velocity.z = -yump.z; #if UF_USE_BULLET ext::bullet::move( collider, physics.linear.velocity ); #endif } } else { if ( !metadata.system.physics.collision ) { transform.position -= yump * uf::physics::time::delta; } else { if ( !metadata.system.crouching ) stats.deltaCrouch = true; metadata.system.crouching = true; } } } else { if ( metadata.system.crouching ) stats.deltaCrouch = true; metadata.system.crouching = false; } } if ( stats.noclipped && !keys.forward && !keys.backwards && !keys.left && !keys.right && !keys.jump && !keys.crouch ) { if ( collider.body && !collider.shared ) { physics.linear.velocity = {}; #if UF_USE_BULLET ext::bullet::move( collider, physics.linear.velocity ); #endif } } if ( stats.deltaCrouch ) { float delta = metadata.system.physics.crouch; if ( metadata.system.crouching ) camera.getTransform().position.y -= delta; else camera.getTransform().position.y += delta; stats.updateCamera = true; } #if UF_USE_OPENAL if ( stats.floored ) { if ( stats.walking ) { auto& emitter = this->getComponent(); int cycle = rand() % metadata.audio.footstep.list.size(); std::string filename = metadata.audio.footstep.list[cycle]; uf::Audio& footstep = emitter.add(filename); bool playing = false; for ( uint i = 0; i < metadata.audio.footstep.list.size(); ++i ) { uf::Audio& audio = emitter.add(metadata.audio.footstep.list[i]); if ( audio.playing() ) playing = true; } if ( !playing ) { footstep.play(); footstep.setVolume(metadata.audio.footstep.volume); footstep.setPosition( transform.position ); // [0, 1] float modulation = (rand() % 100) / 100.0; // [0, 0.1] modulation *= 0.1f; // [-0.05, 0.05] modulation -= 0.05f; if ( keys.running ) modulation += 0.5f; footstep.setPitch(1 + modulation); } // set animation to walk stats.targetAnimation = "walk"; } else if ( !keys.jump ) { stats.targetAnimation = "idle_wank"; } } #endif #if !UF_ENV_DREAMCAST // set animation to idle if ( stats.targetAnimation != "" ) { auto* playerModel = this->findByName("Player: Model"); if ( playerModel && playerModel->hasComponent() ) { auto& graph = playerModel->getComponent(); bool should = true; if ( graph.sequence.empty() && graph.sequence.front() == stats.targetAnimation ) should = false; if ( should ) { graph.settings.animations.loop = true; uf::graph::animate( graph, stats.targetAnimation ); } } } #endif #if UF_USE_LUA #define TRACK_ORIENTATION(ORIENTATION) {\ static pod::Quaternion<> storedCameraOrientation = ORIENTATION;\ const pod::Quaternion<> prevCameraOrientation = storedCameraOrientation;\ const pod::Quaternion<> curCameraOrientation = ORIENTATION;\ const pod::Quaternion<> deltaOrientation = uf::quaternion::multiply( curCameraOrientation, uf::quaternion::inverse( prevCameraOrientation ) ) ;\ const pod::Vector3f deltaAngles = uf::quaternion::eulerAngles( deltaOrientation );\ combinedDeltaAngles = uf::vector::add( combinedDeltaAngles, deltaAngles );\ combinedDeltaOrientation = uf::quaternion::multiply( deltaOrientation, combinedDeltaOrientation );\ storedCameraOrientation = ORIENTATION;\ } { pod::Quaternion<> combinedDeltaOrientation = {0,0,0,1}; pod::Vector3f combinedDeltaAngles = {}; TRACK_ORIENTATION(transform.orientation); TRACK_ORIENTATION(camera.getTransform().orientation); float magnitude = uf::quaternion::magnitude( combinedDeltaOrientation ); if ( magnitude > 0.0001f ) { uf::Serializer payload; payload["delta"] = uf::vector::encode( combinedDeltaOrientation ); payload["angle"]["pitch"] = combinedDeltaAngles.x; payload["angle"]["yaw"] = combinedDeltaAngles.y; payload["angle"]["roll"] = combinedDeltaAngles.z; payload["magnitude"] = magnitude; this->callHook("controller:Camera.Rotated", payload); } } #endif if ( stats.updateCamera ) camera.updateView(); #if UF_ENTITY_METADATA_USE_JSON auto& metadataJson = this->getComponent(); auto& metadataSystem = metadataJson["system"] metadataSystem["menu"] = metadata.system.menu; metadataSystem["control"] = metadata.system.control; metadataSystem["crouching"] = metadata.system.crouching; metadataSystem["noclipped"] = metadata.system.noclipped; auto& metadataSystemPhysics = metadataSystem["physics"] metadataSystemPhysics["impulse"] = metadata.system.physics.impulse; metadataSystemPhysics["rotate"] = metadata.system.physics.rotate; metadataSystemPhysics["move"] = metadata.system.physics.move; metadataSystemPhysics["run"] = metadata.system.physics.run; metadataSystemPhysics["walk"] = metadata.system.physics.walk; metadataSystemPhysics["collision"] = metadata.system.physics.collision; metadataSystemPhysics["jump"] = uf::vector::encode(metadata.system.physics.jump); metadataSystemPhysics["crouch"] = metadata.system.physics.crouch; auto& metadataAudioFootstep = metadata["audio"]["footstep"]; metadataAudioFootstep["list"] = metadata.audio.footstep.list; metadataAudioFootstep["volume"] = metadata.audio.footstep.volume #endif } void ext::PlayerBehavior::render( uf::Object& self ){} void ext::PlayerBehavior::destroy( uf::Object& self ){} #undef this