#include common_scripts\utility; #include maps\mp\_utility; #using_animtree( "animated_props_mp_descent" ); CONST_EARTHQUAKE_RANGE = 5000; CONST_DEFAULT_PREFALL_TIME = 1.5; CONST_DEFAULT_FALL_TIME = 3.0; CONST_DEFAULT_SETTLE_TIME = 1.5; CONST_DEFAULT_MAX_ROLL = 6; CONST_DEFAULT_MIN_ROLL = 4; CONST_DEFAULT_MAX_PITCH = 3; CONST_DEFAULT_MIN_PITCH = 2; CONST_FALL_EXPLODER_ID = 1; CONST_GLASS_BUILDING_EXPLODER_ID = 3; CONST_CONCRETE_FACADE_NAME = "concrete_building"; CONST_CONCRETE_FACADE_RUIN_MODEL = "desc_building_destroyed_02"; CONST_GLASS_FACADE_NAME = "facade_glass_"; CONST_COLUMN_FALL_ANIM = "mp_descent_column_collapsing"; CONST_COLUMN_FALL_SFX = "scn_dest_collapse_fall"; CONST_COLUMN_IMPACT_SFX = "scn_dest_collapse_impact"; CONST_COLUMN_IMPACT_VFX_EXPLODER_ID = 23; CONST_COLUMN_IMPACT_OFFSET = 180; // units below origin CONST_COLUMN_IMPACT_DELAY = 3.5; // in seconds CONST_COLUMN_DAMAGE_RADIUS = 192; CONST_COLUMN_DAMAGE = 500; CONST_SNIPER_DUCT_ANGLE = -90; CONST_SNIPER_DUCT_TIME = 1; CONST_EVENT_TRIGGER_FALL_DELAY_MIN = 3; // in seconds CONST_EVENT_TRIGGER_FALL_DELAY_MAX = 6; // in seconds main() { maps\mp\mp_descent_new_precache::main(); maps\createart\mp_descent_new_art::main(); maps\mp\mp_descent_new_fx::main(); maps\mp\_load::main(); maps\mp\_compass::setupMiniMap( "compass_map_mp_descent_new" ); setdvar( "r_lightGridEnableTweaks", 1 ); setdvar( "r_lightGridIntensity", 1.33 ); setdvar_cg_ng( "r_specularColorScale", 2.5 , 2.5 ); if ( level.ps3 ) { SetDvar( "sm_sunShadowScale", "0.5" ); // ps3 optimization SetDvar( "sm_sunsamplesizenear", ".15" ); } else if( level.xenon ) { SetDvar( "sm_sunShadowScale", "0.8" ); SetDvar( "sm_sunsamplesizenear", ".25" ); } game["attackers"] = "allies"; game["defenders"] = "axis"; game[ "allies_outfit" ] = "urban"; game[ "axis_outfit" ] = "elite"; // level thread fall_objects(); // level thread tilt_objects(); // level thread hang_objects(); // level thread gap_objects(); // level thread world_tilt(); level thread watersheet_trig_setup(); /# thread debugDescent(); #/ // level thread doubleDoorCreate( "door1" ); // animated props for fall events animLength = GetAnimLength( %mp_descent_light01_fall ); animateScriptableProps( "animated_model_descent_light01", animLength ); animLength = GetAnimLength( %mp_descent_light02_fall ); animateScriptableProps( "animated_model_descent_light02", animLength ); animLength = GetAnimLength( %mp_descent_light03_fall ); animateScriptableProps( "animated_model_descent_light03", animLength ); animLength = GetAnimLength( %mp_descent_light04_fall ); animateScriptableProps( "animated_model_descent_light04", animLength ); animLength = GetAnimLength( %mp_descent_phone01_fall ); animateScriptableProps( "animated_model_descent_phone01", animLength ); animLength = GetAnimLength( %mp_descent_phone02_fall ); animateScriptableProps( "animated_model_descent_phone02", animLength ); animLength = GetAnimLength( %mp_descent_microwave_fall ); animateScriptableProps( "animated_model_descent_microwave", animLength ); animLength = GetAnimLength( %mp_descent_tv01_fall ); animateScriptableProps( "animated_model_descent_tv01", animLength ); // whiteboard and bulletin board use the same anim animLength = GetAnimLength( %mp_descent_whiteboard_fall ); animateScriptableProps( "animated_model_descent_whiteboard", animLength ); animateScriptableProps( "animated_model_descent_bulletinboard", animLength ); animLength = GetAnimLength( %mp_descent_kitchen_fall ); animateScriptableProps( "animated_model_descent_kitchen", animLength ); // falling walls and columns // moverCreate( "side_wall_fall", "trigger_movers" ); // elevator // elevator = GetEnt( "elevator", "targetname" ); // building shake events level setupBuildingCollapse(); // setupBuildingFx(); level thread connect_watch(); // level thread spawn_watch(); thread setupCollapsingColumn( "column" ); thread setupSniperDuct( "sniper_mover" ); // thread animatedMoverCreate( "collapse", "mp_descent_wallscollapse_anim" ); } connect_watch() { while(1) { level waittill("connected", player); thread connect_watch_endofframe(player); } } connect_watch_endofframe(player) { player endon("death"); // self waittill( "player_spawned" ); waittillframeend; // so that spectators see it too? if(IsDefined(level.vista)) player PlayerSetGroundReferenceEnt(level.vista); } spawn_watch() { while(1) { level waittill( "player_spawned", player ); if(IsDefined(level.vista)) player PlayerSetGroundReferenceEnt(level.vista); } } anglesClamp180(angles) { return (AngleClamp180(angles[0]),AngleClamp180(angles[1]),AngleClamp180(angles[2])); } world_tilt() { level endon ( "game_ended" ); damage_triggers = GetEntArray("world_tilt_damage", "targetname"); array_thread(damage_triggers, ::world_tilt_damage_watch); vista_ents = GetEntArray("vista", "targetname"); if(!vista_ents.size) return; level.vista = vista_ents[0]; foreach(ent in vista_ents) { if(IsDefined(ent.script_noteworthy) && ent.script_noteworthy=="main") { level.vista = ent; break; } } //Init rotation Origins level.vista_rotation_origins = []; rotation_orgs = getstructarray("rotation_point", "targetname"); foreach(org in rotation_orgs) { if(!isDefined(org.script_noteworthy)) continue; org.angles = (0,0,0); level.vista_rotation_origins[ org.script_noteworthy ] = org; } foreach(ent in vista_ents) { if(ent!=level.vista) { if( IsDefined(ent.classname) && IsSubStr( ent.classname, "trigger" )) ent EnableLinkTo(); ent LinkTo(level.vista); } if(!IsDefined(ent.target)) continue; targets = GetEntArray(ent.target, "targetname"); foreach(target in targets) { if(!IsDefined(target.script_noteworthy)) target.script_noteworthy = "link"; switch(target.script_noteworthy) { case "link": target LinkTo(ent); break; default: break; } } } while(!IsDefined(level.players)) waitframe(); foreach(player in level.players) { player PlayerSetGroundReferenceEnt(level.vista); } level.max_world_pitch = 8; level.max_world_roll = 8; max_abs_pitch = level.max_world_pitch; max_abs_roll = level.max_world_roll; max_fall_dist = 6500; num_lerp_tilts = 2; //scale first num_lerp_tilts to be less severe tilt_chance = .5; //Chance of playspace being level after a fall. multi_fall_chance = [0,0,1,0]; //[ignored, ignored, double_fall_chance, triple_fall_chance] test_vista = SpawnStruct(); test_vista.origin = level.vista.origin; test_vista.angles = level.vista.angles; //Create random falls until the building is on (near) the ground vista_trans = []; while(1) { rotate_to = (0,0,0); if(tilt_chance0) { scale = (vista_trans.size+1)/num_lerp_tilts; scale = clamp(scale, 0, 1.0); } rotate_to *= scale; move_by *= scale; trans = test_vista world_tilt_get_trans(move_by, rotate_to); if(trans["origin"][2] > level.vista.origin[2] + max_fall_dist) { break; } trans["time"] = RandomFloatRange(1,2) * scale; test_vista.origin = trans["origin"]; test_vista.angles = trans["angles"]; vista_trans[vista_trans.size] = trans; } fall_count = vista_trans.size; //Distribute any extra fall distance across existing falls extra_z = level.vista.origin[2] + max_fall_dist - vista_trans[vista_trans.size-1]["origin"][2]; if(extra_z > 0) { add_z = array_zero_to_one(vista_trans.size, .5); for(i=0; i=2; j--) { if(multi_fall_chance[j]>chance) { multi_fall_end = multi_fall_start+(j-1); break; } } //Redistribute the wait times if there is a double/triple fall multi_fall_count = multi_fall_end - multi_fall_start; if(multi_fall_count>0) { delta = fall_wait_times[multi_fall_end] - fall_wait_times[multi_fall_start]; delta *= RandomFloatRange(.2, .8); new_time = fall_wait_times[multi_fall_start] + delta; for(j=multi_fall_start; j<=multi_fall_end; j++) { fall_wait_times[j] = new_time; } i+=multi_fall_count; } } /* Assign fall objects to a fall stage level.fall_object_stage = []; for(i=0; i highestScore ) { highestScore = game["teamScores"]["axis"]; } } } else { if(IsDefined(level.players)) { foreach( player in level.players ) { if( IsDefined( player.score ) && player.score > highestScore ) highestScore = player.score; } } } return highestScore; } world_tilt_get_trans(move_delta, rotate_to) { delta_angles = anglesClamp180(rotate_to - self.angles); rotation_point = level.vista_rotation_origins["south"]; if(delta_angles[2]<0) { rotation_point = level.vista_rotation_origins["north"]; } rotation_point.angles = self.angles; goal = SpawnStruct(); goal.origin = rotation_point.origin + move_delta; goal.angles = rotate_to; trans = TransformMove(goal.origin, goal.angles, rotation_point.origin, rotation_point.angles, self.origin, self.angles); return trans; } world_tilt_move(trans) { exploder(1); level thread tilt_sounds(); move_time = trans["time"]; if(trans["origin"] != self.origin ) self MoveTo(trans["origin"], move_time, move_time); if(anglesClamp180(trans["angles"]) != anglesClamp180(self.angles) ) self RotateTo(trans["angles"], move_time); Earthquake(RandomFloatRange(.3, .5), move_time, self.origin, 100000); wait move_time; } array_zero_to_one_rand(count, min_value, max_value, sum_to) { if(!IsDefined(min_value)) min_value = 0; if(!IsDefined(max_value)) max_value = 1; a = []; sum = 0; for(i=0; i0) { a[i] = a[i] + RandomFloatRange(-1*center_offset*rand, center_offset*rand); } sum += a[i]; } if(IsDefined(sum_to)) { for(i=0; i0.1) { if(set_pitch<0) { pos_a = self.positions["west"]; } else { pos_a = self.positions["east"]; } self.positions["current_we"] = pos_a; } pos_b = self.positions["current_ns"]; pos_b_lerp = 1; if(abs(set_roll)>0.1) { if(set_roll<0) { pos_b = self.positions["south"]; } else { pos_b = self.positions["north"]; } self.positions["current_ns"] = pos_b; } new_origin_offset = (0,0,0); new_origin_offset += pos_a.origin_offset * pos_a_lerp; new_origin_offset += pos_b.origin_offset * pos_b_lerp; self.positions["current"].origin_offset = new_origin_offset; new_angles_offset = (0,0,0); new_angles_offset += pos_a.angles_offset * pos_a_lerp; new_angles_offset += pos_b.angles_offset * pos_b_lerp; self.positions["current"].angles_offset = new_angles_offset; new_origin = self.start_origin + self.positions["current"].origin_offset; new_angles = self.start_angles + self.positions["current"].angles_offset; move_time = 3; if(new_origin!=self.move_ent.origin) self.move_ent MoveTo(new_origin, move_time, move_time*.3, move_time*.3); if(anglesClamp180(new_angles) != anglesClamp180(self.move_ent.angles)) self.move_ent rotateTo(new_angles, move_time); wait move_time; if(self is_dynamic_path()) self DisconnectPaths(); } is_dynamic_path() { return IsDefined(self.spawnflags) && self.spawnflags&1; } fall_objects() { level.fall_objects = []; objects = GetEntArray("fall_object", "targetname"); array_thread(objects, ::fall_object_init); } fall_object_init() { self.end = self; self.clip_move = []; self.connect_paths = []; self.disconnect_paths = []; things = []; if(IsDefined(self.target)) { structs = getstructarray(self.target, "targetname"); set_default_script_noteworthy(structs, "angle_ref"); things = array_combine(things, structs); ents = GetEntArray(self.target, "targetname"); things = array_combine(things, ents); } if(IsDefined(self.script_linkto)) { structs = self get_linked_structs(); set_default_script_noteworthy(structs, "start"); things = array_combine(things, structs); ents = self get_linked_ents(); things = array_combine(things, ents); } foreach(thing in things) { if(!IsDefined(thing.script_noteworthy)) continue; switch(thing.script_noteworthy) { case "angle_ref": self.end = thing; break; case "start": self.start = thing; break; case "link": thing linkto(self); break; case "connect_paths": self.connect_paths[self.connect_paths.size] = thing; thing DisconnectPaths(); break; case "disconnect_paths": self.disconnect_paths[self.disconnect_paths.size] = thing; thing ConnectPaths(); break; case "clip_move": thing.start_origin = thing.origin; self.clip_move[self.clip_move.size] = thing; break; default: break; } } set_default_angles(things); if(IsDefined(self.start) && IsDefined(self.end)) { level.fall_objects[level.fall_objects.size] = self; self fall_object_set_start_pos(); } } set_default_script_noteworthy(things, noteworthy) { if(!IsDefined(things)) return; if(!IsDefined(noteworthy)) noteworthy = ""; if(!isArray(things)) things = [things]; foreach(thing in things) { if(!IsDefined(thing.script_noteworthy)) thing.script_noteworthy = noteworthy; } } set_default_angles(things, angles) { if(!IsDefined(things)) return; if(!IsDefined(angles)) angles = (0,0,0); if(!isArray(things)) things = [things]; foreach(thing in things) { if(!IsDefined(thing.angles)) thing.angles = angles; } } fall_object_set_start_pos() { self.fall_to_origin = self.origin; self.fall_to_angles = self.angles; trans = TransformMove(self.start.origin, self.start.angles, self.end.origin, self.end.angles, self.origin, self.angles); self.origin = trans["origin"]; self.angles = trans["angles"]; } fall_object_run(delayTime) { //Hack revisit this if(IsDefined(self.fall_object_done) && self.fall_object_done) return; self.fall_object_done = true; if(isDefined(delayTime) && delayTime>0) wait delayTime; //Move clip into place first if(self.clip_move.size) { clip_move_time = .5; foreach(clip in self.clip_move) { clip MoveTo(self.fall_to_origin, clip_move_time ); } wait clip_move_time; foreach(clip in self.clip_move) { if(clip fall_object_is_dynamic_path()) clip DisconnectPaths(); } } fall_speed = RandomFloatRange(300,320); dist = Distance(self.fall_to_origin, self.origin); time = dist/fall_speed; self moveTo(self.fall_to_origin, time, time, 0); if(self.fall_to_angles != self.angles) self RotateTo(self.fall_to_angles, time, 0, 0); wait time; foreach(ent in self.disconnect_paths) { ent DisconnectPaths(); } foreach(ent in self.connect_paths) { ent ConnectPaths(); ent Delete(); } foreach(clip in self.clip_move) { clip.origin = clip.start_origin; } } fall_object_is_dynamic_path() { return self.spawnflags&1; } //Move to common script get_linked_structs() { array = []; if ( IsDefined( self.script_linkTo ) ) { linknames = get_links(); for ( i = 0; i < linknames.size; i++ ) { ent = getstruct( linknames[ i ], "script_linkname" ); if ( IsDefined( ent ) ) { array[ array.size ] = ent; } } } return array; } */ watersheet_trig_setup() { level endon( "game_ended" ); self endon( "death" ); self endon( "using_remote" ); self endon( "stopped_using_remote" ); self endon( "disconnect" ); self endon( "above_water" ); trig = getent("watersheet", "targetname" ); //level.player ent_flag_init("water_sheet_sound"); //level thread watersheet_sound( trig ); while(1) { trig waittill("trigger", player ); if ( !isDefined(player.isTouchingWaterSheetTrigger) || player.isTouchingWaterSheetTrigger == false) { thread watersheet_PlayFX( player ); } } } watersheet_PlayFX( player ) { player.isTouchingWaterSheetTrigger = true; player SetWaterSheeting( 1, 2 ); wait( randomfloatrange( .15, .75) ); player SetWaterSheeting( 0 ); player.isTouchingWaterSheetTrigger = false; } watersheet_sound( trig ) { trig endon("death"); thread watersheet_sound_play(trig); while( 1 ) { trig waittill( "trigger", player ); trig.sound_end_time = GetTime() + 100; trig notify("start_sound"); } } watersheet_sound_play(trig) { trig endon("death"); while(1) { trig waittill("start_sound"); trig PlayLoopSound("scn_jungle_under_falls_plr"); while(trig.sound_end_time>GetTime()) wait (trig.sound_end_time-GetTime())/1000; trig StopLoopSound(); } } animateScriptableProps( targetName, fallAnimLength ) { ents = GetScriptableArray( targetName, "targetname" ); foreach ( ent in ents ) { ent thread animateOneScriptableProp( fallAnimLength ); } } animateOneScriptableProp( fallAnimLength ) { while ( true ) { // random delay to offset the idle anims // frameDelay = RandomIntRange(0, 11) * 0.05; // wait( frameDelay ); self SetScriptablePartState( 0, "idle" ); level waittill( "shake_props" ); frameDelay = RandomIntRange(0, 7) * 0.05; wait( frameDelay ); self SetScriptablePartState( 0, "fall" ); wait ( fallAnimLength ); } } CONST_DOUBLE_DOOR_MIN_DAMAGE = 50; CONST_DOUBLE_DOOR_VFX = "equipment_explode_big"; CONST_DOUBLE_DOOR_ANGLE_MAX = 85; CONST_DOUBLE_DOOR_EXPLODE_OPEN_TIME = 0.125; doubleDoorCreate( doorName ) { door = GetEnt( doorName, "targetname" ); if ( IsDefined( door ) ) { door.collision = GetEnt( doorName + "_clip", "targetname" ); door.ruins = []; door.ruins[0] = getRuin( doorName + "_upper" ); door.ruins[1] = getRuin( doorName + "_lower" ); door.destroyFxPoint = getstruct( door.ruins[0].target, "targetname" ); Assert( IsDefined( door.destroyFxPoint ) ); waitframe(); door blockPath(); door thread doubleDoorWaitForDamage(); } } doubleDoorWaitForDamage() { self.health = 9999; self SetCanDamage( true ); while( true ) { self waittill( "damage", damage, attacker, direction_vec, impact_loc, damage_type ); if ( IsExplosiveDamageMOD( damage_type ) && damage > CONST_DOUBLE_DOOR_MIN_DAMAGE ) { self thread doubleDoorDestroy( attacker, direction_vec, impact_loc ); } } } doubleDoorDestroy( attacker, direction_vec, impact_loc ) { self.collision clearPath(); self clearPath(); facingDir = AnglesToForward( self.destroyFxPoint.angles ); dotProd = VectorDot( facingDir, direction_vec ); isFront = dotProd > 0; angleLimit = CONST_DOUBLE_DOOR_ANGLE_MAX; thread drawLine( self.destroyFxPoint.origin, self.destroyFxPoint.origin + 20 * facingDir, 50, (1, 0, 0) ); if ( !isFront ) { facingDir *= -1; } else { angleLimit *= -1; } PlayFX( getfx( CONST_DOUBLE_DOOR_VFX ), self.destroyFxPoint.origin, facingDir ); foreach( doorRuin in self.ruins ) { doorRuin Show(); doorRuin thread doorApplyImpulse( angleLimit ); angleLimit *= -1; } } getRuin( ruinName ) { ruin = GetEnt( ruinName, "targetname" ); Assert( IsDefined( ruin ) ); ruin Hide(); return ruin; } trapDoorCreate( doorName ) { door = GetEnt( doorName, "targetname" ); if ( IsDefined( door ) ) { self.pathBlocker = GetEnt( door.target, "targetname" ); waitframe(); self.pathBlocker blockPath(); door thread trapDoorWaitForDamage(); } } trapDoorWaitForDamage() { self.health = 9999; self SetCanDamage( true ); while( true ) { self waittill( "damage", damage, attacker, direction_vec, impact_loc, damage_type ); self thread trapDoorDestroy( attacker, direction_vec, impact_loc ); } } trapDoorDestroy( attacker, direction_vec, impact_loc ) { self.pathBlocker NotSolid(); self.pathBlocker clearPath(); /* facingDir = AnglesToForward( self.destroyFxPoint.angles ); dotProd = VectorDot( facingDir, direction_vec ); isFront = dotProd > 0; angleLimit = CONST_DOUBLE_DOOR_ANGLE_MAX; thread drawLine( self.destroyFxPoint.origin, self.destroyFxPoint.origin + 20 * facingDir, 50, (1, 0, 0) ); if ( !isFront ) { facingDir *= -1; angleLimit *= -1; } PlayFX( getfx( CONST_DOUBLE_DOOR_VFX ), self.destroyFxPoint.origin, facingDir ); */ anglelimit = 90.0; self thread doorApplyImpulse( angleLimit ); } doorApplyImpulse( angleLimit ) { self RotateBy( (angleLimit, 0, 0), CONST_DOUBLE_DOOR_EXPLODE_OPEN_TIME, CONST_DOUBLE_DOOR_EXPLODE_OPEN_TIME, 0 ); } clearPath() // self == blockingEnt { self ConnectPaths(); self Hide(); self NotSolid(); } blockPath() { self Solid(); self Show(); self DisconnectPaths(); } GRAVITY_DVAR = "phys_gravity"; levitateProps( minTime, maxTime ) { baseGravity = GetDvarInt( GRAVITY_DVAR, -800 ); SetDvar( GRAVITY_DVAR, 0 ); PhysicsJitter( level.mapCenter, 2500, 0, 5.0, 5.0 ); gravityTime = RandomFloatRange( minTime, maxTime ); wait( gravityTime ); SetDvar( GRAVITY_DVAR, baseGravity ); } // ----------------------------------- // Movers - big things that fall on a keyframed path // Requires entity with a known // Collision named "_collision" // entity targets a series of script structs with position and orientation that represent the flight path of ent // Each entity should have a script_duration, which specifies the MoveTo time // optionally have script_accel and script_decel to control acceleration. First node should have script_accel = script_duration // Somehow, we'll use script_noteworthy to control vfx and sounds? // triggerFlag - the name of the notify that it will respond to. // Also responds to explosions moverCreate( moverName, triggerFlag ) { mover = GetEnt( moverName, "targetname" ); mover.collision = GetEnt( moverName + "_collision", "targetname" ); if ( IsDefined( mover.collision ) ) { mover.collision LinkTo( mover ); mover.collision thread moverExplosiveTrigger( triggerFlag ); } mover.unresolved_collision_func = maps\mp\_movers::unresolved_collision_void; // get keyframes ent = mover; ent.keyframes = []; nextKeyFrameName = ent.target; i = 0; while ( IsDefined( nextKeyFrameName ) ) { struct = getstruct( nextKeyFrameName, "targetname" ); if ( IsDefined( struct ) ) { ent.keyframes[i] = struct; // set default values if ( !IsDefined( struct.script_duration ) ) struct.script_duration = 1.0; if ( !IsDefined( struct.script_accel ) ) struct.script_accel = 0.0; if ( !IsDefined( struct.script_decel ) ) struct.script_decel = 0.0; i++; nextKeyFrameName = struct.target; } else { break; } } // bus.pathBlocker = GetEnt( "pathBlocker", "targetname" ); // wait(0.05); // bus.pathBlocker elevatorClearPath(); mover thread moverDoMove( triggerFlag ); } moverExplosiveTrigger( note ) { level endon( "game_ended" ); if(!IsDefined(note)) note = "explosive_damage"; self SetCanDamage( true ); while ( true ) { self.health = 1000000; self waittill("damage", amount, attacker, direction_vec, point, type); if ( IsExplosiveDamageMOD( type ) ) { level notify( note, self ); } } } moverDoMove( waitString ) // self == mover entity { level endon ( "game_ended" ); while ( true ) { level waittill( waitString, mover ); if ( !IsDefined( mover ) || mover == self ) { break; } } // play start sfx, vfx // self.pathBlocker elevatorBlockPath(); // self.collision killLinkedEntities( attacker ); for ( i = 1 ; i < self.keyframes.size; i++ ) { kf = self.keyframes[i]; self MoveTo( kf.origin, kf.script_duration, kf.script_accel, kf.script_decel ); self RotateTo( kf.angles, kf.script_duration, kf.script_accel, kf.script_decel ); // self PlaySound( "scn_bus_groan" ); // self busSlidingEffect(); if ( IsDefined( kf.shakeMag ) ) { Earthquake( kf.shakeMag, kf.shakeDuration, self.origin, kf.shakeDistance ); } self waittill( "movedone" ); } // play another sound? // shake? fakeImpactPoint = self.origin + (0, 0, 2000); Earthquake( 0.25, .5, fakeImpactPoint, 3000 ); // play stop vfx, sfx // PlaySoundAtPos(fakeBusPos, "scn_bus_crash"); } //entName //animName //Sounds - start, stop //VFX - start, stop // animation length? animatedMoverCreate( entName, animName, fallSound, impactSound, impactDelay, impactOffset ) { mover = GetEnt( entName, "targetname" ); if ( IsDefined( mover ) ) { level waittill( "trigger_movers" ); if ( IsDefined( fallSound ) ) { mover PlaySound( fallSound ); } mover ScriptModelPlayAnim( animName ); if ( IsDefined( impactSound ) ) { wait ( impactDelay ); PlaySoundAtPos( impactOffset, impactSound ); } } } setupCollapsingColumn( entName ) { mover = GetEnt( entName, "targetname" ); if ( IsDefined( mover ) ) { rubble = GetEnt( entName + "_debris_clip", "targetname" ); // rubble Hide(); rubble NotSolid(); // rubble.clip clearPath(); // must make sure that there are enough drop nodes for this to work // we also don't want to trigger the column on the first drop fallNum = RandomIntRange( 1, level.dropNodes.size ); fallString = "buildingCollapseEnd_" + fallNum; level waittill_any( fallString, "trigger_movers" ); // turn off collision on pristine model mover PlaySound( CONST_COLUMN_FALL_SFX ); mover ScriptModelPlayAnim( CONST_COLUMN_FALL_ANIM ); // This should use the position of rubble! impactPos = rubble.origin + (0, 0, 10); // offset slightly for radius damage wait ( CONST_COLUMN_IMPACT_DELAY ); PlaySoundAtPos( impactPos, CONST_COLUMN_IMPACT_SFX ); // play effect exploder( CONST_COLUMN_IMPACT_VFX_EXPLODER_ID ); Earthquake( 0.3, 0.25, impactPos, 500 ); RadiusDamage( impactPos, CONST_COLUMN_DAMAGE_RADIUS, CONST_COLUMN_DAMAGE, CONST_COLUMN_DAMAGE, undefined, "MOD_CRUSH" ); // swap in fallen model, connect paths, etc. // mover Hide(); // rubble Show(); rubble Solid(); // rubble.clip blockPath(); } } setupSniperDuct( entName ) { duct = GetEnt( entName, "targetname" ); if ( IsDefined( duct ) ) { // have to create an ent to rotate around, because the origin of the duct isn't the right point rootStruct = getstruct( entName + "_origin", "targetname" ); root = Spawn( "script_model", rootStruct.origin ); duct LinkTo( root ); duct2 = GetEnt( duct.target, "targetname" ); duct2 LinkTo( root ); // must make sure that there are enough drop nodes for this to work // we also don't want to trigger the column on the first drop fallNum = RandomIntRange( 1, level.dropNodes.size ); fallString = "buildingCollapseEnd_" + fallNum; // dbg // while ( true ) { level waittill_any( fallString, "trigger_movers" ); // duct PlaySound( CONST_COLUMN_FALL_SFX ); // play fx // rotate some swinging root RotatePitch( 1.1 * CONST_SNIPER_DUCT_ANGLE, CONST_SNIPER_DUCT_TIME, 0.85 * CONST_SNIPER_DUCT_TIME, 0.15 * CONST_SNIPER_DUCT_TIME ); wait ( CONST_SNIPER_DUCT_TIME ); CONST_SWING_PERCENTAGE = 0.5; recoveryTime = CONST_SWING_PERCENTAGE * CONST_SNIPER_DUCT_TIME; root RotatePitch( -0.15 * CONST_SNIPER_DUCT_ANGLE, recoveryTime, 0.5 * recoveryTime, 0.5 * recoveryTime ); wait ( recoveryTime ); // root RotatePitch( 0.3 * CONST_SNIPER_DUCT_ANGLE, recoveryTime, 0.5 * recoveryTime, 0.5 * recoveryTime ); // wait ( recoveryTime ); root RotatePitch( 0.05 * CONST_SNIPER_DUCT_ANGLE, recoveryTime, 0.5 * recoveryTime, 0.5 * recoveryTime ); wait ( recoveryTime ); // dbg // wait ( 3 ); // // root RotatePitch( -1 * CONST_SNIPER_DUCT_ANGLE, recoveryTime, 0.5 * recoveryTime, 0.5 * recoveryTime ); // // wait ( recoveryTime ); // } } } // these are the structs that control the position/orientation of each stage of the building collapsing setupBuildingCollapse() { // find all the sound sources for the falling builindgs level.soundSources = []; soundStructs = getstructarray( "tilt_sound", "targetname" ); if ( IsDefined( soundStructs ) ) { foreach( struct in soundStructs ) { level.soundSources[ struct.script_label ] = struct; } } // configuration data for each stage of collapse level.collapseSettings = []; curSettings = []; curSettings[ "prefalltime" ] = CONST_DEFAULT_PREFALL_TIME; // amount of time to play sfx/vfx before actually moving curSettings[ "falltime" ] = CONST_DEFAULT_FALL_TIME; curSettings[ "sfx" ] = []; curSettings[ "sfx" ][ "rubble_left" ] = "scn_bldg_fall1_rubble_left"; curSettings[ "sfx" ][ "rubble_right" ] = "scn_bldg_fall1_rubble_right"; curSettings[ "sfx" ][ "glass_left" ] = "scn_bldg_fall1_glass_left"; curSettings[ "sfx" ][ "glass_right" ] = "scn_bldg_fall1_glass_right"; level.collapseSettings[ level.collapseSettings.size ] = curSettings; curSettings = []; curSettings[ "prefalltime" ] = CONST_DEFAULT_PREFALL_TIME; // amount of time to play sfx/vfx before actually moving curSettings[ "falltime" ] = CONST_DEFAULT_FALL_TIME; curSettings[ "sfx" ] = []; curSettings[ "sfx" ][ "rubble_left" ] = "scn_bldg_fall2_rubble_left"; curSettings[ "sfx" ][ "rubble_right" ] = "scn_bldg_fall2_rubble_right"; curSettings[ "sfx" ][ "glass_left" ] = "scn_bldg_fall2_glass_left"; curSettings[ "sfx" ][ "glass_right" ] = "scn_bldg_fall2_glass_right"; level.collapseSettings[ level.collapseSettings.size ] = curSettings; curSettings = []; curSettings[ "prefalltime" ] = CONST_DEFAULT_PREFALL_TIME; // amount of time to play sfx/vfx before actually moving curSettings[ "falltime" ] = CONST_DEFAULT_FALL_TIME; curSettings[ "sfx" ] = []; curSettings[ "sfx" ][ "rubble_left" ] = "scn_bldg_fall3_rubble_left"; curSettings[ "sfx" ][ "rubble_right" ] = "scn_bldg_fall3_rubble_right"; curSettings[ "sfx" ][ "glass_left" ] = "scn_bldg_fall3_glass_left"; curSettings[ "sfx" ][ "glass_right" ] = "scn_bldg_fall3_glass_right"; level.collapseSettings[ level.collapseSettings.size ] = curSettings; curSettings = []; curSettings[ "prefalltime" ] = CONST_DEFAULT_PREFALL_TIME; // amount of time to play sfx/vfx before actually moving curSettings[ "falltime" ] = CONST_DEFAULT_FALL_TIME; curSettings[ "sfx" ] = []; curSettings[ "sfx" ][ "rubble_left" ] = "scn_bldg_fall4_rubble_left"; curSettings[ "sfx" ][ "rubble_right" ] = "scn_bldg_fall4_rubble_right"; curSettings[ "sfx" ][ "glass_left" ] = "scn_bldg_fall4_glass_left"; curSettings[ "sfx" ][ "glass_right" ] = "scn_bldg_fall4_glass_right"; level.collapseSettings[ level.collapseSettings.size ] = curSettings; curSettings = []; curSettings[ "prefalltime" ] = 0.5; // amount of time to play sfx/vfx before actually moving curSettings[ "falltime" ] = CONST_DEFAULT_FALL_TIME; curSettings[ "sfx" ] = []; curSettings[ "sfx" ][ "rubble_left" ] = "scn_bldg_fall5_rubble_left"; curSettings[ "sfx" ][ "rubble_right" ] = "scn_bldg_fall5_rubble_right"; curSettings[ "sfx" ][ "glass_left" ] = "scn_bldg_fall5_glass_left"; curSettings[ "sfx" ][ "glass_right" ] = "scn_bldg_fall5_glass_right"; level.collapseSettings[ level.collapseSettings.size ] = curSettings; level.vista = GetEnt( "vista_test", "targetname" ); dropNodeOffsets = []; dropNodeOffsets[0] = 550; dropNodeOffsets[1] = 400; dropNodeOffsets[2] = 350; level.dropNodes = []; curNode = getstruct( "drop_node2", "targetname" ); // maybe should be per-fall maxPitch = CONST_DEFAULT_MAX_PITCH; maxRoll = CONST_DEFAULT_MAX_ROLL; curHeight = level.vista.origin[2]; i = 0; while ( IsDefined( curNode ) ) { curNode.angles = (RandomFloatRange(-1*maxPitch, maxPitch), 0, RandomFloatRange(-1*maxRoll, maxRoll)); curNode.origin -= (0, 0, dropNodeOffsets[i]); i++; // add a secondary angle? level.dropNodes[ level.dropNodes.size ] = curNode; if ( IsDefined( curNode.target ) ) curNode = getstruct( curNode.target, "targetname" ); else break; } level thread dropNodeWait(); level thread setupDropEventTrigger(); // setup concrete building facades level.facadeConcrete = []; for ( i = 0; i < level.dropNodes.size; i++ ) { entName = CONST_CONCRETE_FACADE_NAME + i; facade = GetEnt( entName, "targetname" ); facade LinkTo( level.vista ); level.facadeConcrete[ i ] = facade; } // setup glass building facades level.facadeGlass = []; level.ruinGlass = []; for ( i = 0; i < level.dropNodes.size; i++ ) { entName = CONST_GLASS_FACADE_NAME + i; facades = GetEntArray( entName, "targetname" ); facades = array_sort_with_func( facades, ::compareHeight ); level.facadeGlass[ i ] = facades; foreach ( item in facades ) { item LinkTo( level.vista ); } ruinName = CONST_GLASS_FACADE_NAME + "ruin_" + i; ruin = GetEnt( ruinName, "targetname" ); if ( IsDefined( ruin ) ) { ruin LinkTo ( level.vista ); ruin Hide(); level.ruinGlass[ i ] = ruin; } } } compareHeight( a, b ) { return (a.origin[2] > b.origin[2]); } dropNodeWait() { level.dropStage = 0; while ( level.dropStage < level.dropNodes.size ) { level waittill( "buildingCollapse" ); doBuildingFall( level.dropStage ); level.dropStage++; } } doBuildingFall( nodeIndex ) { level.buildingIsFalling = true; targetPos = level.dropNodes[ nodeIndex ]; settings = level.collapseSettings[ nodeIndex ]; startShockTime = settings[ "prefalltime" ]; moveTime = settings[ "falltime" ]; // ---------------------------------- // start sequence // trigger sounds foreach ( structName, struct in level.soundSources ) { PlaySoundAtPos( struct.origin, settings[ "sfx" ][ structName ] ); } Earthquake( RandomFloatRange( 0.1, 0.2), startShockTime, level.mapCenter, CONST_EARTHQUAKE_RANGE ); targetRoll = RandomFloatRange( CONST_DEFAULT_MIN_ROLL, CONST_DEFAULT_MAX_ROLL ); if ( RandomFloat( 1 ) < 0.5 ) targetRoll *= -1; targetPitch = RandomFloatRange( CONST_DEFAULT_MIN_PITCH, CONST_DEFAULT_MAX_PITCH ); if ( RandomFloat( 1 ) < 0.5 ) targetPitch *= -1; targetAngle = (targetPitch, 0, targetRoll ); level.vista RotateTo( 0.75 * targetAngle, startShockTime, 1.0 * startShockTime, 0.0 ); level thread destroyAirKillstreaks(); wait( startShockTime ); level.disableVanguardsInAir = true; exploder( CONST_GLASS_BUILDING_EXPLODER_ID ); playRumble( "damage_light" ); // ---------------------------------- // Main fall // shake for the duration of the fall Earthquake( RandomFloatRange( 0.3, 0.45), moveTime, level.mapCenter, CONST_EARTHQUAKE_RANGE ); // play some effects, hide the old facade level thread animateConcreteBuildingFacade( nodeIndex, moveTime ); level thread animateGlassBuildingFacade( nodeIndex, moveTime ); level.vista MoveTo( targetPos.origin, moveTime, 0.25 * moveTime, 0.0 ); level.vista RotateTo( -1 * targetAngle, moveTime, 0.25 * moveTime, 0.0 ); // trigger other effects level notify( "shake_props" ); level notify( "buildingCollapseStart_" + nodeIndex ); wait( moveTime ); // ----------------------------------- // Impact // should get these earthquake values from elsewhere? Earthquake( RandomFloatRange(0.8, 1.0), 1.5, level.mapCenter, CONST_EARTHQUAKE_RANGE ); exploder( CONST_FALL_EXPLODER_ID ); exploder( CONST_GLASS_BUILDING_EXPLODER_ID ); level notify( "buildingCollapseEnd_" + nodeIndex ); level.disableVanguardsInAir = undefined; playRumble( "artillery_rumble" ); wait( 0.5 ); // -------------------- // Settle into position settleTime = CONST_DEFAULT_SETTLE_TIME; finalAngles = ( -0.25 * targetPitch, 0, 0.25 * targetRoll); level.vista RotateTo( finalAngles, settleTime, 0.8 * settleTime, 0.2 * settleTime ); wait( 0.75 * settleTime ); Earthquake( RandomFloatRange(0.3, 0.5), 0.25 * settleTime + 1.0, level.mapCenter, CONST_EARTHQUAKE_RANGE ); playRumble( "damage_light" ); // play fall impact sound level.buildingIsFalling = undefined; } animateConcreteBuildingFacade( nodeIndex, moveTime ) { // only show the first two facades // if ( nodeIndex >= 2 ) return; facade = level.facadeConcrete[ nodeIndex ]; if ( IsDefined( facade ) ) { // trigger particle effects oldModel = facade.model; facade SetModel( CONST_CONCRETE_FACADE_RUIN_MODEL ); // create a new model of the pristine building to slide away oldFace = Spawn( "script_model", facade.origin ); oldFace.angles = facade.angles; oldFace SetModel( oldModel ); upVec = AnglesToUp( oldFace.angles ); endPos = oldFace.origin - 150 * upVec; oldFace MoveTo( endPos, moveTime, moveTime, 0 ); oldFace RotateRoll( 20, moveTime, moveTime, 0 ); wait ( movetime ); oldFace Delete(); } } animateGlassBuildingFacade( nodeIndex, moveTime ) { if ( IsDefined( level.facadeGlass[ nodeIndex ] ) ) { timeStep = moveTime / level.facadeGlass[ nodeIndex ].size; level.ruinGlass[ nodeIndex ] Show(); foreach ( item in level.facadeGlass[ nodeIndex ] ) { item thread glassFacadeFall( moveTime ); wait( timeStep ); } } } glassFacadeFall( moveTime ) { self Unlink(); upVec = AnglesToUp( self.angles ); endPos = self.origin - 400 * upVec; self MoveTo( endPos, moveTime, moveTime, 0 ); self RotateRoll( -60, moveTime, moveTime, 0 ); wait( moveTime ); self Delete(); } setupDropEventTrigger() { numIntervals = level.dropNodes.size + 1; // +1 so we can to play in the level after the last drop // this should be set up per game mode if ( level.gameType == "sr" || level.gameType == "sd" ) { level thread searchAndRescueEventTriggerDrop(); } else { // setup score-based events scoreLimit = getWatchedDvar( "scorelimit" ); if ( level.teamBased && scoreLimit >= level.dropNodes.size * 20 ) // do a drop at most every X kills { level thread scoreLimitTriggerDrop( scoreLimit, level.dropNodes.size ); } else { timeLimit = getWatchedDvar( "timelimit" ); if ( timeLimit <= 0 ) { timeLimit = 10; // in minutes } timeLimit *= 60; // convert to seconds level thread timeLimitTriggerDrop( timeLimit, numIntervals ); } } } searchAndRescueEventTriggerDrop() { level endon( "game_ended" ); while ( true ) { result = level waittill_any_return( "last_alive", "bomb_exploded", "bomb_dropped" ); if ( result == "last_alive" || !level.bombPlanted ) { // unfortunately, I don't know how to get the game end time, so always fire off the event // luckily, we can only trigger one fall at a time interval = RandomFloatRange( CONST_EVENT_TRIGGER_FALL_DELAY_MIN, CONST_EVENT_TRIGGER_FALL_DELAY_MAX ); wait( interval ); } else if ( result != "bomb_exploded" ) { continue; } level notify( "buildingCollapse" ); } } scoreLimitTriggerDrop( scoreLimit, numDrops ) { level endon( "game_ended" ); level thread periodicTremor( 90, 120 ); // !!! hack. We assume numIntervals == 3, because we're going to go 30%, 60% 90% scoreInterval = Int( 0.3 * scoreLimit ); /# SetDvarIfUninitialized( "scr_desc_scoreInterval", scoreInterval ); #/ curDrop = 1; while ( true ) { level waittill ( "update_team_score", team, newScore ); /# scoreInterval = GetDvarInt( "scr_desc_scoreInterval" ); #/ if ( newScore >= curDrop * scoreInterval ) { interval = RandomFloatRange( CONST_EVENT_TRIGGER_FALL_DELAY_MIN, CONST_EVENT_TRIGGER_FALL_DELAY_MAX ); wait( interval ); level notify( "buildingCollapse" ); curDrop++; } } } timeLimitTriggerDrop( timeLimit, numIntervals ) { level endon( "game_ended" ); timeInterval = timeLimit / numIntervals; level thread periodicTremor( 0.8 * timeInterval, 1.2 * timeInterval ); while ( level.dropStage < level.dropNodes.size ) { wait( timeInterval ); level notify( "buildingCollapse" ); } } periodicTremor( minTime, maxTime ) { level endon( "game_ended" ); // wait an initial delay interval = 0.5 * RandomFloatRange( minTime, maxTime ); /# debugInterval = GetDvarInt( "scr_dbg_tremor_interval" ); if ( debugInterval > 0 ) interval = debugInterval; #/ wait( interval ); while ( true ) { if ( !IsDefined( level.buildingIsFalling ) ) { // play sound PlaySoundAtPos( level.mapCenter, "scn_bldg_tremor_lr" ); magnitude = RandomFloatRange( 0.2, 0.3 ); duration = RandomFloatRange( 1.0, 1.5 ); Earthquake( magnitude, duration, level.mapCenter, CONST_EARTHQUAKE_RANGE ); } interval = RandomFloatRange( minTime, maxTime ); /# debugInterval = GetDvarInt( "scr_dbg_tremor_interval" ); if ( debugInterval > 0 ) interval = debugInterval; #/ wait( interval ); } } /* shakePlayerControlledHelis( duration ) { if ( IsDefined( level.littleBirds ) ) { foreach ( heli in level.littleBirds ) { Earthquake( 0.5, duration, heli.origin, 512 ); } } } */ setupBuildingFx() { fxRig = GetEnt( "column_chunk", "targetname" ); PlayFXOnTag( getfx( "vfx_building_debris_runner" ), fxRig, "tag_03_vfx_building_debris_runner" ); PlayFXOnTag( getfx( "vfx_spark_drip_dec_runner" ), fxRig, "tag_02_vfx_spark_drip_dec_runner" ); PlayFXOnTag( getfx( "vfx_building_hole_elec_short_runner" ), fxRig, "tag_01_vfx_building_hole_elec_short_runner" ); } // ugh, this is very heavy handed // Becuase the fall sequence looks bad from the air, we will kill all air vehicles. destroyAirKillstreaks() { // play an effect? destroyAirKillstreaksForTeam( undefined, "allies" ); destroyAirKillstreaksForTeam( undefined, "axis" ); } destroyAirKillstreaksForTeam( attacker, victimTeam ) { maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", level.heli_pilot ); if ( IsDefined( level.lbSniper ) ) { // kind of hack, but destroyTargets does a lot of needed setup tempArray = []; tempArray[0] = level.lbSniper; maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", tempArray ); } } playRumble( rumbleType ) { foreach ( player in level.players ) { player PlayRumbleOnEntity( rumbleType ); } } /# debugDescent() { SetDvarIfUninitialized( "scr_dbg_shake_props", 0 ); SetDvarIfUninitialized( "scr_dbg_movers", 0 ); SetDvarIfUninitialized( "scr_dbg_building_collapse", 0 ); SetDvarIfUninitialized( "scr_dbg_tremor_interval", 0 ); while ( true ) { checkDbgDvar( "scr_dbg_shake_props", undefined, "shake_props" ); checkDbgDvar( "scr_dbg_movers", undefined, "trigger_movers" ); checkDbgDvar( "scr_dbg_building_collapse", undefined, "buildingCollapse" ); checkDbgDvar( "scr_desc_fx", ::dbgFireFx, undefined ); wait ( 0.1 ); } } checkDbgDvar( dvarName, callback, notifyStr ) { if ( GetDvarInt( dvarName ) > 0 ) { if ( IsDefined( callback ) ) [[ callback ]]( GetDvarInt( dvarName ) ); if ( IsDefined( notifyStr ) ) level notify( notifyStr ); SetDvar( dvarName, 0 ); } } dbgFireFx( fxId ) { exploder( fxId ); } #/