diff --git a/Auxiliary b/Auxiliary index 25b0f79f1..7f384831c 160000 --- a/Auxiliary +++ b/Auxiliary @@ -1 +1 @@ -Subproject commit 25b0f79f18ce398a013749ec9da2e0ddaaae0f9b +Subproject commit 7f384831c918751fcd9b01f6349b22e658645f35 diff --git a/Data/Scripts/aschar.as b/Data/Scripts/aschar.as index 420b3b1df..59342f4cf 100644 --- a/Data/Scripts/aschar.as +++ b/Data/Scripts/aschar.as @@ -26,6 +26,7 @@ #include "aschar_aux.as" #include "aircontrols.as" #include "aircontrols_params.as" +#include "tween.as" enum WalkDir { WALK_BACKWARDS, @@ -187,6 +188,7 @@ float body_bob_freq = 0.0f; float body_bob_time_offset; // Parameter values +float p_idlesway; float p_aggression; float p_ground_aggression; float p_damage_multiplier; @@ -552,6 +554,55 @@ class PredictPathOutput { vec3 normal; }; +class DialogueMorph { + bool morphing = false; + string morph_name; + float start_value; + float end_value; + float morph_speed; + int tween_type; + + float timer = 0.0f; + float morph_value = 0.0f; + + DialogueMorph(string _morph_name){ + morph_name = _morph_name; + morph_value = 0.0f; + // Make sure the morph targets are set to zero by default. + this_mo.rigged_object().SetMorphTargetWeight(morph_name, morph_value, 1.0f); + } + + void SetMorphTarget(float target_value, float duration, string tween_name){ + // Use the current morph value as a starting point. It doesn't matter if it's 0.0f, 1.0f or halfway somewhere. + start_value = morph_value; + end_value = target_value; + tween_type = GetTweenType(tween_name); + timer = 0.0f; + // Make sure speed calculation doesn't divide by zero. + morph_speed = 1.0f / max(0.001, duration); + morphing = true; + } + + void Update(){ + // Skip setting the morph target when the target has been reached. + if(!morphing){return;} + + timer += time_step * morph_speed; + // Apply the tween on the timer that goes from 0.0 - 1.0. + float tweened_timer = ApplyTween(timer, tween_type); + // The morph value is stored in this class and can be used again with the next target value. + morph_value = mix(start_value, end_value, tweened_timer); + this_mo.rigged_object().SetMorphTargetWeight(morph_name, morph_value, 1.0f); + + // Once the target has been reached, stop updating this morph target. + if(timer >= 1.0){ + morphing = false; + } + } +} + +array dialogue_morphs; + // the main timer of the script, used whenever anything has to know how much time has passed since something else happened. float time = 0; @@ -3507,7 +3558,7 @@ void HandleSpecialKeyPresses() { "recovery_time = 2.0f;"); } } - +/* if(GetInputPressed(this_mo.controller_id, "debug_switch_to_combat_rabbit")) { int rand_int = rand() % 3; @@ -3603,7 +3654,7 @@ void HandleSpecialKeyPresses() { if(GetInputPressed(this_mo.controller_id, "debug_switch_to_rabbot")) { SwitchCharacter("Data/Characters/rabbot.xml"); } - +*/ if(GetInputPressed(this_mo.controller_id, "debug_misc_action")) { const bool kTestIdleAntic = false; @@ -5333,6 +5384,10 @@ int BlockedAttack(const vec3 &in dir, const vec3 &in pos, int attacker_id) { if(attack_getter2.GetFleshUnblockable() == 0) { level.SendMessage("active_blocked " + this_mo.getID() + " " + attacker_id); + if(this_mo.controlled) { + level.SendMessage("player_active_blocked"); + level.SendMessage("player_gainedpoint"); + } sound = "Data/Sounds/hit/hit_block.xml"; MakeParticle("Data/Particles/impactfast.xml", pos, vec3(0.0f)); MakeParticle("Data/Particles/impactslow.xml", pos, vec3(0.0f)); @@ -5667,6 +5722,10 @@ int HitByAttack(const vec3 &in dir, const vec3 &in pos, int attacker_id, float a block_stunned = 1.0; } + if(permanent_health <= -0.5f && knocked_out == _dead && !this_mo.controlled) { + level.SendMessage("enemy_overkilled"); + } + ReceiveMessage("notice " + attacker_id); if(this_mo.controlled && (state == _ragdoll_state || state == _ground_state) && knocked_out == _awake) { @@ -5688,6 +5747,11 @@ int HitByAttack(const vec3 &in dir, const vec3 &in pos, int attacker_id, float a (attack_getter2.GetHeight() == _high && duck_amount >= 0.5f) || (attack_getter2.GetHeight() == _low && !on_ground)) { level.SendMessage("dodged " + this_mo.getID() + " " + attacker_id); + if(this_mo.controlled) { + level.SendMessage("player_dodged"); + level.SendMessage("player_gainedpoint"); + } + return _miss; } @@ -5695,6 +5759,12 @@ int HitByAttack(const vec3 &in dir, const vec3 &in pos, int attacker_id, float a if(this_mo.controlled) { camera_shake += 1.0f; // Shake camera if player is hit + level.SendMessage("player_damaged"); + level.SendMessage("player_lostpoint"); + } + if(!this_mo.controlled) { + level.SendMessage("ai_damaged"); + level.SendMessage("ai_lostpoint"); } if(tether_id != attacker_id) { @@ -5907,6 +5977,11 @@ int HitByAttack(const vec3 &in dir, const vec3 &in pos, int attacker_id, float a } TakeSharpDamage(sharp_damage * attack_damage_mult, pos, attacker_id, true); + if(this_mo.controlled) { + level.SendMessage("player_took_sharp_damage"); + } else { + level.SendMessage("enemy_took_sharp_damage"); + } } } @@ -6009,6 +6084,10 @@ int HitByAttack(const vec3 &in dir, const vec3 &in pos, int attacker_id, float a } level.SendMessage("knocked_over " + this_mo.getID() + " " + attacker_id); + if(this_mo.controlled) { + level.SendMessage("player_knocked_over"); + } + if(knocked_out == _dead && old_knocked_out != _dead) { PlaySoundGroup("Data/Sounds/hit/hit_hard.xml", pos, sound_priority); @@ -6302,6 +6381,22 @@ void SetKnockedOut(int val) { if(!this_mo.controlled) { AchievementEvent("enemy_died"); + level.SendMessage("enemy_died"); + } + if(!this_mo.controlled) { + AchievementEvent("enemy_died"); + level.SendMessage("enemy_died"); + + array player_ids = GetPlayerCharacterIDs(); + if(player_ids.size() != 0) { + MovementObject@ player_char = ReadCharacterID(player_ids[0]); + + const float kDistanceThreshold = 10.0 * 10.0; + + if(distance_squared(this_mo.position, player_char.position) < kDistanceThreshold * 9){ + level.SendMessage("enemy_killed"); + } + } } } @@ -6311,6 +6406,7 @@ void SetKnockedOut(int val) { if(!this_mo.controlled) { AchievementEvent("enemy_ko"); + level.SendMessage("enemy_ko"); } } @@ -6383,6 +6479,9 @@ void ReceiveMessage(string msg) { RecoverHealth(); } else if(token == "full_revive") { Recover(); + } else if(token == "knockout") { + SetKnockedOut(_unconscious); + Ragdoll(_RGDL_FALL); } else if(token == "fall_death") { fall_death = true; } else if(token == "tutorial") { @@ -6494,6 +6593,27 @@ void ReceiveMessage(string msg) { } else { Log(error, "Couldn't find file \"" + token + "\""); } + } else if(token == "set_morph_target") { + token_iter.FindNextToken(msg); + string morph_name = token_iter.GetToken(msg); + + token_iter.FindNextToken(msg); + float target_value = atof(token_iter.GetToken(msg)); + + token_iter.FindNextToken(msg); + float duration = atof(token_iter.GetToken(msg)); + + token_iter.FindNextToken(msg); + string tween_name = token_iter.GetToken(msg); + + for(uint i = 0; i < dialogue_morphs.size(); i++){ + if(dialogue_morphs[i].morph_name == morph_name){ + // Find the correct morph target by name and set the target value. + dialogue_morphs[i].SetMorphTarget(target_value, duration, tween_name); + break; + } + } + } else if(token == "set_eye_dir") { // Get params token_iter.FindNextToken(msg); @@ -6739,9 +6859,15 @@ void CharacterDefeated() { const float kDistanceThreshold = 10.0 * 10.0; if(zone_killed == 0) { + if(!this_mo.controlled && distance_squared(this_mo.position, player_char.position) < kDistanceThreshold * 9) { + level.SendMessage("enemy_eliminated"); + } if(this_mo.controlled || (IsAggro() == 1 && num_threats == 0 && distance_squared(this_mo.position, player_char.position) < kDistanceThreshold)) { TimedSlowMotion(0.1f, 0.7f, 0.05f); } + } else if(!this_mo.controlled && distance_squared(this_mo.position, player_char.position) < kDistanceThreshold * 9) { + level.SendMessage("enemy_zone_killed"); + level.SendMessage("enemy_eliminated"); } } } @@ -6754,8 +6880,11 @@ void TakeDamage(float how_much) { if(this_mo.controlled) { AchievementEventFloat("player_damage", how_much); + level.SendMessage("player_damage" + how_much); + level.SendMessage("player_lostpoint"); } else { AchievementEventFloat("ai_damage", how_much); + level.SendMessage("ai_damage" + how_much); } HandleAIEvent(_damaged); @@ -6798,6 +6927,7 @@ void TakeBloodDamage(float how_much) { if(this_mo.controlled) { AchievementEventFloat("player_blood_loss", how_much); + level.SendMessage("player_blood_loss" + how_much); } HandleAIEvent(_damaged); @@ -6868,6 +6998,8 @@ void AttachWeapon(int which) { this_mo.AttachItemToSlot(which, _at_grip, mirror); HandleEditorAttachment(which, _at_grip, mirror); } + + level.SendMessage("character_item_pickup " + this_mo.getID() + " " + which); } void HandleEditorAttachment(int which, int attachment_type, bool mirror) { @@ -7208,6 +7340,8 @@ void HandleAnimationCombatEvent(const string &in event, const vec3 &in world_pos if(event == "golimp") { if(this_mo.controlled) { AchievementEvent("player_was_hit"); + level.SendMessage("player_was_hit"); + level.SendMessage("player_lostpoint"); } if(state == _hit_reaction_state && hit_reaction_thrown) { @@ -7784,8 +7918,10 @@ void UpdateGroundAttackControls(const Timestep &in ts) { } else if(attack_id != -1) { if(this_mo.controlled) { AchievementEvent("player_attacked"); + level.SendMessage("player_attacked"); } else { AchievementEvent("ai_attacked"); + level.SendMessage("ai_attacked"); } ++num_strikes; @@ -7837,6 +7973,7 @@ void UpdateGroundAttackControls(const Timestep &in ts) { } else { if(this_mo.controlled) { AchievementEvent("player_attacked"); + level.SendMessage("player_attacked"); } breath_speed += 2.0f; @@ -9789,6 +9926,9 @@ void UpdateHitReaction(const Timestep &in ts) { if(this_mo.rigged_object().GetStatusKeyValue("escape")>=1.0f && WantsToCounterThrow() && !block_reaction_anim_set) { level.SendMessage("character_throw_escape " + this_mo.getID() + " " + target_id); + if(this_mo.controlled) { + level.SendMessage("player_gainedpoint"); + } this_mo.SwapAnimation(attack_getter2.GetThrownCounterAnimPath()); this_mo.rigged_object().anim_client().SetAnimationCallback("void EndHitReaction()"); string sound = "Data/Sounds/weapon_foley/swoosh/weapon_whoos_big.xml"; @@ -9913,6 +10053,7 @@ void WakeUp(int how) { } else if(how == _wake_roll) { if(this_mo.controlled) { AchievementEvent("player_wake_roll"); + level.SendMessage("player_wake_roll"); } SetOnGround(true); @@ -11479,6 +11620,7 @@ void PostReset() { target_rotation2 = 0; cam_rotation = target_rotation; cam_rotation2 = target_rotation2; + SetupDialogueMorphs(); if(this_mo.controlled) { SetCameraFromFacing(); @@ -14405,7 +14547,7 @@ void FinalAnimationMatrixUpdate(int num_frames) { vec3 body_offset(0.0f); - if(idle_stance_amount > 0.0f && !sitting && !asleep) { + if(idle_stance_amount > 0.0f && !sitting && !asleep && p_idlesway != 0.0f) { vec3 left_foot = (key_transforms[kLeftLegKey] * inv_skeleton_bind_transforms[ik_chain_elements[ik_chain_start_index[kLeftLegIK]]]).origin; vec3 right_foot = (key_transforms[kRightLegKey] * inv_skeleton_bind_transforms[ik_chain_elements[ik_chain_start_index[kRightLegIK]]]).origin; @@ -14421,7 +14563,7 @@ void FinalAnimationMatrixUpdate(int num_frames) { com_offset_vel += (target_com_offset - com_offset) * 5.0f * time_step * num_frames; com_offset += com_offset_vel * time_step * num_frames; vec3 target_com = (right_foot - left_foot) * com_offset.x + this_mo.GetFacing() * com_offset.z; - float body_shift_amount = idle_stance_amount; + float body_shift_amount = idle_stance_amount * p_idlesway; if(dialogue_control) { body_shift_amount = 1.0; @@ -14434,7 +14576,7 @@ void FinalAnimationMatrixUpdate(int num_frames) { vec3 axis = right_foot - left_foot; axis = vec3(axis.z, 0.0f, -axis.x); axis = normalize(axis); - quaternion rotate_x(vec4(axis.x, axis.y, axis.z, com_offset.x * 0.25f * body_shift_amount)); + quaternion rotate_x(vec4(axis.x, axis.y, axis.z, com_offset.x * 0.25f * body_shift_amount * p_idlesway)); key_transforms[kChestKey].rotation = rotate_x * key_transforms[kChestKey].rotation; key_transforms[kHipKey].rotation = rotate_x * key_transforms[kHipKey].rotation; key_transforms[kHeadKey].origin -= key_transforms[kChestKey].origin; @@ -14851,9 +14993,33 @@ void FinalAnimationMatrixUpdate(int num_frames) { UpdateEyeLook(); LeaveTelemetryZone(); + UpdateDialogueMorph(); + LeaveTelemetryZone(); } +void SetupDialogueMorphs(){ + dialogue_morphs.resize(0); + + dialogue_morphs.insertLast(DialogueMorph("afraid")); + dialogue_morphs.insertLast(DialogueMorph("angry")); + dialogue_morphs.insertLast(DialogueMorph("diss")); + dialogue_morphs.insertLast(DialogueMorph("frown")); + dialogue_morphs.insertLast(DialogueMorph("mouth_open")); + dialogue_morphs.insertLast(DialogueMorph("sad")); + dialogue_morphs.insertLast(DialogueMorph("kidding")); + dialogue_morphs.insertLast(DialogueMorph("shocked")); + dialogue_morphs.insertLast(DialogueMorph("shock")); + dialogue_morphs.insertLast(DialogueMorph("smile")); + dialogue_morphs.insertLast(DialogueMorph("think")); +} + +void UpdateDialogueMorph(){ + for(uint i = 0; i < dialogue_morphs.size(); i++){ + dialogue_morphs[i].Update(); + } +} + void DoChestIK(float chest_tilt_offset, float angle_threshold, float torso_damping, float torso_stiffness, int num_frames) { if(true) { this_mo.CDoChestIK(chest_tilt_offset, angle_threshold, torso_damping, torso_stiffness, num_frames); @@ -15384,6 +15550,9 @@ void SetParameters() { max_ko_shield = max(0, params.GetInt("Knockout Shield")); ko_shield = max_ko_shield; + params.AddFloatSlider("Idle Sway", 1, "min:0,max:2,step:0.1,text_mult:100"); + p_idlesway = min(2.0f, max(0.0f, params.GetFloat("Idle Sway"))); + params.AddFloatSlider("Aggression", 0.5, "min:0,max:1,step:0.1,text_mult:100"); p_aggression = min(1.0f, max(0.0f, params.GetFloat("Aggression")));